This commit is contained in:
Nolan Lawson 2020-05-12 20:25:46 -07:00
parent b2d3c5cc38
commit 961140c167
9 changed files with 113 additions and 70 deletions

View File

@ -41,6 +41,7 @@
},
"standard": {
"global": [
"fetch",
"indexedDB",
"IDBKeyRange"
]

73
src/database/Database.js Normal file
View File

@ -0,0 +1,73 @@
import { IndexedDBEngine } from './IndexedDBEngine'
import { assertNonEmptyString } from './utils/assertNonEmptyString'
const DEFAULT_DATA_SOURCE = 'https://cdn.jsdelivr.net/npm/emojibase-data@5/en/compact.json'
const DEFAULT_LOCALE = 'en'
export class Database {
constructor ({ dataSource = DEFAULT_DATA_SOURCE, locale = DEFAULT_LOCALE }) {
this._dataSource = dataSource
this._locale = locale
this._idbEngine = undefined
this._readyPromise = this._init()
}
async _init () {
const emojiBaseData = await (await fetch(this._dataSource)).json()
if (!emojiBaseData || !Array.isArray(emojiBaseData)) {
throw new Error('Expected emojibase data, but data was in wrong format: ' + emojiBaseData)
}
this._idbEngine = new IndexedDBEngine(`lite-emoji-picker-${this._locale}`)
await this._idbEngine.open()
}
async loadData (emojiBaseData) {
if (!emojiBaseData || !Array.isArray(emojiBaseData)) {
throw new Error('Expected emojibase data, got: ' + emojiBaseData)
}
await this._readyPromise
await this._idbEngine.loadData(emojiBaseData)
}
async getEmojiByGroup (group) {
if (typeof group !== 'number') {
throw new Error('group must be a number, got: ' + group)
}
await this._readyPromise
return this._idbEngine.getEmojiByGroup(group)
}
async getEmojiBySearchPrefix (prefix) {
assertNonEmptyString(prefix)
await this._readyPromise
return this._idbEngine.getEmojiBySearchPrefix(prefix)
}
async getEmojiByShortcode (shortcode) {
assertNonEmptyString(shortcode)
await this._readyPromise
return this._idbEngine.getEmojiByShortcode(shortcode)
}
async getEmojiByUnicode (unicode) {
assertNonEmptyString(unicode)
await this._readyPromise
return this._idbEngine.getEmojiByUnicode(unicode)
}
async close () {
await this._readyPromise
if (this._idbEngine) {
await this._idbEngine.close()
this._idbEngine = undefined
}
}
async delete () {
await this._readyPromise
if (this._idbEngine) {
await this._idbEngine.delete()
this._idbEngine = undefined
}
}
}

View File

@ -1,7 +1,6 @@
import { closeDatabase, dbPromise, openDatabase } from './databaseLifecycle'
import { closeDatabase, dbPromise, deleteDatabase, openDatabase } from './databaseLifecycle'
import {
DATA_VERSION_CURRENT,
DB_NAME,
INDEX_GROUP_AND_ORDER, INDEX_TOKENS,
KEY_VERSION, MODE_READONLY, MODE_READWRITE,
STORE_EMOJI,
@ -10,11 +9,13 @@ import {
import { transformEmojiBaseData } from './transformEmojiBaseData'
export class IndexedDBEngine {
constructor () {
constructor (dbName) {
this._db = null
this.readyPromise = openDatabase(DB_NAME).then(db => {
this._db = db
})
this._dbName = dbName
}
async open () {
this._db = await openDatabase(this._dbName)
}
async loadData (emojiBaseData) {
@ -76,7 +77,12 @@ export class IndexedDBEngine {
}
async close () {
await closeDatabase(DB_NAME)
await closeDatabase(this._dbName)
this._db = null
}
async delete () {
await deleteDatabase(this._dbName)
this._db = null
}
}

View File

@ -1,7 +1,6 @@
export const DB_VERSION_CURRENT = 1
export const DB_VERSION_INITIAL = 1
export const DATA_VERSION_INITIAL = 1
export const DB_NAME = 'lite-emoji-picker'
export const DATA_VERSION_CURRENT = 2
export const STORE_EMOJI = 'emoji'
export const STORE_META = 'meta'

View File

@ -1,58 +0,0 @@
import { IndexedDBEngine } from './IndexedDBEngine'
let idbEngine
function verifyNonEmptyString (str) {
if (typeof str !== 'string' || !str) {
throw new Error('expected a non-empty string, got: ' + str)
}
}
async function init () {
if (!idbEngine) {
idbEngine = new IndexedDBEngine()
await idbEngine.readyPromise
}
}
export async function loadData (emojiBaseData) {
if (!emojiBaseData || !Array.isArray(emojiBaseData)) {
throw new Error('Expected emojibase data, got: ' + emojiBaseData)
}
await init()
await idbEngine.loadData(emojiBaseData)
}
export async function getEmojiByGroup (group) {
if (typeof group !== 'number') {
throw new Error('group must be a number, got: ' + group)
}
await init()
return idbEngine.getEmojiByGroup(group)
}
export async function getEmojiBySearchPrefix (prefix) {
verifyNonEmptyString(prefix)
await init()
return idbEngine.getEmojiBySearchPrefix(prefix)
}
export async function getEmojiByShortcode (shortcode) {
verifyNonEmptyString(shortcode)
await init()
return idbEngine.getEmojiByShortcode(shortcode)
}
export async function getEmojiByUnicode (unicode) {
verifyNonEmptyString(unicode)
await init()
return idbEngine.getEmojiByUnicode(unicode)
}
export async function closeDatabase () {
if (idbEngine) {
await idbEngine.readyPromise
await idbEngine.close()
idbEngine = undefined
}
}

View File

@ -69,3 +69,19 @@ export function closeDatabase (dbName) {
delete openReqs[dbName]
delete databaseCache[dbName]
}
export function deleteDatabase (dbName) {
return new Promise((resolve, reject) => {
// close any open requests
const openReq = openReqs[dbName]
if (openReq && openReq.result) {
openReq.result.close()
}
delete openReqs[dbName]
delete databaseCache[dbName]
const req = indexedDB.deleteDatabase(dbName)
req.onsuccess = () => resolve()
req.onerror = () => reject(req.error)
req.onblocked = () => console.error(`database ${dbName} blocked`)
})
}

View File

@ -0,0 +1,5 @@
export function assertNonEmptyString (str) {
if (typeof str !== 'string' || !str) {
throw new Error('expected a non-empty string, got: ' + str)
}
}

View File

@ -1,5 +1,7 @@
import Picker from './svelte/Picker.svelte'
import { Database } from './database/Database'
export {
Picker
Picker,
Database
}
export * from './database/database.js'

View File

@ -10,8 +10,7 @@
import * as liteEmojiPicker from '../dist/es/index.js'
(async () => {
const emojiBaseData = await (await fetch('../node_modules/emojibase-data/en/compact.json')).json()
await liteEmojiPicker.loadData(emojiBaseData)
const database = new liteEmojiPicker.Database()
window.liteEmojiPicker = liteEmojiPicker
customElements.define('lite-emoji-picker', liteEmojiPicker.Picker)