* Also request persistent storage on wallet save * Request persistent storage on push subscription
181 lines
4.4 KiB
JavaScript
181 lines
4.4 KiB
JavaScript
import { useMe } from '@/components/me'
|
|
import { useCallback, useMemo } from 'react'
|
|
|
|
const VERSION = 2
|
|
|
|
export function useIndexedDB (dbName) {
|
|
const { me } = useMe()
|
|
if (!dbName) dbName = me?.id ? `app:storage:${me.id}` : 'app:storage'
|
|
|
|
const set = useCallback(async (storeName, key, value) => {
|
|
const db = await _open(dbName, VERSION)
|
|
|
|
try {
|
|
return await _set(db, storeName, key, value)
|
|
} finally {
|
|
db.close()
|
|
}
|
|
}, [dbName])
|
|
|
|
const get = useCallback(async (storeName, key) => {
|
|
const db = await _open(dbName, VERSION)
|
|
|
|
try {
|
|
return await _get(db, storeName, key)
|
|
} finally {
|
|
db.close()
|
|
}
|
|
}, [dbName])
|
|
|
|
const deleteDb = useCallback(async () => {
|
|
return await _delete(dbName)
|
|
}, [dbName])
|
|
|
|
const open = useCallback(async () => {
|
|
return await _open(dbName, VERSION)
|
|
}, [dbName])
|
|
|
|
return useMemo(() => ({ set, get, deleteDb, open }), [set, get, deleteDb, open])
|
|
}
|
|
|
|
async function _open (dbName, version = 1) {
|
|
return await new Promise((resolve, reject) => {
|
|
if (typeof window.indexedDB === 'undefined') {
|
|
return reject(new IndexedDBOpenError('IndexedDB unavailable'))
|
|
}
|
|
|
|
const request = window.indexedDB.open(dbName, version)
|
|
|
|
request.onupgradeneeded = (event) => {
|
|
try {
|
|
const db = event.target.result
|
|
if (!db.objectStoreNames.contains('vault')) db.createObjectStore('vault')
|
|
if (db.objectStoreNames.contains('wallet_logs')) db.deleteObjectStore('wallet_logs')
|
|
} catch (error) {
|
|
reject(new IndexedDBOpenError(`upgrade failed: ${error?.message}`))
|
|
}
|
|
}
|
|
|
|
request.onerror = (event) => {
|
|
reject(new IndexedDBOpenError(request.error?.message))
|
|
}
|
|
|
|
request.onsuccess = (event) => {
|
|
const db = request.result
|
|
resolve(db)
|
|
}
|
|
})
|
|
}
|
|
|
|
async function _set (db, storeName, key, value) {
|
|
return await new Promise((resolve, reject) => {
|
|
let request
|
|
try {
|
|
request = db
|
|
.transaction(storeName, 'readwrite')
|
|
.objectStore(storeName)
|
|
.put(value, key)
|
|
} catch (error) {
|
|
return reject(new IndexedDBSetError(error?.message))
|
|
}
|
|
|
|
request.onerror = (event) => {
|
|
reject(new IndexedDBSetError(event.target?.error?.message))
|
|
}
|
|
|
|
request.onsuccess = () => {
|
|
resolve(request.result)
|
|
}
|
|
})
|
|
}
|
|
|
|
async function _get (db, storeName, key) {
|
|
return await new Promise((resolve, reject) => {
|
|
let request
|
|
try {
|
|
request = db
|
|
.transaction(storeName)
|
|
.objectStore(storeName)
|
|
.get(key)
|
|
} catch (error) {
|
|
return reject(new IndexedDBGetError(error?.message))
|
|
}
|
|
|
|
request.onerror = (event) => {
|
|
reject(new IndexedDBGetError(event.target?.error?.message))
|
|
}
|
|
|
|
request.onsuccess = () => {
|
|
resolve(request.result)
|
|
}
|
|
})
|
|
}
|
|
|
|
async function _delete (dbName) {
|
|
return await new Promise((resolve, reject) => {
|
|
if (typeof window.indexedDB === 'undefined') {
|
|
return reject(new IndexedDBOpenError('IndexedDB unavailable'))
|
|
}
|
|
|
|
const request = window.indexedDB.deleteDatabase(dbName)
|
|
|
|
request.onerror = (event) => {
|
|
reject(new IndexedDBDeleteError(event.target?.error?.message))
|
|
}
|
|
|
|
request.onsuccess = () => {
|
|
resolve(request.result)
|
|
}
|
|
})
|
|
}
|
|
|
|
export async function requestPersistentStorage () {
|
|
try {
|
|
if (!('persisted' in navigator.storage) || !('persist' in navigator.storage)) {
|
|
throw new Error('persistent storage not supported')
|
|
}
|
|
const persisted = await navigator.storage.persisted()
|
|
if (!persisted) {
|
|
// browser might prompt the user to allow persistent storage
|
|
return await navigator.storage.persist()
|
|
}
|
|
} catch (err) {
|
|
console.error('failed to request persistent storage:', err)
|
|
}
|
|
}
|
|
|
|
class IndexedDBError extends Error {
|
|
constructor (message) {
|
|
super(message)
|
|
this.name = 'IndexedDBError'
|
|
}
|
|
}
|
|
|
|
class IndexedDBOpenError extends IndexedDBError {
|
|
constructor (message) {
|
|
super(message)
|
|
this.name = 'IndexedDBOpenError'
|
|
}
|
|
}
|
|
|
|
class IndexedDBSetError extends IndexedDBError {
|
|
constructor (message) {
|
|
super(message)
|
|
this.name = 'IndexedDBSetError'
|
|
}
|
|
}
|
|
|
|
class IndexedDBGetError extends IndexedDBError {
|
|
constructor (message) {
|
|
super(message)
|
|
this.name = 'IndexedDBGetError'
|
|
}
|
|
}
|
|
|
|
class IndexedDBDeleteError extends IndexedDBError {
|
|
constructor (message) {
|
|
super(message)
|
|
this.name = 'IndexedDBDeleteError'
|
|
}
|
|
}
|