start on UI, shortcode -> shortcodes
This commit is contained in:
parent
786027fbd6
commit
15af26d5e7
|
@ -542,16 +542,20 @@ Custom emoji should follow the format:
|
|||
```js
|
||||
[
|
||||
{
|
||||
shortcode: 'foo',
|
||||
name: 'foo',
|
||||
shortcodes: ['foo'],
|
||||
url: 'http://example.com/foo.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'bar',
|
||||
name: 'bar',
|
||||
shortcodes: ['bar'],
|
||||
url: 'http://example.com/bar.png'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Note that names are assumed to be unique (case-insensitive), and it's assumed that the `shortcodes` have at least one entry.
|
||||
|
||||
To pass custom emoji into the `Picker`:
|
||||
|
||||
```js
|
||||
|
|
|
@ -129,10 +129,10 @@ export default class Database {
|
|||
return set(this._db, STORE_KEYVALUE, KEY_PREFERRED_SKINTONE, skinTone)
|
||||
}
|
||||
|
||||
async incrementFavoriteEmojiCount (unicodeOrShortcode) {
|
||||
assertNonEmptyString(unicodeOrShortcode)
|
||||
async incrementFavoriteEmojiCount (unicodeOrName) {
|
||||
assertNonEmptyString(unicodeOrName)
|
||||
await this.ready()
|
||||
return incrementFavoriteEmojiCount(this._db, unicodeOrShortcode)
|
||||
return incrementFavoriteEmojiCount(this._db, unicodeOrName)
|
||||
}
|
||||
|
||||
async getTopFavoriteEmoji (limit) {
|
||||
|
|
|
@ -5,31 +5,43 @@ import { findCommonMembers } from './utils/findCommonMembers'
|
|||
|
||||
export function customEmojiIndex (customEmojis) {
|
||||
assertCustomEmojis(customEmojis)
|
||||
// sort custom emojis by shortcode
|
||||
const all = customEmojis.sort((a, b) => a.shortcode.toLowerCase() < b.shortcode.toLowerCase() ? -1 : 1)
|
||||
// sort custom emojis by name
|
||||
const all = customEmojis.sort((a, b) => a.name < b.name ? -1 : 1)
|
||||
|
||||
// search query to custom emoji. Similar to how we do this in IDB, the last token
|
||||
// is treated as a prefix search, but every other one is treated as an exact match
|
||||
// Then we AND the results together
|
||||
const { byPrefix, byExactMatch } = trie(customEmojis, emoji => extractTokens(emoji.shortcode))
|
||||
const emojiToTokens = emoji => (
|
||||
[...new Set(emoji.shortcodes.map(shortcode => extractTokens(shortcode)).flat())]
|
||||
)
|
||||
const { byPrefix, byExactMatch } = trie(customEmojis, emojiToTokens)
|
||||
const search = query => {
|
||||
const searchTokens = extractTokens(query)
|
||||
const intermediateResults = [
|
||||
...searchTokens.slice(0, -1).map(byExactMatch),
|
||||
byPrefix(searchTokens[searchTokens.length - 1])
|
||||
]
|
||||
return findCommonMembers(intermediateResults, _ => _.shortcode).sort((a, b) => a.shortcode < b.shortcode ? -1 : 1)
|
||||
return findCommonMembers(intermediateResults, _ => _.name).sort((a, b) => a.name < b.name ? -1 : 1)
|
||||
}
|
||||
|
||||
// shortcodes to custom emoji
|
||||
const shortcodeToEmoji = new Map()
|
||||
for (const customEmoji of customEmojis) {
|
||||
shortcodeToEmoji.set(customEmoji.shortcode.toLowerCase(), customEmoji)
|
||||
for (const shortcode of customEmoji.shortcodes) {
|
||||
shortcodeToEmoji.set(shortcode.toLowerCase(), customEmoji)
|
||||
}
|
||||
}
|
||||
const byShortcode = shortcode => shortcodeToEmoji.get(shortcode.toLowerCase())
|
||||
|
||||
const nameToEmoji = new Map()
|
||||
for (const customEmoji of customEmojis) {
|
||||
nameToEmoji.set(customEmoji.name.toLowerCase(), customEmoji)
|
||||
}
|
||||
const byName = name => nameToEmoji.get(name.toLowerCase())
|
||||
return {
|
||||
all,
|
||||
search,
|
||||
byShortcode
|
||||
byShortcode,
|
||||
byName
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,13 +166,13 @@ export function getTopFavoriteEmoji (db, customEmojiIndex, limit) {
|
|||
cursor.continue()
|
||||
}
|
||||
|
||||
const unicodeOrShortcode = cursor.primaryKey
|
||||
const custom = customEmojiIndex.byShortcode(unicodeOrShortcode)
|
||||
const unicodeOrName = cursor.primaryKey
|
||||
const custom = customEmojiIndex.byName(unicodeOrName)
|
||||
if (custom) {
|
||||
return addResult(custom)
|
||||
}
|
||||
// TODO: this could be optimized by doing the get and the cursor.continue() in parallel
|
||||
getIDB(emojiStore, unicodeOrShortcode, emoji => {
|
||||
getIDB(emojiStore, unicodeOrName, emoji => {
|
||||
if (emoji) {
|
||||
return addResult(emoji)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const requiredKeys = [
|
||||
'shortcode',
|
||||
'name',
|
||||
'shortcodes',
|
||||
'url'
|
||||
]
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ export default class Picker extends SveltePicker {
|
|||
locale = DEFAULT_LOCALE,
|
||||
dataSource = DEFAULT_DATA_SOURCE,
|
||||
i18n = enI18n,
|
||||
skinToneEmoji = DEFAULT_SKIN_TONE_EMOJI
|
||||
skinToneEmoji = DEFAULT_SKIN_TONE_EMOJI,
|
||||
customEmoji = []
|
||||
} = {}) {
|
||||
mark('initialLoad')
|
||||
// Make the API simpler, directly pass in the props
|
||||
|
@ -19,7 +20,8 @@ export default class Picker extends SveltePicker {
|
|||
props: {
|
||||
database: new Database({ dataSource, locale }),
|
||||
i18n,
|
||||
skinToneEmoji
|
||||
skinToneEmoji,
|
||||
customEmoji
|
||||
}
|
||||
})
|
||||
this._locale = locale
|
||||
|
|
|
@ -123,8 +123,8 @@
|
|||
{#each currentEmojis as emoji, i (emoji.unicode)}
|
||||
<button role={searchMode ? 'option' : 'menuitem'}
|
||||
{...(searchMode ? { 'aria-selected': i == activeSearchItem } : null)}
|
||||
aria-label={emojiLabel(emoji)}
|
||||
title={emojiTitle(emoji)}
|
||||
aria-label={emoji.label}
|
||||
title={emoji.title}
|
||||
class="emoji {searchMode && i === activeSearchItem ? 'active' : ''}"
|
||||
data-emoji={emoji.unicode}>
|
||||
{unicodeWithSkin(emoji, currentSkinTone)}
|
||||
|
@ -140,8 +140,8 @@
|
|||
data-testid="favorites">
|
||||
{#each currentFavorites as emoji, i (emoji.unicode)}
|
||||
<button role="menuitem"
|
||||
aria-label={emojiLabel(emoji)}
|
||||
title={emojiTitle(emoji)}
|
||||
aria-label={emoji.label}
|
||||
title={emoji.title}
|
||||
class="emoji"
|
||||
data-emoji={emoji.unicode}>
|
||||
{unicodeWithSkin(emoji, currentSkinTone)}
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
} from '../../constants'
|
||||
import { uniqBy } from '../../../shared/uniqBy'
|
||||
import { mergeI18n } from '../../utils/mergeI18n'
|
||||
import { summarizeEmojisForUI } from '../../utils/summarizeEmojisForUI'
|
||||
|
||||
let skinToneEmoji = DEFAULT_SKIN_TONE_EMOJI
|
||||
let i18n = enI18n
|
||||
|
@ -56,6 +57,7 @@ let defaultFavoriteEmojisPromise
|
|||
let numColumns = DEFAULT_NUM_COLUMNS
|
||||
let scrollbarWidth = 0 // eslint-disable-line no-unused-vars
|
||||
let shouldUpdateFavorites = {} // hack to force svelte to recalc favorites
|
||||
let customEmoji = []
|
||||
|
||||
const getBaselineEmojiWidth = thunk(() => calculateTextWidth(baselineEmoji))
|
||||
|
||||
|
@ -98,6 +100,12 @@ Promise.resolve().then(() => {
|
|||
}
|
||||
})
|
||||
|
||||
$: {
|
||||
if (customEmoji && database) {
|
||||
database.customEmoji = customEmoji
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if (i18n !== enI18n) {
|
||||
i18n = mergeI18n(enI18n, i18n) // if partial translations are provided, merge with English
|
||||
|
@ -293,26 +301,7 @@ async function filterEmojisByVersion (emojis) {
|
|||
}
|
||||
|
||||
async function summarizeEmojis (emojis) {
|
||||
const emojiSupportLevel = await emojiSupportLevelPromise
|
||||
// We don't need all the data on every emoji, so we can conserve memory by removing it
|
||||
// Also we can simplify the way we access the "skins" object
|
||||
const toSimpleSkinsMap = skins => {
|
||||
const res = {}
|
||||
for (const skin of skins) {
|
||||
// ignore arrays like [1, 2] with multiple skin tones
|
||||
// also ignore variants that are in an unsupported emoji version
|
||||
// (these do exist - variants from a different version than their base emoji)
|
||||
if (typeof skin.tone === 'number' && skin.version <= emojiSupportLevel) {
|
||||
res[skin.tone] = skin.unicode
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
return emojis.map(({ unicode, skins, shortcodes }) => ({
|
||||
unicode,
|
||||
skins: skins && toSimpleSkinsMap(skins),
|
||||
shortcodes
|
||||
}))
|
||||
return summarizeEmojisForUI(emojis, await emojiSupportLevelPromise)
|
||||
}
|
||||
|
||||
async function getEmojisByGroup (group) {
|
||||
|
@ -480,18 +469,12 @@ async function onSkinToneOptionsBlur () {
|
|||
skinTonePickerExpanded = false
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function emojiLabel (emoji) {
|
||||
return emoji.unicode + ', ' + emoji.shortcodes.join(', ')
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function emojiTitle (emoji) {
|
||||
return emoji.shortcodes.join(', ')
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function unicodeWithSkin (emoji, currentSkinTone) {
|
||||
if (!emoji.unicode) { // custom emoji
|
||||
return ''
|
||||
}
|
||||
if (currentSkinTone && emoji.skins && emoji.skins[currentSkinTone]) {
|
||||
return emoji.skins[currentSkinTone]
|
||||
}
|
||||
|
@ -501,5 +484,6 @@ function unicodeWithSkin (emoji, currentSkinTone) {
|
|||
export {
|
||||
database,
|
||||
i18n,
|
||||
skinToneEmoji
|
||||
skinToneEmoji,
|
||||
customEmoji
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// We don't need all the data on every emoji, and there are specific things we need
|
||||
// for the UI, so build a "view model" from the emoji object we got from the database
|
||||
export function summarizeEmojisForUI (emojis, emojiSupportLevel) {
|
||||
const toSimpleSkinsMap = skins => {
|
||||
const res = {}
|
||||
for (const skin of skins) {
|
||||
// ignore arrays like [1, 2] with multiple skin tones
|
||||
// also ignore variants that are in an unsupported emoji version
|
||||
// (these do exist - variants from a different version than their base emoji)
|
||||
if (typeof skin.tone === 'number' && skin.version <= emojiSupportLevel) {
|
||||
res[skin.tone] = skin.unicode
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
return emojis.map(({ unicode, skins, shortcodes, url }) => ({
|
||||
unicode,
|
||||
skins: skins && toSimpleSkinsMap(skins),
|
||||
shortcodes,
|
||||
url,
|
||||
label: (unicode ? (unicode + ', ') : '') + shortcodes.join(', '),
|
||||
title: shortcodes.join(', ')
|
||||
}))
|
||||
}
|
|
@ -93,11 +93,11 @@ export default class Database {
|
|||
/**
|
||||
* Increment the favorite count for an emoji by one. The unicode string must be non-empty. It should
|
||||
* correspond to the base (non-skin-tone) unicode string from the emoji object, or in the case of
|
||||
* custom emoji, it should be the shortcode.
|
||||
* custom emoji, it should be the name.
|
||||
*
|
||||
* @param unicodeOrShortcode - unicode of the native emoji, or shortcode of a custom emoji
|
||||
* @param unicodeOrName - unicode of a native emoji, or name of a custom emoji
|
||||
*/
|
||||
incrementFavoriteEmojiCount (unicodeOrShortcode: string): Promise<void> {
|
||||
incrementFavoriteEmojiCount (unicodeOrName: string): Promise<void> {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
|
|
|
@ -90,12 +90,13 @@ export type SkinToneChangeEvent = Modify<UIEvent, {
|
|||
}>
|
||||
|
||||
export interface EmojiPickerEventMap {
|
||||
"emoji-click": EmojiClickEvent;
|
||||
"skin-tone-change": SkinToneChangeEvent;
|
||||
"emoji-click": EmojiClickEvent
|
||||
"skin-tone-change": SkinToneChangeEvent
|
||||
}
|
||||
|
||||
export interface CustomEmoji {
|
||||
shortcode: string,
|
||||
name: string
|
||||
shortcodes: string[]
|
||||
url: string
|
||||
}
|
||||
|
||||
|
|
|
@ -3,44 +3,55 @@ import Database from '../../../src/database/Database'
|
|||
|
||||
const customEmojis = [
|
||||
{
|
||||
shortcode: 'CapitalLettersLikeThis',
|
||||
name: 'CapitalLettersLikeThis',
|
||||
shortcodes: ['CapitalLettersLikeThis'],
|
||||
url: 'caps.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'underscores_like_this',
|
||||
name: 'underscores_like_this',
|
||||
shortcodes: ['underscores_like_this'],
|
||||
url: 'underscores.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'a',
|
||||
name: 'a',
|
||||
shortcodes: ['a'],
|
||||
url: 'a.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'z',
|
||||
name: 'z',
|
||||
shortcodes: ['z'],
|
||||
url: 'z.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'monkey', // conflicts with native emoji
|
||||
name: 'monkey',
|
||||
shortcodes: ['monkey'], // conflicts with native emoji
|
||||
url: 'monkey.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'multiple_results_match',
|
||||
name: 'multiple_results_match',
|
||||
shortcodes: ['multiple_results_match'],
|
||||
url: 'multiple1.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'multiple_results_match_too',
|
||||
name: 'multiple_results_match_too',
|
||||
shortcodes: ['multiple_results_match_too'],
|
||||
url: 'multiple2.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'multiple_results_match_again',
|
||||
name: 'multiple_results_match_again',
|
||||
shortcodes: ['multiple_results_match_again'],
|
||||
url: 'multiple3.png'
|
||||
}
|
||||
]
|
||||
|
||||
const summarize = ({ unicode, shortcode, url }) => {
|
||||
const res = { shortcode, url }
|
||||
const summarize = ({ unicode, shortcodes, url, name }) => {
|
||||
const res = { shortcodes, url }
|
||||
if (unicode) {
|
||||
res.unicode = unicode
|
||||
}
|
||||
if (name) {
|
||||
res.name = name
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
@ -48,7 +59,7 @@ const summaryByUnicode = unicode => {
|
|||
const emoji = truncatedEmoji.find(_ => _.emoji === unicode)
|
||||
return summarize({
|
||||
unicode: emoji.emoji,
|
||||
shortcode: emoji.shortcode
|
||||
shortcodes: emoji.shortcodes
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -77,17 +88,17 @@ describe('custom emoji', () => {
|
|||
db.customEmoji = customEmojis
|
||||
expect(db.customEmoji).toStrictEqual(customEmojis)
|
||||
expect(await db.getEmojiByShortcode('capitalletterslikethis')).toStrictEqual(
|
||||
{ shortcode: 'CapitalLettersLikeThis', url: 'caps.png' }
|
||||
{ name: 'CapitalLettersLikeThis', shortcodes: ['CapitalLettersLikeThis'], url: 'caps.png' }
|
||||
)
|
||||
expect(await db.getEmojiByShortcode('underscores_like_this')).toStrictEqual(
|
||||
{ shortcode: 'underscores_like_this', url: 'underscores.png' }
|
||||
{ name: 'underscores_like_this', shortcodes: ['underscores_like_this'], url: 'underscores.png' }
|
||||
)
|
||||
expect(await db.getEmojiByShortcode('Underscores_Like_This')).toStrictEqual(
|
||||
{ shortcode: 'underscores_like_this', url: 'underscores.png' }
|
||||
{ name: 'underscores_like_this', shortcodes: ['underscores_like_this'], url: 'underscores.png' }
|
||||
)
|
||||
// custom emoji take precedence over native in case of conflict
|
||||
expect(await db.getEmojiByShortcode('monkey')).toStrictEqual(
|
||||
{ shortcode: 'monkey', url: 'monkey.png' }
|
||||
{ name: 'monkey', shortcodes: ['monkey'], url: 'monkey.png' }
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -95,47 +106,115 @@ describe('custom emoji', () => {
|
|||
db.customEmoji = customEmojis
|
||||
|
||||
expect((await db.getEmojiBySearchQuery('monkey')).map(summarize)).toStrictEqual([
|
||||
{ shortcode: 'monkey', url: 'monkey.png' },
|
||||
{ name: 'monkey', shortcodes: ['monkey'], url: 'monkey.png' },
|
||||
summaryByUnicode('🐵'),
|
||||
summaryByUnicode('🐒')
|
||||
])
|
||||
|
||||
expect((await db.getEmojiBySearchQuery('undersc'))).toStrictEqual([
|
||||
{ shortcode: 'underscores_like_this', url: 'underscores.png' }
|
||||
{ name: 'underscores_like_this', shortcodes: ['underscores_like_this'], url: 'underscores.png' }
|
||||
])
|
||||
|
||||
expect((await db.getEmojiBySearchQuery('underscores lik'))).toStrictEqual([
|
||||
{ shortcode: 'underscores_like_this', url: 'underscores.png' }
|
||||
{ name: 'underscores_like_this', shortcodes: ['underscores_like_this'], url: 'underscores.png' }
|
||||
])
|
||||
|
||||
expect((await db.getEmojiBySearchQuery('undersc like'))).toStrictEqual([
|
||||
])
|
||||
expect((await db.getEmojiBySearchQuery('undersc lik'))).toStrictEqual([
|
||||
])
|
||||
expect((await db.getEmojiBySearchQuery('undersc like'))).toStrictEqual([])
|
||||
expect((await db.getEmojiBySearchQuery('undersc lik'))).toStrictEqual([])
|
||||
|
||||
expect((await db.getEmojiBySearchQuery('underscores like'))).toStrictEqual([
|
||||
{ shortcode: 'underscores_like_this', url: 'underscores.png' }
|
||||
{ name: 'underscores_like_this', shortcodes: ['underscores_like_this'], url: 'underscores.png' }
|
||||
])
|
||||
expect((await db.getEmojiBySearchQuery('underscores like th'))).toStrictEqual([
|
||||
{ shortcode: 'underscores_like_this', url: 'underscores.png' }
|
||||
{ name: 'underscores_like_this', shortcodes: ['underscores_like_this'], url: 'underscores.png' }
|
||||
])
|
||||
expect((await db.getEmojiBySearchQuery('underscores like this'))).toStrictEqual([
|
||||
{ shortcode: 'underscores_like_this', url: 'underscores.png' }
|
||||
{ name: 'underscores_like_this', shortcodes: ['underscores_like_this'], url: 'underscores.png' }
|
||||
])
|
||||
|
||||
expect((await db.getEmojiBySearchQuery('multiple match'))).toStrictEqual([
|
||||
{
|
||||
shortcode: 'multiple_results_match',
|
||||
name: 'multiple_results_match',
|
||||
shortcodes: ['multiple_results_match'],
|
||||
url: 'multiple1.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'multiple_results_match_again',
|
||||
name: 'multiple_results_match_again',
|
||||
shortcodes: ['multiple_results_match_again'],
|
||||
url: 'multiple3.png'
|
||||
},
|
||||
{
|
||||
shortcode: 'multiple_results_match_too',
|
||||
name: 'multiple_results_match_too',
|
||||
shortcodes: ['multiple_results_match_too'],
|
||||
url: 'multiple2.png'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
test('increment favorites with custom', async () => {
|
||||
db.customEmoji = customEmojis
|
||||
|
||||
const counts = {
|
||||
'🐵': 5,
|
||||
'🐒': 4,
|
||||
'🤣': 2,
|
||||
'🖐️': 1,
|
||||
'🤏': 6,
|
||||
'✌️': 8,
|
||||
'🐶': 9,
|
||||
'🎉': 3,
|
||||
'✨': 3,
|
||||
CapitalLettersLikeThis: 3,
|
||||
underscores_like_this: 6,
|
||||
monkey: 5
|
||||
}
|
||||
|
||||
for (const [unicodeOrShortcode, count] of Object.entries(counts)) {
|
||||
for (let i = 0; i < count; i++) {
|
||||
await db.incrementFavoriteEmojiCount(unicodeOrShortcode)
|
||||
}
|
||||
}
|
||||
|
||||
expect((await db.getTopFavoriteEmoji(10)).map(summarize)).toStrictEqual([
|
||||
summaryByUnicode('🐶'),
|
||||
summaryByUnicode('✌️'),
|
||||
summaryByUnicode('🤏'),
|
||||
{ name: 'underscores_like_this', shortcodes: ['underscores_like_this'], url: 'underscores.png' },
|
||||
summaryByUnicode('🐵'),
|
||||
{ name: 'monkey', shortcodes: ['monkey'], url: 'monkey.png' },
|
||||
summaryByUnicode('🐒'),
|
||||
summaryByUnicode('🎉'),
|
||||
summaryByUnicode('✨'),
|
||||
{ name: 'CapitalLettersLikeThis', shortcodes: ['CapitalLettersLikeThis'], url: 'caps.png' }
|
||||
])
|
||||
|
||||
expect((await db.getTopFavoriteEmoji(20)).map(summarize)).toStrictEqual([
|
||||
summaryByUnicode('🐶'),
|
||||
summaryByUnicode('✌️'),
|
||||
summaryByUnicode('🤏'),
|
||||
{ name: 'underscores_like_this', shortcodes: ['underscores_like_this'], url: 'underscores.png' },
|
||||
summaryByUnicode('🐵'),
|
||||
{ name: 'monkey', shortcodes: ['monkey'], url: 'monkey.png' },
|
||||
summaryByUnicode('🐒'),
|
||||
summaryByUnicode('🎉'),
|
||||
summaryByUnicode('✨'),
|
||||
{ name: 'CapitalLettersLikeThis', shortcodes: ['CapitalLettersLikeThis'], url: 'caps.png' },
|
||||
summaryByUnicode('🤣'),
|
||||
summaryByUnicode('🖐️')
|
||||
])
|
||||
|
||||
db.customEmoji = []
|
||||
// favorites are now in the database but missing from the in-memory index, so we should just skip them
|
||||
expect((await db.getTopFavoriteEmoji(10)).map(summarize)).toStrictEqual([
|
||||
summaryByUnicode('🐶'),
|
||||
summaryByUnicode('✌️'),
|
||||
summaryByUnicode('🤏'),
|
||||
summaryByUnicode('🐵'),
|
||||
summaryByUnicode('🐒'),
|
||||
summaryByUnicode('🎉'),
|
||||
summaryByUnicode('✨'),
|
||||
summaryByUnicode('🤣'),
|
||||
summaryByUnicode('🖐️')
|
||||
])
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue