diff --git a/lib/storage/platforms/index.ts b/lib/storage/platforms/index.ts index 0b95dc97d..77112b012 100644 --- a/lib/storage/platforms/index.ts +++ b/lib/storage/platforms/index.ts @@ -1,3 +1,3 @@ -import WebStorage from '../providers/IDBKeyValProvider'; +import WebStorage from '../providers/DexieProvider'; export default WebStorage; diff --git a/lib/storage/providers/DexieProvider.ts b/lib/storage/providers/DexieProvider.ts new file mode 100644 index 000000000..f5bb54e70 --- /dev/null +++ b/lib/storage/providers/DexieProvider.ts @@ -0,0 +1,108 @@ +// TODO: Fix types +import Dexie from 'dexie'; +import utils from '../../utils'; +import type StorageProvider from './types'; +import type {OnyxKey, OnyxValue} from '../../types'; + +class OnyxDatabase extends Dexie { + keyvaluepairs!: Dexie.Table, OnyxKey>; + + constructor() { + super('OnyxDB'); + this.version(0.1).stores({ + keyvaluepairs: '', + }); + } +} + +let db: OnyxDatabase; + +const initDB = () => { + if (!db) { + db = new OnyxDatabase(); + return db.open(); + } + return Promise.resolve(); +}; + +const provider: StorageProvider = { + /** + * The name of the provider that can be printed to the logs + */ + name: 'DexieProvider', + init: initDB, + + setItem: (key, value) => { + if (value === null) { + return provider.removeItem(key); + } + return db.keyvaluepairs.put(value, key); + }, + multiGet: (keysParam) => { + return db.keyvaluepairs.bulkGet(keysParam).then((results) => { + return results.map((result, index) => [keysParam[index], result ?? null]); + }); + }, + multiMerge: (pairs) => { + return db.transaction('rw', db.keyvaluepairs, () => { + return Promise.all( + pairs.map(([key, value]) => { + if (value === null) { + return provider.removeItem(key); + } + return db.keyvaluepairs.get(key).then((existingItem) => { + const newValue = utils.fastMerge(existingItem as Record, value as Record); + return db.keyvaluepairs.put(newValue, key); + }); + }), + ); + }); + }, + mergeItem: (key, _deltaChanges, preMergedValue) => { + // Since Onyx also merged the existing value with the changes, we can just set the value directly + return provider.setItem(key, preMergedValue); + }, + multiSet: (pairs) => { + const pairsWithoutNull = pairs.filter(([, value]) => value !== null); + return db.keyvaluepairs.bulkPut( + pairsWithoutNull.map(([, value]) => value), + pairsWithoutNull.map(([key]) => key), + ); + }, + clear: () => { + return db.keyvaluepairs.clear(); + }, + getAllKeys: () => { + return db.keyvaluepairs.toCollection().keys(); + }, + getItem: (key) => { + return db.keyvaluepairs.get(key).then((result) => { + return result ?? null; + }); + }, + removeItem: (key) => { + return db.keyvaluepairs.delete(key); + }, + removeItems: (keysParam) => { + return db.keyvaluepairs.bulkDelete(keysParam); + }, + getDatabaseSize: () => { + if (!window.navigator || !window.navigator.storage) { + return Promise.reject(new Error('StorageManager browser API unavailable')); + } + + return window.navigator.storage + .estimate() + .then((estimate) => { + return { + bytesUsed: estimate.usage ?? 0, + bytesRemaining: (estimate.quota ?? 0) - (estimate.usage ?? 0), + }; + }) + .catch((error) => { + throw new Error(`Unable to estimate web storage quota. Original error: ${error}`); + }); + }, +}; + +export default provider; diff --git a/package-lock.json b/package-lock.json index 820608662..c68ce764d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "ascii-table": "0.0.9", + "dexie": "^4.0.8", "fast-equals": "^4.0.3", "underscore": "^1.13.6" }, @@ -7296,6 +7297,11 @@ "node": ">=8" } }, + "node_modules/dexie": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.8.tgz", + "integrity": "sha512-1G6cJevS17KMDK847V3OHvK2zei899GwpDiqfEXHP1ASvme6eWJmAp9AU4s1son2TeGkWmC0g3y8ezOBPnalgQ==" + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -24344,6 +24350,11 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, + "dexie": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.8.tgz", + "integrity": "sha512-1G6cJevS17KMDK847V3OHvK2zei899GwpDiqfEXHP1ASvme6eWJmAp9AU4s1son2TeGkWmC0g3y8ezOBPnalgQ==" + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", diff --git a/package.json b/package.json index a226c7bee..ec021d95f 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ }, "dependencies": { "ascii-table": "0.0.9", + "dexie": "^4.0.8", "fast-equals": "^4.0.3", "underscore": "^1.13.6" },