import { AppImManager } from "../lib/appManagers/appImManager"; import { AppMessagesManager } from "../lib/appManagers/appMessagesManager"; import { horizontalMenu } from "./misc"; import lottieLoader from "../lib/lottieLoader"; import Scrollable from "./scrollable"; import { findUpTag, whichChild, calcImageInBox } from "../lib/utils"; import { RichTextProcessor } from "../lib/richtextprocessor"; import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickersManager"; //import apiManager from '../lib/mtproto/apiManager'; import apiManager from '../lib/mtproto/mtprotoworker'; //import CryptoWorker from '../lib/crypto/cryptoworker'; import LazyLoadQueue from "./lazyLoadQueue"; import { MTDocument, wrapSticker } from "./wrappers"; import appWebpManager from "../lib/appManagers/appWebpManager"; import appDocsManager from "../lib/appManagers/appDocsManager"; import ProgressivePreloader from "./preloader"; import Config from "../lib/config"; export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; const initEmoticonsDropdown = (pageEl: HTMLDivElement, appImManager: AppImManager, appMessagesManager: AppMessagesManager, messageInput: HTMLDivElement, toggleEl: HTMLButtonElement, btnSend: HTMLButtonElement) => { let dropdown = pageEl.querySelector('.emoji-dropdown') as HTMLDivElement; dropdown.classList.add('active'); // need let lazyLoadQueue = new LazyLoadQueue(5); let container = pageEl.querySelector('.emoji-container .tabs-container') as HTMLDivElement; let tabs = pageEl.querySelector('.emoji-dropdown .emoji-tabs') as HTMLUListElement; horizontalMenu(tabs, container, (id) => { lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP); if(id == 1 && stickersInit) { stickersInit(); } else if(id == 2 && gifsInit) { gifsInit(); } }, () => { lottieLoader.checkAnimations(false, EMOTICONSSTICKERGROUP); lazyLoadQueue.check(); // for stickers or gifs }); (tabs.firstElementChild.children[0] as HTMLLIElement).click(); // set emoji tab (tabs.lastElementChild as HTMLSpanElement).style.cssText = 'width: 44.1719px; transform: translateX(88.5781px);'; // мы снова встретились)))))) let emoticonsMenuOnClick = (menu: HTMLUListElement, heights: number[], scroll: Scrollable, menuScroll?: Scrollable) => { menu.addEventListener('click', function(e) { let target = e.target as HTMLLIElement; target = findUpTag(target, 'LI'); let index = whichChild(target); let y = heights[index - 1/* 2 */] || 0; // 10 == padding .scrollable /* if(menuScroll) { menuScroll.container.scrollLeft = target.scrollWidth * index; } console.log('emoticonsMenuOnClick', menu.getBoundingClientRect(), target.getBoundingClientRect()); */ /* scroll.onAddedBottom = () => { // привет, костыль, давно не виделись! scroll.container.scrollTop = y; scroll.onAddedBottom = () => {}; }; */ scroll.container.scrollTop = y; }); }; let emoticonsContentOnScroll = (menu: HTMLUListElement, heights: number[], prevCategoryIndex: number, scroll: HTMLDivElement, menuScroll?: Scrollable) => { let y = scroll.scrollTop; //console.log(heights, y); for(let i = 0; i < heights.length; ++i) { let height = heights[i]; if(y < height) { menu.children[prevCategoryIndex].classList.remove('active'); prevCategoryIndex = i/* + 1 */; menu.children[prevCategoryIndex].classList.add('active'); if(menuScroll) { if(i < heights.length - 4) { menuScroll.container.scrollLeft = (i - 3) * 47; } else { menuScroll.container.scrollLeft = i * 47; } } break; } } return prevCategoryIndex; }; { let categories = ["Smileys & Emotion", "Animals & Nature", "Food & Drink", "Travel & Places", "Activities", "Objects", /* "Symbols", */"Flags", "Skin Tones"]; let divs: { [category: string]: HTMLDivElement } = {}; let keyCategory = Config.Emoji.keyCategory; let sorted: { [category: string]: any[] } = {}; for(let unified in Config.Emoji.emoji) { // @ts-ignore let details = Config.Emoji.emoji[unified]; let category = details[keyCategory]; if(category == 'Symbols') category = 'Objects'; details.unified = unified; if(!sorted[category]) sorted[category] = []; sorted[category][details.sort_order] = details; } //console.log('emoticons sorted:', sorted); Object.keys(sorted).forEach(c => sorted[c].sort()); categories.pop(); delete sorted["Skin Tones"]; //console.time('emojiParse'); for(let category in sorted) { let div = document.createElement('div'); div.classList.add('emoji-category'); let emojis = sorted[category]; emojis.forEach(details => { let emoji = details.unified; //let emoji = (details.unified as string).split('-') //.reduce((prev, curr) => prev + String.fromCodePoint(parseInt(curr, 16)), ''); let spanEmoji = document.createElement('span'); let kek = RichTextProcessor.wrapRichText(emoji); if(!kek.includes('emoji')) { console.log(details, emoji, kek, spanEmoji, emoji.length, new TextEncoder().encode(emoji)); return; } //console.log(kek); spanEmoji.innerHTML = kek; //spanEmoji = spanEmoji.firstElementChild as HTMLSpanElement; //spanEmoji.setAttribute('emoji', emoji); div.appendChild(spanEmoji); }); divs[category] = div; } //console.timeEnd('emojiParse'); let contentEmojiDiv = document.getElementById('content-emoji') as HTMLDivElement; let heights: number[] = [0]; let prevCategoryIndex = 1; let menu = contentEmojiDiv.nextElementSibling.firstElementChild as HTMLUListElement; let emojiScroll = new Scrollable(contentEmojiDiv, 'y', 500, 'EMOJI', null); emojiScroll.container.addEventListener('scroll', (e) => { prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, emojiScroll.container); }); //emojiScroll.setVirtualContainer(emojiScroll.container); categories.map(category => { let div = divs[category]; if(!div) { console.error('no div by category:', category); } emojiScroll.append(div); return div; }).forEach(div => { //console.log('emoji heights push: ', (heights[heights.length - 1] || 0) + div.scrollHeight, div, div.scrollHeight); heights.push((heights[heights.length - 1] || 0) + div.scrollHeight); }); contentEmojiDiv.addEventListener('click', function(e) { let target = e.target as any; //if(target.tagName != 'SPAN') return; if(target.tagName == 'SPAN' && !target.classList.contains('emoji')) { target = target.firstElementChild; } else if(target.tagName == 'DIV') return; //console.log('contentEmoji div', target); /* if(!target.classList.contains('emoji')) { target = target.parentElement as HTMLSpanElement; if(!target.classList.contains('emoji')) { return; } } */ //messageInput.innerHTML += target.innerHTML; messageInput.innerHTML += target.outerHTML; btnSend.classList.add('tgico-send'); btnSend.classList.remove('tgico-microphone2'); }); emoticonsMenuOnClick(menu, heights, emojiScroll); } let onMediaClick = (e: MouseEvent) => { let target = e.target as HTMLDivElement; target = findUpTag(target, 'DIV'); let fileID = target.dataset.docID; let document = appDocsManager.getDoc(fileID); if(document._ != 'documentEmpty') { appMessagesManager.sendFile(appImManager.peerID, document, {isMedia: true}); appImManager.chatInputC.onMessageSent(true, false); dropdown.classList.remove('active'); toggleEl.classList.remove('active'); } else { console.warn('got no doc by id:', fileID); } }; let stickersInit = () => { let contentStickersDiv = document.getElementById('content-stickers') as HTMLDivElement; //let stickersDiv = contentStickersDiv.querySelector('.os-content') as HTMLDivElement; let menuWrapper = contentStickersDiv.nextElementSibling as HTMLDivElement; let menu = menuWrapper.firstElementChild.firstElementChild as HTMLUListElement; let menuScroll = new Scrollable(menuWrapper, 'x'); let stickersDiv = document.createElement('div'); stickersDiv.classList.add('stickers-categories'); contentStickersDiv.append(stickersDiv); stickersDiv.addEventListener('mouseover', (e) => { let target = e.target as HTMLElement; if(target.tagName == 'CANVAS') { // turn on sticker let animation = lottieLoader.getAnimation(target.parentElement, EMOTICONSSTICKERGROUP); if(animation) { // @ts-ignore if(animation.currentFrame == animation.totalFrames - 1) { animation.goToAndPlay(0, true); } else { animation.play(); } } } }); stickersDiv.addEventListener('click', onMediaClick); let heights: number[] = []; let heightRAF = 0; let categoryPush = (categoryDiv: HTMLDivElement, docs: MTDocument[], prepend?: boolean) => { //if((docs.length % 5) != 0) categoryDiv.classList.add('not-full'); docs.forEach(doc => { let div = document.createElement('div'); wrapSticker(doc, div, undefined, lazyLoadQueue, EMOTICONSSTICKERGROUP, true, false, true); categoryDiv.append(div); }); if(prepend) stickersScroll.prepend(categoryDiv); else stickersScroll.append(categoryDiv); setTimeout(() => lazyLoadQueue.check(), 0); /* let scrollHeight = categoryDiv.scrollHeight; let prevHeight = heights[heights.length - 1] || 0; //console.log('scrollHeight', scrollHeight, categoryDiv, stickersDiv.childElementCount); if(prepend && heights.length) {// all stickers loaded faster than recent heights.forEach((h, i) => heights[i] += scrollHeight); return heights.unshift(scrollHeight) - 1; } */ if(heightRAF) window.cancelAnimationFrame(heightRAF); heightRAF = window.requestAnimationFrame(() => { heightRAF = 0; let paddingTop = parseInt(window.getComputedStyle(stickersScroll.container).getPropertyValue('padding-top')) || 0; heights.length = 0; let concated = stickersScroll.hiddenElements.up.concat(stickersScroll.visibleElements, stickersScroll.hiddenElements.down); concated.forEach((el, i) => { heights[i] = (heights[i - 1] || 0) + el.height + (i == 0 ? paddingTop : 0); }); //console.log('stickers concated', concated, heights); }); /* Array.from(stickersDiv.children).forEach((div, i) => { heights[i] = (heights[i - 1] || 0) + div.scrollHeight; }); */ //stickersScroll.onScroll(); //return heights.push(prevHeight + scrollHeight) - 1; }; let prevCategoryIndex = 0; let stickersScroll = new Scrollable(contentStickersDiv, 'y', 500, 'STICKERS'); stickersScroll.container.addEventListener('scroll', (e) => { lazyLoadQueue.check(); lottieLoader.checkAnimations(); prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll); }); stickersScroll.setVirtualContainer(stickersDiv); stickersScroll.lock('both'); emoticonsMenuOnClick(menu, heights, stickersScroll, menuScroll); stickersInit = null; Promise.all([ apiManager.invokeApi('messages.getRecentStickers', {flags: 0, hash: 0}).then((res) => { let stickers: { _: string, hash: number, packs: any[], stickers: MTDocument[], dates: number[] } = res as any; let categoryDiv = document.createElement('div'); categoryDiv.classList.add('sticker-category'); //stickersScroll.prepend(categoryDiv); categoryPush(categoryDiv, stickers.stickers, true); }), apiManager.invokeApi('messages.getAllStickers', {hash: 0}).then(async(res) => { let stickers: { _: 'messages.allStickers', hash: number, sets: Array } = res as any; for(let set of stickers.sets) { let categoryDiv = document.createElement('div'); categoryDiv.classList.add('sticker-category'); let li = document.createElement('li'); li.classList.add('btn-icon'); menu.append(li); //stickersScroll.append(categoryDiv); let stickerSet = await appStickersManager.getStickerSet(set); //console.log('got stickerSet', stickerSet, li); if(stickerSet.set.thumb) { appStickersManager.getStickerSetThumb(stickerSet.set).then((blob) => { //console.log('setting thumb', stickerSet, blob); if(stickerSet.set.pFlags.animated) { // means animated const reader = new FileReader(); reader.addEventListener('loadend', async(e) => { // @ts-ignore const text = e.srcElement.result; let json = await apiManager.gzipUncompress(text, true); let animation = await lottieLoader.loadAnimation({ container: li, loop: true, autoplay: false, animationData: JSON.parse(json) }, EMOTICONSSTICKERGROUP); }); reader.readAsArrayBuffer(blob); } else { let image = new Image(); //image.src = URL.createObjectURL(blob); appWebpManager.polyfillImage(image, blob); li.append(image); } }); } else { // as thumb will be used first sticker wrapSticker(stickerSet.documents[0], li as any, undefined, undefined, EMOTICONSSTICKERGROUP); // kostil } categoryPush(categoryDiv, stickerSet.documents, false); } }) ]).then(() => { stickersScroll.unlock('both'); }); }; let gifsInit = () => { let contentDiv = document.getElementById('content-gifs') as HTMLDivElement; let masonry = contentDiv.firstElementChild as HTMLDivElement; masonry.addEventListener('click', onMediaClick); let scroll = new Scrollable(contentDiv, 'y', 500, 'GIFS', null); scroll.container.addEventListener('scroll', (e) => { lazyLoadQueue.check(); }); let width = 400; let maxSingleWidth = width - 100; let height = 100; apiManager.invokeApi('messages.getSavedGifs', {hash: 0}).then((_res) => { let res = _res as { _: 'messages.savedGifs', gifs: MTDocument[], hash: number }; console.log('getSavedGifs res:', res); let line: MTDocument[] = []; let wastedWidth = 0; res.gifs.forEach((gif, idx) => { res.gifs[idx] = appDocsManager.saveDoc(gif); }); for(let i = 0, length = res.gifs.length; i < length;) { let gif = res.gifs[i]; let gifWidth = gif.w; let gifHeight = gif.h; if(gifHeight < height) { gifWidth = height / gifHeight * gifWidth; gifHeight = height; } let willUseWidth = Math.min(maxSingleWidth, width - wastedWidth, gifWidth); let {w, h} = calcImageInBox(gifWidth, gifHeight, willUseWidth, height); /* wastedWidth += w; if(wastedWidth == width || h < height) { wastedWidth = 0; console.log('completed line', i, line); line = []; continue; } line.push(gif); */ ++i; console.log('gif:', gif, w, h); let div = document.createElement('div'); div.style.width = w + 'px'; //div.style.height = h + 'px'; div.dataset.docID = gif.id; masonry.append(div); let preloader = new ProgressivePreloader(div); lazyLoadQueue.push({ div, load: () => { let promise = appDocsManager.downloadDoc(gif); preloader.attach(div, true, promise); promise.then(blob => { preloader.detach(); div.innerHTML = ``; }); return promise; } }); } }); gifsInit = undefined; }; return {dropdown, lazyLoadQueue}; }; export default initEmoticonsDropdown;