diff --git a/config.ts b/config.ts index 6a09ba4150..b96a06fde8 100644 --- a/config.ts +++ b/config.ts @@ -27,7 +27,6 @@ export const Config = { ipfs: { gateway: 'https://satellite.infura-ipfs.io/ipfs/', }, - indexedDbName: 'SatelliteDB', // Keep in sync with Sounds enum in SoundManager.ts sounds: { doesLoop: ['call'], diff --git a/libraries/SatelliteDB/SatelliteDB.test.ts b/libraries/SatelliteDB/SatelliteDB.test.ts deleted file mode 100644 index 044b66a78e..0000000000 --- a/libraries/SatelliteDB/SatelliteDB.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import 'fake-indexeddb/auto' -import { SatelliteDB } from './SatelliteDB' -import SearchIndex from './SearchIndex' - -describe('SatelliteDB', () => { - let db: SatelliteDB - beforeEach(async () => { - db = new SatelliteDB() - }) - - test('tables', () => { - expect(Object.keys(db.tables)).toMatchSnapshot() - }) - - test('storing and retrieving data', async () => { - const data = [ - { - key: '1', - lastInbound: 1, - }, - { - key: '2', - lastInbound: 2, - }, - ] - await db.conversations.bulkPut(data) - expect(await db.conversations.toArray()).toEqual(data) - }) - - test('creating search indexes', async () => { - await db.initializeSearchIndexes() - expect(Object.keys(db.search)).toEqual(['friends', 'conversationMessages']) - expect(db.search.conversationMessages).toBeInstanceOf(SearchIndex) - }) - - test('reinitializing schema', async () => { - db.version = jest.fn() - await db.initializeSchema() - expect(db.version).not.toHaveBeenCalled() - }) - - test('restoring search indexes', async () => { - const where = { address: 'foo' } - const data = { name: 'bar' } - await db.initializeSearchIndexes() - await db.upsert('friends', where, data) - await db.saveSearchIndexes() - await db.close() - - db = new SatelliteDB() - await db.initializeSearchIndexes() - expect(await db.search.friends.search('bar')?.[0]?.address).toEqual( - where.address, - ) - }) - - test('reinitializing search indexes', async () => { - const where = { address: 'foo' } - const data = { name: 'bar' } - await db.initializeSearchIndexes() - await db.upsert('friends', where, data) - await db.saveSearchIndexes() - await db.close() - - db = new SatelliteDB() - await db.initializeSearchIndexes() - expect(await db.search.friends.search('bar')?.[0]?.address).toEqual( - where.address, - ) - db.keyValue = jest.fn() - await db.initializeSearchIndexes() - expect(db.keyValue).not.toHaveBeenCalled() - }) - - test('upserting records', async () => { - const where = { address: '1' } - const original = { - name: 'foo', - photoHash: 'bar', - lastUpdate: 1, - } - await db.upsert('friends', where, original) - expect(await db.friends.get('1')).toEqual({ ...where, ...original }) - const update = { - name: 'foo bar', - } - await db.upsert('friends', where, update) - expect(await db.friends.get('1')).toEqual({ - ...where, - ...original, - ...update, - }) - }) -}) diff --git a/libraries/SatelliteDB/SatelliteDB.ts b/libraries/SatelliteDB/SatelliteDB.ts deleted file mode 100644 index cc4167bfa3..0000000000 --- a/libraries/SatelliteDB/SatelliteDB.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { Dexie } from 'dexie' -import SearchIndex from './SearchIndex' -import { Config } from '~/config' - -export type DexieConversation = { - key: string - lastInbound: number -} -export type DexieMessage = Message & { - conversation: string -} - -export type DexieFriend = { - address: string - name: string - photoHash: string | undefined - lastUpdate: number -} - -export type KeyValue = { - key: string - value: string - namespace: string -} - -export class SatelliteDB extends Dexie { - public isInitialized: boolean = false - public isSearchInitialized: boolean = false - public conversations: Dexie.Table - public conversationMessages: Dexie.Table - public friends: Dexie.Table - public keyValue: Dexie.Table - - public search: { - [key: string]: SearchIndex - } = {} - - public constructor() { - super(Config.indexedDbName) - this.initializeSchema() - - this.conversations = this.table('conversations') - this.conversationMessages = this.table('conversationMessages') - this.friends = this.table('friends') - this.keyValue = this.table('keyValue') - } - - /** - * Initialize the schema for the database. - * @returns {void} - */ - initializeSchema() { - if (this.isInitialized) return - this.version(1).stores({ - conversations: 'key, lastInbound', - conversationMessages: - '&id, conversation, from, to, at, readAt, type, payload', - }) - - this.version(2).stores({ - friends: '&address, name, photoHash, lastUpdate', - }) - - this.version(3).stores({ - keyValue: '&key, value, namespace', - }) - this.isInitialized = true - } - - /** - * Initialize search indexes for tables that have them. - * @returns {Promise - */ - async initializeSearchIndexes() { - if (this.isSearchInitialized) return - const searchIndexes: KeyValue[] = await this.keyValue - .where('namespace') - .equals('searchIndex') - .toArray() - for (const index of searchIndexes) { - const { key, value } = index - if (typeof key === 'string' && value) { - try { - this.search[key] = SearchIndex.deserialize(value) - } catch (_) {} - } - } - - if (!this.search.friends) { - this.search.friends = new SearchIndex({ - schema: { - fields: ['address', 'name', 'photoHash'], - storeFields: ['address'], - idField: 'address', - }, - }) - } - - if (!this.search.conversationMessages) { - this.search.conversationMessages = new SearchIndex({ - schema: { - fields: [ - 'id', - 'conversation', - 'from', - 'to', - 'at', - 'readAt', - 'type', - 'payload', - ], - storeFields: ['id', 'conversation', 'from'], - }, - }) - } - this.isSearchInitialized = true - } - - /** - * - * @param table the name of the table - * @param where the where clause - * @param data the data to insert or update. if inserting, will merge with where clause, - * @returns {Promise} - */ - async upsert( - table: string, - where: { [key: string]: any }, - data: { [key: string]: any }, - ) { - if (this.search[table]) { - this.search[table].add({ ...where, ...data }) - } - const exists = await this.table(table).get(where) - if (exists) { - return this.table(table).update(where, data) - } - return this.table(table).add({ ...where, ...data }) - } - - async saveSearchIndexes() { - for (const [key, index] of Object.entries(this.search)) { - await this.keyValue.put({ - key, - value: index.serialize(), - namespace: 'searchIndex', - }) - } - } -} - -export const db = new SatelliteDB() -export default db diff --git a/libraries/SatelliteDB/__snapshots__/SatelliteDB.test.ts.snap b/libraries/SatelliteDB/__snapshots__/SatelliteDB.test.ts.snap deleted file mode 100644 index 0dbf532147..0000000000 --- a/libraries/SatelliteDB/__snapshots__/SatelliteDB.test.ts.snap +++ /dev/null @@ -1,10 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SatelliteDB tables 1`] = ` -Array [ - "0", - "1", - "2", - "3", -] -`; diff --git a/package.json b/package.json index ee9852dc4f..12d6fd6261 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "dayjs": "^1.10.8", "deepmerge": "^4.2.2", "detectrtc": "^1.4.1", - "dexie": "^3.2.1", "ed25519-hd-key": "^1.2.0", "ed2curve": "^0.3.0", "emoji-regex": "^10.1.0", @@ -87,10 +86,10 @@ "@commitlint/cli": "^12.0.1", "@commitlint/config-conventional": "^12.0.1", "@nuxt/bridge": "npm:@nuxt/bridge-edge", - "@nuxtjs/device": "^2.1.0", - "@nuxtjs/style-resources": "^1.2.1", "@nuxt/types": "^2.15.8", + "@nuxtjs/device": "^2.1.0", "@nuxtjs/eslint-config-typescript": "^10.0.0", + "@nuxtjs/style-resources": "^1.2.1", "@types/ed2curve": "^0.2.2", "@types/howler": "^2.2.5", "@types/jest": "^28.1.7", diff --git a/store/settings/actions.test.ts b/store/settings/actions.test.ts index 8b7da4fe83..1794ce6da6 100644 --- a/store/settings/actions.test.ts +++ b/store/settings/actions.test.ts @@ -1,7 +1,5 @@ -import { Dexie } from 'dexie' import * as actions from './actions' import { SettingsError } from './types' -import { db } from '~/libraries/SatelliteDB/SatelliteDB' describe('actions.default', () => { const original = window.location diff --git a/store/settings/actions.ts b/store/settings/actions.ts index fcc6e11961..9c8e933025 100644 --- a/store/settings/actions.ts +++ b/store/settings/actions.ts @@ -1,18 +1,66 @@ -import { Dexie } from 'dexie' import { SettingsError } from '~/store/settings/types' -async function deleteAllDexieDbs() { - const databases = await Dexie.getDatabaseNames() +// begin - firefox polyfill +if (window.indexedDB && typeof window.indexedDB.databases === 'undefined') { + const LOCALSTORAGE_CACHE_KEY = 'indexedDBDatabases' - databases.forEach((name) => { - new Dexie(name).delete() - }) + // Store a key value map of databases + const getFromStorage = () => + JSON.parse(window.localStorage[LOCALSTORAGE_CACHE_KEY] || '{}') + + // Write the database to local storage + const writeToStorage = (value: any) => + (window.localStorage[LOCALSTORAGE_CACHE_KEY] = JSON.stringify(value)) + + IDBFactory.prototype.databases = () => + Promise.resolve( + Object.entries(getFromStorage()).reduce((acc, [name, version]) => { + acc.push({ name, version }) + return acc + }, []), + ) + + // Intercept the existing open handler to write our DBs names + // and versions to localStorage + const open = IDBFactory.prototype.open + + IDBFactory.prototype.open = function (...args) { + const dbName = args[0] + const version = args[1] || 1 + const existing = getFromStorage() + writeToStorage({ ...existing, [dbName]: version }) + return open.apply(this, args) + } + + // Intercept the existing deleteDatabase handler remove our + // dbNames from localStorage + const deleteDatabase = IDBFactory.prototype.deleteDatabase + + IDBFactory.prototype.deleteDatabase = function (...args) { + const dbName = args[0] + const existing = getFromStorage() + delete existing[dbName] + writeToStorage(existing) + return deleteDatabase.apply(this, args) + } +} +// end polyfill + +async function deleteIndexedDb() { + const databases = await indexedDB.databases() + Promise.all( + databases.map(async (db) => { + if (db.name) { + indexedDB.deleteDatabase(db.name) + } + }), + ) } export default { async clearLocalStorage() { try { - await deleteAllDexieDbs() + await deleteIndexedDb() localStorage.clear() } catch (e) { throw new Error(SettingsError.DATABASE_NOT_CLEARED)