diff --git a/flow-typed/indexedDB.js b/flow-typed/indexedDB.js new file mode 100644 index 000000000..0b0c94f1e --- /dev/null +++ b/flow-typed/indexedDB.js @@ -0,0 +1,31 @@ +declare class DOMStringList { + +[key: number]: string; + +length: number; + item: number => string | null; + contains: string => boolean; +} + +declare interface IDBDatabase extends EventTarget { + close(): void; + createObjectStore( + name: string, + options?: { + keyPath?: ?(string | string[]), + autoIncrement?: boolean, + ... + }, + ): IDBObjectStore; + deleteObjectStore(name: string): void; + transaction( + storeNames: string | string[], + mode?: 'readonly' | 'readwrite' | 'versionchange', + ): IDBTransaction; + name: string; + version: number; + objectStoreNames: string[]; + objectStoreNames: DOMStringList; + onabort: (e: any) => mixed; + onclose: (e: any) => mixed; + onerror: (e: any) => mixed; + onversionchange: (e: any) => mixed; +} diff --git a/src/plugins/navigation/util/indexedDB.js b/src/plugins/navigation/util/indexedDB.js new file mode 100644 index 000000000..b0fe6910a --- /dev/null +++ b/src/plugins/navigation/util/indexedDB.js @@ -0,0 +1,94 @@ +/** + * Copyright 2018-present Facebook. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * @format + * @flow strict-local + */ + +import type {Bookmark} from '../'; + +const FLIPPER_NAVIGATION_PLUGIN_DB = 'flipper_navigation_plugin_db'; +const FLIPPER_NAVIGATION_PLUGIN_DB_VERSION = 1; + +const BOOKMARKS_KEY = 'bookmarks'; + +const createBookmarksObjectStore: IDBDatabase => Promise = ( + db: IDBDatabase, +) => { + return new Promise((resolve, reject) => { + if (!db.objectStoreNames.contains(BOOKMARKS_KEY)) { + const bookmarksObjectStore = db.createObjectStore(BOOKMARKS_KEY, { + keyPath: 'uri', + }); + bookmarksObjectStore.transaction.oncomplete = () => resolve(); + bookmarksObjectStore.transaction.onerror = event => + reject(event.target.error); + } else { + resolve(); + } + }); +}; + +const initializeNavigationPluginDB: IDBDatabase => Promise> = ( + db: IDBDatabase, +) => { + return Promise.all([createBookmarksObjectStore(db)]); +}; + +const openNavigationPluginDB: () => Promise = () => { + return new Promise((resolve, reject) => { + const openRequest = window.indexedDB.open( + FLIPPER_NAVIGATION_PLUGIN_DB, + FLIPPER_NAVIGATION_PLUGIN_DB_VERSION, + ); + openRequest.onupgradeneeded = () => { + const db = openRequest.result; + initializeNavigationPluginDB(db).then(() => resolve(db)); + }; + openRequest.onerror = event => reject(event.target.error); + openRequest.onsuccess = () => resolve(openRequest.result); + }); +}; + +export const writeBookmarkToDB: Bookmark => Promise = ( + bookmark: Bookmark, +) => { + return new Promise((resolve, reject) => { + openNavigationPluginDB() + .then((db: IDBDatabase) => { + const bookmarksObjectStore = db + .transaction(BOOKMARKS_KEY, 'readwrite') + .objectStore(BOOKMARKS_KEY); + const request = bookmarksObjectStore.put(bookmark); + request.onsuccess = () => resolve(); + request.onerror = event => reject(event.target.error); + }) + .catch(reject); + }); +}; + +export const readBookmarksFromDB: () => Promise> = () => { + return new Promise((resolve, reject) => { + const bookmarks = new Map(); + openNavigationPluginDB() + .then((db: IDBDatabase) => { + const bookmarksObjectStore = db + .transaction(BOOKMARKS_KEY) + .objectStore(BOOKMARKS_KEY); + bookmarksObjectStore.openCursor().onsuccess = event => { + const cursor = event.target.result; + if (cursor) { + const bookmark = cursor.value; + bookmarks.set(bookmark.uri, bookmark); + cursor.continue(); + } else { + resolve(bookmarks); + } + }; + bookmarksObjectStore.openCursor().onerror = event => + reject(event.target.error); + }) + .catch(reject); + }); +};