Created
May 5, 2020 20:48
-
-
Save codinronan/89d764b42d70aff6fa8e1c601f3092d2 to your computer and use it in GitHub Desktop.
idb-keyval temporary improvements
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a private copy of idb-keyval vNext, which has a simpler API with many bug fixes. | |
// https://raw.githubusercontent.com/jakearchibald/idb-keyval/next/src/index.ts | |
// https://github.com/jakearchibald/idb-keyval/issues/80 (description of API) | |
// The old implementation with bug fixes from several PRs in idb-keyval is at | |
// https://github.com/DestinyItemManager/DIM/blob/master/src/app/storage/idb-keyval.ts | |
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB#Version_changes_while_a_web_app_is_open_in_another_tab | |
const DB_NAME = 'rg-app-data'; // 'keyval-store' | |
const DB_STORE = 'rg-data'; // 'keyval' | |
let defaultGetStoreFunc: | |
| ((txMode: IDBTransactionMode) => Promise<IDBObjectStore>) | |
| undefined; | |
let indexedDBSupported = false; | |
let _close = () => undefined; | |
export function checkSupported(): Promise<boolean> { | |
if (indexedDBSupported) { | |
return Promise.resolve(true); | |
} | |
(window as any).indexedDB = | |
window.indexedDB || (window as any).mozIndexedDB || (window as any).webkitIndexedDB || (window as any).msIndexedDB; | |
window.IDBTransaction = window.IDBTransaction || (window as any).webkitIDBTransaction || (window as any).msIDBTransaction; | |
window.IDBKeyRange = window.IDBKeyRange || (window as any).webkitIDBKeyRange || (window as any).msIDBKeyRange; | |
const isSupported = !!window.indexedDB && !!window.IDBTransaction && !!window.IDBKeyRange; | |
if (isSupported) { | |
const request = indexedDB.open(DB_NAME, 1); | |
const dbp = promisifyRequest<IDBDatabase>(request); | |
return dbp.then(() => { indexedDBSupported = true; return true; }).catch(() => false); | |
} | |
return Promise.resolve(false); | |
} | |
function promisifyRequest<T = undefined>( | |
request: IDBRequest<T> | IDBTransaction, | |
): Promise<T> { | |
return new Promise<T>((resolve, reject) => { | |
// @ts-ignore - file size hacks | |
request.oncomplete = request.onsuccess = () => resolve(request.result); | |
// @ts-ignore - file size hacks | |
request.onabort = request.onerror = () => reject(request.error); | |
}); | |
} | |
function useDatabase(db: IDBDatabase) { | |
_close = () => { | |
db.close(); | |
defaultGetStoreFunc = null; // we must null this so that a new open() call occurs. | |
}; | |
// Make sure to add a handler to be notified if another page requests a version | |
// change. We must close the database. This allows the other page to upgrade the database. | |
// If you don't do this then the upgrade won't happen until the user closes the tab. | |
db.onversionchange = function () { | |
_close(); | |
console.log('A new version of this page is ready. Please reload or close this tab!'); | |
}; | |
db.onclose = () => (defaultGetStoreFunc = null); | |
} | |
export function createStore(dbName: string, storeName: string) { | |
const request = indexedDB.open(dbName, 1); | |
request.onupgradeneeded = () => { | |
request.result.createObjectStore(storeName); | |
useDatabase(request.result); | |
}; | |
request.onblocked = function () { | |
// If some other tab is loaded with the database, then it needs to be closed before we can proceed. | |
console.log('Please close all other tabs with this site open!'); | |
}; | |
const dbp = promisifyRequest<IDBDatabase>(request); | |
dbp.then(useDatabase); | |
return (txMode?: IDBTransactionMode) => | |
dbp.then((db) => db.transaction(storeName, txMode).objectStore(storeName)); | |
} | |
function defaultGetStore() { | |
if (!defaultGetStoreFunc) { | |
defaultGetStoreFunc = createStore(DB_NAME, DB_STORE); | |
} | |
return defaultGetStoreFunc; | |
} | |
export function get<T = any>( | |
key: IDBValidKey, | |
getStore = defaultGetStore(), | |
): Promise<T> { | |
return getStore('readonly').then((store) => promisifyRequest(store.get(key))); | |
} | |
export function set( | |
key: IDBValidKey, | |
value: any, | |
getStore = defaultGetStore(), | |
): Promise<void> { | |
return getStore('readwrite').then((store) => { | |
store.put(value, key); | |
return promisifyRequest(store.transaction); | |
}); | |
} | |
export function del( | |
key: IDBValidKey, | |
getStore = defaultGetStore(), | |
): Promise<void> { | |
return getStore('readwrite').then((store) => { | |
store.delete(key); | |
return promisifyRequest(store.transaction); | |
}); | |
} | |
export function clear(getStore = defaultGetStore()): Promise<void> { | |
return getStore('readwrite').then((store) => { | |
store.clear(); | |
return promisifyRequest(store.transaction); | |
}); | |
} | |
export function keys(getStore = defaultGetStore()): Promise<IDBValidKey[]> { | |
const keyset: IDBValidKey[] = []; | |
return getStore('readonly') | |
.then((store) => { | |
// This would be store.getAllKeys(), but it isn't supported by Edge or Safari. | |
// And openKeyCursor isn't supported by Safari. | |
(store.openKeyCursor || store.openCursor).call( | |
store, | |
).onsuccess = function () { | |
if (!this.result) { return; } | |
keyset.push(this.result.key); | |
this.result.continue(); | |
}; | |
return promisifyRequest(store.transaction); | |
}) | |
.then(() => keyset); | |
} | |
export function close(): Promise<void> { | |
return _close(); | |
} | |
// When the app gets frozen (iOS PWA), close the IDBDatabase connection | |
window.addEventListener('freeze', () => { | |
close(); | |
}); | |
checkSupported(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment