Added IndexedDB utility functions for reading and writing bookmarks

Summary:
These are utility function for reading from the IndexedDB and writing to it. This will be used for persistant storage of bookmarks.

The reason I chose IndexedDB over local storage is due to the poor efficiency of stringifying and parsing a long list of bookmarks everytime we read and write to local storage.

With IndexedDB we can modify a single value at a time.

Bookmarks are passed around as a Map, with the key being the uri's and the common names being the values. This allows me to check if a specified uri is in the Map in O(1) time, so that I can highlight the star icon in the UI with gold.

Reviewed By: jknoxville

Differential Revision: D16498744

fbshipit-source-id: 7c7af28bf4eb3fcc985a71dfd61ffbdb8481b6a6
This commit is contained in:
Benjamin Elo
2019-07-28 12:46:10 -07:00
committed by Facebook Github Bot
parent f6a4ad59c0
commit e4601a89f3
2 changed files with 125 additions and 0 deletions

31
flow-typed/indexedDB.js vendored Normal file
View File

@@ -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;
}

View File

@@ -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<void> = (
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<Array<void>> = (
db: IDBDatabase,
) => {
return Promise.all([createBookmarksObjectStore(db)]);
};
const openNavigationPluginDB: () => Promise<IDBDatabase> = () => {
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<void> = (
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<Map<string, Bookmark>> = () => {
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);
});
};