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' } }