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

151 lines
4.2 KiB
JavaScript
Raw Normal View History

2020-05-13 05:25:46 +02:00
import { assertNonEmptyString } from './utils/assertNonEmptyString'
2020-05-17 02:51:37 +02:00
import { assertNumber } from './utils/assertNumber'
2020-06-12 06:12:00 +02:00
import {
DEFAULT_DATA_SOURCE,
DEFAULT_LOCALE,
KEY_PREFERRED_SKINTONE,
STORE_KEYVALUE
} from './constants'
2020-05-17 19:56:31 +02:00
import { uniqEmoji } from './utils/uniqEmoji'
import {
closeDatabase,
2024-03-10 20:56:51 +01:00
deleteDatabase
} from './databaseLifecycle'
2020-06-04 03:09:40 +02:00
import {
2024-03-10 20:56:51 +01:00
getEmojiByGroup,
2020-06-12 06:12:00 +02:00
getEmojiBySearchQuery, getEmojiByShortcode, getEmojiByUnicode,
get, set, getTopFavoriteEmoji, incrementFavoriteEmojiCount
2020-06-04 03:09:40 +02:00
} from './idbInterface'
2020-06-14 20:30:38 +02:00
import { customEmojiIndex } from './customEmojiIndex'
import { cleanEmoji } from './utils/cleanEmoji'
2024-03-10 20:56:51 +01:00
import { initDatabase } from './initDatabase.js'
2020-05-13 05:25:46 +02:00
export default class Database {
2020-06-14 20:30:38 +02:00
constructor ({ dataSource = DEFAULT_DATA_SOURCE, locale = DEFAULT_LOCALE, customEmoji = [] } = {}) {
2020-06-17 09:03:50 +02:00
this.dataSource = dataSource
this.locale = locale
this._dbName = `emoji-picker-element-${this.locale}`
2020-06-14 20:30:38 +02:00
this._custom = customEmojiIndex(customEmoji)
this._clear = this._clear.bind(this)
2024-03-10 20:56:51 +01:00
/* no await */ this._init()
2020-05-13 05:25:46 +02:00
}
async _init () {
2024-03-10 20:56:51 +01:00
if (this._controller) {
this._controller.abort()
2020-06-05 04:06:51 +02:00
}
2024-03-10 20:56:51 +01:00
this._controller = new AbortController()
this._dbPromise = initDatabase(this._dbName, this.dataSource, this._clear, this._controller.signal)
2020-05-13 05:25:46 +02:00
}
async ready () {
2024-03-10 20:56:51 +01:00
await this._recreateIfNecessary()
await this._dbPromise
}
async _recreateIfNecessary () {
if (!this._controller) {
await this._init()
}
2024-03-10 20:56:51 +01:00
const db = await this._dbPromise
return db
}
2020-05-13 05:25:46 +02:00
async getEmojiByGroup (group) {
2020-05-17 02:51:37 +02:00
assertNumber(group)
2024-03-10 20:56:51 +01:00
const db = await this._recreateIfNecessary()
return uniqEmoji(await getEmojiByGroup(db, group)).map(cleanEmoji)
2020-05-13 05:25:46 +02:00
}
2020-06-06 02:41:38 +02:00
async getEmojiBySearchQuery (query) {
assertNonEmptyString(query)
2024-03-10 20:56:51 +01:00
const db = await this._recreateIfNecessary()
2020-06-14 20:30:38 +02:00
const customs = this._custom.search(query)
2024-03-10 20:56:51 +01:00
const natives = uniqEmoji(await getEmojiBySearchQuery(db, query)).map(cleanEmoji)
2020-06-14 20:30:38 +02:00
return [
...customs,
...natives
]
2020-05-13 05:25:46 +02:00
}
async getEmojiByShortcode (shortcode) {
assertNonEmptyString(shortcode)
2024-03-10 20:56:51 +01:00
const db = await this._recreateIfNecessary()
2020-06-14 20:30:38 +02:00
const custom = this._custom.byShortcode(shortcode)
if (custom) {
return custom
}
2024-03-10 20:56:51 +01:00
return cleanEmoji(await getEmojiByShortcode(db, shortcode))
2020-05-13 05:25:46 +02:00
}
2020-06-15 08:38:43 +02:00
async getEmojiByUnicodeOrName (unicodeOrName) {
assertNonEmptyString(unicodeOrName)
2024-03-10 20:56:51 +01:00
const db = await this._recreateIfNecessary()
2020-06-15 08:38:43 +02:00
const custom = this._custom.byName(unicodeOrName)
if (custom) {
return custom
}
2024-03-10 20:56:51 +01:00
return cleanEmoji(await getEmojiByUnicode(db, unicodeOrName))
2020-05-13 05:25:46 +02:00
}
2020-06-12 05:04:42 +02:00
async getPreferredSkinTone () {
2024-03-10 20:56:51 +01:00
const db = await this._recreateIfNecessary()
return (await get(db, STORE_KEYVALUE, KEY_PREFERRED_SKINTONE)) || 0
2020-06-12 05:04:42 +02:00
}
async setPreferredSkinTone (skinTone) {
assertNumber(skinTone)
2024-03-10 20:56:51 +01:00
const db = await this._recreateIfNecessary()
return set(db, STORE_KEYVALUE, KEY_PREFERRED_SKINTONE, skinTone)
2020-06-12 05:04:42 +02:00
}
2020-06-14 23:42:05 +02:00
async incrementFavoriteEmojiCount (unicodeOrName) {
assertNonEmptyString(unicodeOrName)
2024-03-10 20:56:51 +01:00
const db = await this._recreateIfNecessary()
return incrementFavoriteEmojiCount(db, unicodeOrName)
2020-06-12 06:12:00 +02:00
}
2020-06-14 20:30:38 +02:00
async getTopFavoriteEmoji (limit) {
assertNumber(limit)
2024-03-10 20:56:51 +01:00
const db = await this._recreateIfNecessary()
return (await getTopFavoriteEmoji(db, this._custom, limit)).map(cleanEmoji)
2020-06-14 20:30:38 +02:00
}
set customEmoji (customEmojis) {
this._custom = customEmojiIndex(customEmojis)
}
get customEmoji () {
return this._custom.all
2020-06-12 06:12:00 +02:00
}
2024-03-10 20:56:51 +01:00
_shutdown () {
if (this._controller) {
this._controller.abort()
this._controller = undefined
this._dbPromise = undefined
}
2020-06-10 06:44:53 +02:00
}
2020-06-27 20:30:47 +02:00
// clear references to IDB, e.g. during a close event
_clear () {
console.log('_clear database', this._dbName)
2020-06-27 20:30:47 +02:00
// We don't need to call removeEventListener or remove the manual "close" listeners.
// The memory leak tests prove this is unnecessary. It's because:
// 1) IDBDatabases that can no longer fire "close" automatically have listeners GCed
// 2) we clear the manual close listeners in databaseLifecycle.js.
2024-03-10 20:56:51 +01:00
this._shutdown()
}
2020-06-10 06:44:53 +02:00
async close () {
2024-03-10 20:56:51 +01:00
this._shutdown()
2021-06-27 01:23:50 +02:00
await closeDatabase(this._dbName)
2020-05-13 05:25:46 +02:00
}
async delete () {
2024-03-10 20:56:51 +01:00
this._shutdown()
2021-06-27 01:23:50 +02:00
await deleteDatabase(this._dbName)
2020-05-13 05:25:46 +02:00
}
2020-05-17 02:20:00 +02:00
}