/
storage.ts
127 lines (105 loc) · 3.47 KB
/
storage.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
export type SettingsValue = string | boolean;
export enum StorageKeys {
'hamsterbaseURL' = 'hamsterbaseURL',
'hamsterUsername' = 'hamsterUsername',
'hamsterPassword' = 'hamsterPassword',
}
export interface ISettingService {
get<V extends SettingsValue>(key: string, defaultValue: V): Promise<V>;
set(key: string, value: SettingsValue): Promise<void>;
readConfig<T extends Record<string, SettingsValue>>(
defaultValue: T
): Promise<T>;
clearAll(): Promise<void>;
}
export interface ConfigDatabase {
settings: Record<string, SettingsValue>;
}
export class IndexedDBSettingService implements ISettingService {
private readonly dbName: string = 'my-settings-db';
private readonly dbVersion: number = 1;
private readonly storeName: string = 'settings';
private async openDB(): Promise<IDBDatabase> {
return new Promise<IDBDatabase>((resolve, reject) => {
const request = this.getIndexedDB().open(this.dbName, this.dbVersion);
request.onerror = (event) => {
console.error('Error opening database', event);
reject(event);
};
request.onsuccess = () => {
resolve(request.result);
};
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName);
}
};
});
}
public async get<V extends SettingsValue>(
key: string,
defaultValue: V
): Promise<V> {
const db = await this.openDB();
return new Promise<V>((resolve, reject) => {
const transaction = db.transaction(this.storeName, 'readonly');
const objectStore = transaction.objectStore(this.storeName);
const request = objectStore.get(key);
request.onerror = (event) => {
console.error(`Error getting value for key "${key}"`, event);
reject(event);
};
request.onsuccess = () => {
const value =
request.result !== undefined ? request.result : defaultValue;
resolve(value);
};
});
}
public async set(key: string, value: SettingsValue): Promise<void> {
const db = await this.openDB();
return new Promise<void>((resolve, reject) => {
const transaction = db.transaction(this.storeName, 'readwrite');
const objectStore = transaction.objectStore(this.storeName);
const request = objectStore.put(value, key);
request.onerror = (event) => {
console.error(`Error setting value for key "${key}"`, event);
reject(event);
};
request.onsuccess = () => {
resolve();
};
});
}
public async readConfig<T extends Record<string, SettingsValue>>(
defaultValue: T
): Promise<T> {
const result = await Promise.all(
Object.keys(defaultValue).map(async (key: string) => {
return {
[key]: await this.get(key, defaultValue[key]),
};
})
);
return Object.assign({}, ...result);
}
public async clearAll(): Promise<void> {
const db = await this.openDB();
return new Promise<void>((resolve, reject) => {
const transaction = db.transaction(this.storeName, 'readwrite');
const objectStore = transaction.objectStore(this.storeName);
const request = objectStore.clear();
request.onerror = () => {
console.error('Error clearing settings', event);
reject(event);
};
request.onsuccess = () => {
resolve();
};
});
}
protected getIndexedDB() {
return indexedDB;
}
}