tweb/src/components/emoticonsDropdown/tabs/emoji.ts

212 lines
6.6 KiB
TypeScript

import { EmoticonsTab, EmoticonsDropdown } from "..";
import Scrollable from "../../scrollable_new";
import Config from "../../../lib/config";
import { putPreloader } from "../../misc";
import appStateManager from "../../../lib/appManagers/appStateManager";
import { RichTextProcessor } from "../../../lib/richtextprocessor";
import appImManager from "../../../lib/appManagers/appImManager";
import StickyIntersector from "../../stickyIntersector";
export default class EmojiTab implements EmoticonsTab {
public content: HTMLElement;
private recent: string[] = [];
private recentItemsDiv: HTMLElement;
private scroll: Scrollable;
private stickyIntersector: StickyIntersector;
init() {
this.content = document.getElementById('content-emoji') as HTMLDivElement;
const categories = ["Smileys & Emotion", "Animals & Nature", "Food & Drink", "Travel & Places", "Activities", "Objects", /* "Symbols", */"Flags", "Skin Tones"];
const divs: {
[category: string]: HTMLDivElement
} = {};
const sorted: {
[category: string]: string[]
} = {
'Recent': []
};
for(const emoji in Config.Emoji) {
const details = Config.Emoji[emoji];
const i = '' + details;
const category = categories[+i[0] - 1];
if(!category) continue; // maybe it's skin tones
if(!sorted[category]) sorted[category] = [];
sorted[category][+i.slice(1) || 0] = emoji;
}
//console.log('emoticons sorted:', sorted);
//Object.keys(sorted).forEach(c => sorted[c].sort((a, b) => a - b));
categories.pop();
delete sorted["Skin Tones"];
//console.time('emojiParse');
for(const category in sorted) {
const div = document.createElement('div');
div.classList.add('emoji-category');
const titleDiv = document.createElement('div');
titleDiv.classList.add('category-title');
titleDiv.innerText = category;
const itemsDiv = document.createElement('div');
itemsDiv.classList.add('category-items');
div.append(titleDiv, itemsDiv);
const emojis = sorted[category];
emojis.forEach(emoji => {
/* if(emojiUnicode(emoji) == '1f481-200d-2642') {
console.log('append emoji', emoji, emojiUnicode(emoji));
} */
emoji = emoji.split('-').reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), '');
this.appendEmoji(emoji/* .replace(/[\ufe0f\u2640\u2642\u2695]/g, '') */, itemsDiv, false/* , false */);
/* if(category == 'Smileys & Emotion') {
console.log('appended emoji', emoji, itemsDiv.children[itemsDiv.childElementCount - 1].innerHTML, emojiUnicode(emoji));
} */
});
divs[category] = div;
}
//console.timeEnd('emojiParse');
const menu = this.content.previousElementSibling.firstElementChild as HTMLUListElement;
const emojiScroll = this.scroll = new Scrollable(this.content, 'EMOJI', null);
//emojiScroll.setVirtualContainer(emojiScroll.container);
const preloader = putPreloader(this.content, true);
Promise.all([
new Promise((resolve) => setTimeout(resolve, 200)),
appStateManager.getState().then(state => {
if(Array.isArray(state.recentEmoji)) {
this.recent = state.recentEmoji;
}
})
]).then(() => {
preloader.remove();
this.recentItemsDiv = divs['Recent'].querySelector('.category-items');
for(const emoji of this.recent) {
this.appendEmoji(emoji, this.recentItemsDiv);
}
categories.unshift('Recent');
categories.map(category => {
const div = divs[category];
if(!div) {
console.error('no div by category:', category);
}
emojiScroll.append(div);
this.stickyIntersector.observeStickyHeaderChanges(div);
return div;
});
});
this.content.addEventListener('click', this.onContentClick);
this.stickyIntersector = EmoticonsDropdown.menuOnClick(menu, emojiScroll);
this.init = null;
}
private appendEmoji(emoji: string, container: HTMLElement, prepend = false/* , unified = false */) {
//const emoji = details.unified;
//const emoji = (details.unified as string).split('-')
//.reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), '');
const spanEmoji = document.createElement('span');
let kek: string;
/* if(unified) {
kek = RichTextProcessor.wrapRichText('_', {
entities: [{
_: 'messageEntityEmoji',
offset: 0,
length: emoji.split('-').length,
unicode: emoji
}]
});
} else { */
kek = RichTextProcessor.wrapEmojiText(emoji);
//}
/* if(!kek.includes('emoji')) {
console.log(emoji, kek, spanEmoji, emoji.length, new TextEncoder().encode(emoji), emojiUnicode(emoji));
return;
} */
//console.log(kek);
spanEmoji.innerHTML = kek;
if(spanEmoji.firstElementChild) {
(spanEmoji.firstElementChild as HTMLImageElement).setAttribute('loading', 'lazy');
}
//spanEmoji = spanEmoji.firstElementChild as HTMLSpanElement;
//spanEmoji.setAttribute('emoji', emoji);
if(prepend) container.prepend(spanEmoji);
else container.appendChild(spanEmoji);
}
private getEmojiFromElement(element: HTMLElement) {
if(element.tagName == 'SPAN' && !element.classList.contains('emoji')) {
element = element.firstElementChild as HTMLElement;
}
return element.getAttribute('alt') || element.innerText;
}
onContentClick = (e: MouseEvent) => {
let target = e.target as HTMLElement;
//if(target.tagName != 'SPAN') return;
if(target.tagName == 'SPAN' && !target.classList.contains('emoji')) {
target = target.firstElementChild as HTMLElement;
} else if(target.tagName == 'DIV') return;
//console.log('contentEmoji div', target);
appImManager.chatInputC.messageInput.innerHTML += target.outerHTML;
// Recent
const emoji = this.getEmojiFromElement(target);
(Array.from(this.recentItemsDiv.children) as HTMLElement[]).forEach((el, idx) => {
const _emoji = this.getEmojiFromElement(el);
if(emoji == _emoji) {
el.remove();
}
});
const scrollHeight = this.recentItemsDiv.scrollHeight;
this.appendEmoji(emoji, this.recentItemsDiv, true);
this.recent.findAndSplice(e => e == emoji);
this.recent.unshift(emoji);
if(this.recent.length > 36) {
this.recent.length = 36;
}
appStateManager.pushToState('recentEmoji', this.recent);
// Append to input
const event = new Event('input', {bubbles: true, cancelable: true});
appImManager.chatInputC.messageInput.dispatchEvent(event);
};
onClose() {
}
}