|
|
|
|
/*
|
|
|
|
|
* https://github.com/morethanwords/tweb
|
|
|
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
|
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import App from "../../config/app";
|
|
|
|
|
import { MOUNT_CLASS_TO } from "../../config/debug";
|
|
|
|
|
import { validateInitObject } from "../../helpers/object";
|
|
|
|
|
import I18n from "../langPack";
|
|
|
|
|
import { isObject } from "../mtproto/bin_utils";
|
|
|
|
|
import apiManager from "../mtproto/mtprotoworker";
|
|
|
|
|
import SearchIndex from "../searchIndex";
|
|
|
|
|
import stateStorage from "../stateStorage";
|
|
|
|
|
import appStateManager from "./appStateManager";
|
|
|
|
|
|
|
|
|
|
type EmojiLangPack = {
|
|
|
|
|
keywords: {
|
|
|
|
|
[keyword: string]: string[],
|
|
|
|
|
},
|
|
|
|
|
version: number,
|
|
|
|
|
langCode: string
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const EMOJI_LANG_PACK: EmojiLangPack = {
|
|
|
|
|
keywords: {},
|
|
|
|
|
version: 0,
|
|
|
|
|
langCode: App.langPackCode
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const RECENT_MAX_LENGTH = 36;
|
|
|
|
|
|
|
|
|
|
export class AppEmojiManager {
|
|
|
|
|
private static POPULAR_EMOJI = ["π", "π", "β€οΈ", "π", "π", "π", "π", "βΊοΈ", "π", "π", "π", "π", "π", "π³", "π", "π", "π", "π", "π’", "π", "π±", "π‘", "π", "π", "π
", "π", "π", "π", "π", "π", "π", "π", "π", "π"];
|
|
|
|
|
private keywordLangPacks: {
|
|
|
|
|
[langCode: string]: EmojiLangPack
|
|
|
|
|
} = {};
|
|
|
|
|
|
|
|
|
|
private index: SearchIndex<EmojiLangPack['keywords'][keyof EmojiLangPack['keywords']]>;
|
|
|
|
|
private indexedLangPacks: {[langCode: string]: boolean} = {};
|
|
|
|
|
|
|
|
|
|
private getKeywordsPromises: {[langCode: string]: Promise<EmojiLangPack>} = {};
|
|
|
|
|
|
|
|
|
|
private recent: string[];
|
|
|
|
|
private getRecentEmojisPromise: Promise<AppEmojiManager['recent']>;
|
|
|
|
|
|
|
|
|
|
/* public getPopularEmoji() {
|
|
|
|
|
return stateStorage.get('emojis_popular').then(popEmojis => {
|
|
|
|
|
var result = []
|
|
|
|
|
if (popEmojis && popEmojis.length) {
|
|
|
|
|
for (var i = 0, len = popEmojis.length; i < len; i++) {
|
|
|
|
|
result.push({code: popEmojis[i][0], rate: popEmojis[i][1]})
|
|
|
|
|
}
|
|
|
|
|
callback(result)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return stateStorage.get('emojis_recent').then(recentEmojis => {
|
|
|
|
|
recentEmojis = recentEmojis || popular || []
|
|
|
|
|
var shortcut
|
|
|
|
|
var code
|
|
|
|
|
for (var i = 0, len = recentEmojis.length; i < len; i++) {
|
|
|
|
|
shortcut = recentEmojis[i]
|
|
|
|
|
if (Array.isArray(shortcut)) {
|
|
|
|
|
shortcut = shortcut[0]
|
|
|
|
|
}
|
|
|
|
|
if (shortcut && typeof shortcut === 'string') {
|
|
|
|
|
if (shortcut.charAt(0) == ':') {
|
|
|
|
|
shortcut = shortcut.substr(1, shortcut.length - 2)
|
|
|
|
|
}
|
|
|
|
|
if (code = shortcuts[shortcut]) {
|
|
|
|
|
result.push({code: code, rate: 1})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
callback(result)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function pushPopularEmoji (code) {
|
|
|
|
|
getPopularEmoji(function (popularEmoji) {
|
|
|
|
|
var exists = false
|
|
|
|
|
var count = popularEmoji.length
|
|
|
|
|
var result = []
|
|
|
|
|
for (var i = 0; i < count; i++) {
|
|
|
|
|
if (popularEmoji[i].code == code) {
|
|
|
|
|
exists = true
|
|
|
|
|
popularEmoji[i].rate++
|
|
|
|
|
}
|
|
|
|
|
result.push([popularEmoji[i].code, popularEmoji[i].rate])
|
|
|
|
|
}
|
|
|
|
|
if (exists) {
|
|
|
|
|
result.sort(function (a, b) {
|
|
|
|
|
return b[1] - a[1]
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
if (result.length > 41) {
|
|
|
|
|
result = result.slice(0, 41)
|
|
|
|
|
}
|
|
|
|
|
result.push([code, 1])
|
|
|
|
|
}
|
|
|
|
|
ConfigStorage.set({emojis_popular: result})
|
|
|
|
|
})
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
public getEmojiKeywords(langCode: string = App.langPackCode) {
|
|
|
|
|
const promise = this.getKeywordsPromises[langCode];
|
|
|
|
|
if(promise) {
|
|
|
|
|
return promise;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const storageKey: any = 'emojiKeywords_' + langCode;
|
|
|
|
|
return this.getKeywordsPromises[langCode] = stateStorage.get(storageKey).then((pack: EmojiLangPack) => {
|
|
|
|
|
if(!isObject(pack)) {
|
|
|
|
|
pack = {} as any;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validateInitObject(EMOJI_LANG_PACK, pack);
|
|
|
|
|
|
|
|
|
|
// important
|
|
|
|
|
pack.langCode = langCode;
|
|
|
|
|
this.keywordLangPacks[langCode] = pack;
|
|
|
|
|
|
|
|
|
|
return apiManager.invokeApi('messages.getEmojiKeywordsDifference', {
|
|
|
|
|
lang_code: pack.langCode,
|
|
|
|
|
from_version: pack.version
|
|
|
|
|
}).then((keywordsDifference) => {
|
|
|
|
|
pack.version = keywordsDifference.version;
|
|
|
|
|
|
|
|
|
|
const packKeywords = pack.keywords;
|
|
|
|
|
const keywords = keywordsDifference.keywords;
|
|
|
|
|
for(let i = 0, length = keywords.length; i < length; ++i) {
|
|
|
|
|
const {keyword, emoticons} = keywords[i];
|
|
|
|
|
packKeywords[keyword] = emoticons;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stateStorage.set({
|
|
|
|
|
[storageKey]: pack
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return pack;
|
|
|
|
|
}, () => {
|
|
|
|
|
return pack;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getBothEmojiKeywords() {
|
|
|
|
|
const promises: Promise<any>[] = [
|
|
|
|
|
this.getEmojiKeywords()
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if(I18n.lastRequestedLangCode !== App.langPackCode) {
|
|
|
|
|
promises.push(this.getEmojiKeywords(I18n.lastRequestedLangCode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!this.recent) {
|
|
|
|
|
promises.push(this.getRecentEmojis());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Promise.all(promises);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public indexEmojis() {
|
|
|
|
|
if(!this.index) {
|
|
|
|
|
this.index = new SearchIndex(false, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(const langCode in this.keywordLangPacks) {
|
|
|
|
|
if(this.indexedLangPacks[langCode]) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const pack = this.keywordLangPacks[langCode];
|
|
|
|
|
const keywords = pack.keywords;
|
|
|
|
|
|
|
|
|
|
for(const keyword in keywords) {
|
|
|
|
|
const emoticons = keywords[keyword];
|
|
|
|
|
this.index.indexObject(emoticons, keyword);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.indexedLangPacks[langCode] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public searchEmojis(q: string) {
|
|
|
|
|
this.indexEmojis();
|
|
|
|
|
|
|
|
|
|
q = q.toLowerCase().replace(/_/g, ' ');
|
|
|
|
|
|
|
|
|
|
//const perf = performance.now();
|
|
|
|
|
let emojis: Array<string>;
|
|
|
|
|
if(q.trim()) {
|
|
|
|
|
const set = this.index.search(q);
|
|
|
|
|
emojis = Array.from(set).reduce((acc, v) => acc.concat(v), []);
|
|
|
|
|
} else {
|
|
|
|
|
emojis = this.recent.concat(AppEmojiManager.POPULAR_EMOJI).slice(0, RECENT_MAX_LENGTH);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emojis = Array.from(new Set(emojis));
|
|
|
|
|
//console.log('searchEmojis', q, 'time', performance.now() - perf);
|
|
|
|
|
|
|
|
|
|
/* for(let i = 0, length = emojis.length; i < length; ++i) {
|
|
|
|
|
if(emojis[i].includes(zeroWidthJoiner) && !emojis[i].includes('\ufe0f')) {
|
|
|
|
|
emojis[i] += '\ufe0f';
|
|
|
|
|
}
|
|
|
|
|
} */
|
|
|
|
|
|
|
|
|
|
return emojis;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getRecentEmojis() {
|
|
|
|
|
if(this.getRecentEmojisPromise) return this.getRecentEmojisPromise;
|
|
|
|
|
return this.getRecentEmojisPromise = appStateManager.getState().then(state => {
|
|
|
|
|
return this.recent = Array.isArray(state.recentEmoji) ? state.recentEmoji : [];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public pushRecentEmoji(emoji: string) {
|
|
|
|
|
this.getRecentEmojis().then(recent => {
|
|
|
|
|
recent.findAndSplice(e => e === emoji);
|
|
|
|
|
recent.unshift(emoji);
|
|
|
|
|
if(recent.length > RECENT_MAX_LENGTH) {
|
|
|
|
|
recent.length = RECENT_MAX_LENGTH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appStateManager.pushToState('recentEmoji', recent);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const appEmojiManager = new AppEmojiManager();
|
|
|
|
|
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appEmojiManager = appEmojiManager);
|
|
|
|
|
export default appEmojiManager;
|