emoji-picker-element/src/database/Database.js

111 lines
3.3 KiB
JavaScript
Raw Normal View History

2020-05-13 05:25:46 +02:00
import { IndexedDBEngine } from './IndexedDBEngine'
import { assertNonEmptyString } from './utils/assertNonEmptyString'
import { warnETag } from './utils/warnETag'
2020-05-17 02:51:37 +02:00
import { assertEmojiBaseData } from './utils/assertEmojiBaseData'
import { assertNumber } from './utils/assertNumber'
2020-05-17 05:36:21 +02:00
import { DEFAULT_DATA_SOURCE, DEFAULT_LOCALE } from './constants'
2020-05-17 19:56:31 +02:00
import { uniqEmoji } from './utils/uniqEmoji'
import { jsonChecksum } from '../svelte/utils/jsonChecksum'
import { warnOffline } from './utils/warnOffline'
2020-05-13 05:25:46 +02:00
export class Database {
2020-05-17 02:20:00 +02:00
constructor ({ dataSource = DEFAULT_DATA_SOURCE, locale = DEFAULT_LOCALE } = {}) {
2020-05-13 05:25:46 +02:00
this._dataSource = dataSource
this._locale = locale
this._idbEngine = undefined
this._readyPromise = this._init()
}
async _init () {
2020-05-17 02:20:00 +02:00
this._idbEngine = new IndexedDBEngine(`lite-emoji-picker-${this._locale}`)
await this._idbEngine.open()
const url = this._dataSource
const isEmpty = await this._idbEngine.isEmpty()
if (!isEmpty) {
2020-05-17 02:20:00 +02:00
// just do a simple HEAD request first to see if the eTags match
let headResponse
try {
headResponse = await fetch(url, { method: 'HEAD' })
} catch (e) { // offline fallback
warnOffline(e)
2020-05-17 02:20:00 +02:00
return
}
const eTag = headResponse.headers.get('etag')
warnETag(eTag)
if (eTag && await this._idbEngine.hasData(url, eTag)) {
console.log('Database already populated')
2020-05-17 02:20:00 +02:00
return // fast init, data is already loaded
}
}
let response
try {
response = await fetch(this._dataSource)
} catch (e) { // offline fallback
if (!isEmpty) {
warnOffline(e)
return
}
throw e
}
2020-05-13 17:12:56 +02:00
const emojiBaseData = await response.json()
2020-05-17 02:51:37 +02:00
assertEmojiBaseData(emojiBaseData)
let eTag = response.headers.get('etag')
warnETag(eTag)
if (!eTag) {
// GNOME Web returns an empty eTag for cross-origin HEAD requests, even when
// Access-Control-Expose-Headers:* is set. So as a fallback, compute an ETag
// from the object itself.
eTag = await jsonChecksum(emojiBaseData)
}
if (!isEmpty && await this._idbEngine.hasData(url, eTag)) {
console.log('Database already populated')
return // data already loaded
}
2020-05-17 02:20:00 +02:00
await this._idbEngine.loadData(emojiBaseData, url, eTag)
2020-05-13 05:25:46 +02:00
}
async getEmojiByGroup (group) {
2020-05-17 02:51:37 +02:00
assertNumber(group)
2020-05-13 05:25:46 +02:00
await this._readyPromise
2020-05-17 19:56:31 +02:00
const emojis = await this._idbEngine.getEmojiByGroup(group)
return uniqEmoji(emojis)
2020-05-13 05:25:46 +02:00
}
async getEmojiBySearchPrefix (prefix) {
assertNonEmptyString(prefix)
await this._readyPromise
2020-05-17 19:56:31 +02:00
const emojis = await this._idbEngine.getEmojiBySearchPrefix(prefix)
return uniqEmoji(emojis)
2020-05-13 05:25:46 +02:00
}
async getEmojiByShortcode (shortcode) {
assertNonEmptyString(shortcode)
await this._readyPromise
2020-05-17 19:56:31 +02:00
const emojis = await this._idbEngine.getEmojiByShortcode(shortcode)
return uniqEmoji(emojis)
2020-05-13 05:25:46 +02:00
}
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
}
}
2020-05-17 02:20:00 +02:00
}