/* * https://github.com/morethanwords/tweb * Copyright (C) 2019-2021 Eduard Kuzmenko * https://github.com/morethanwords/tweb/blob/master/LICENSE */ import { IS_TOUCH_SUPPORTED } from "../../environment/touchSupport"; import appImManager from "../../lib/appManagers/appImManager"; import rootScope from "../../lib/rootScope"; import animationIntersector from "../animationIntersector"; import { horizontalMenu } from "../horizontalMenu"; import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue"; import Scrollable, { ScrollableX } from "../scrollable"; import appSidebarRight from "../sidebarRight"; import StickyIntersector from "../stickyIntersector"; import EmojiTab from "./tabs/emoji"; import GifsTab from "./tabs/gifs"; import StickersTab from "./tabs/stickers"; import { MOUNT_CLASS_TO } from "../../config/debug"; import AppGifsTab from "../sidebarRight/tabs/gifs"; import AppStickersTab from "../sidebarRight/tabs/stickers"; import findUpClassName from "../../helpers/dom/findUpClassName"; import findUpTag from "../../helpers/dom/findUpTag"; import blurActiveElement from "../../helpers/dom/blurActiveElement"; import whichChild from "../../helpers/dom/whichChild"; import { cancelEvent } from "../../helpers/dom/cancelEvent"; import DropdownHover from "../../helpers/dropdownHover"; import { pause } from "../../helpers/schedulers/pause"; import appMessagesManager from "../../lib/appManagers/appMessagesManager"; export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; export interface EmoticonsTab { init: () => void, onCloseAfterTimeout?: () => void } export class EmoticonsDropdown extends DropdownHover { public static lazyLoadQueue = new LazyLoadQueue(); private emojiTab: EmojiTab; public stickersTab: StickersTab; private gifsTab: GifsTab; private container: HTMLElement; private tabsEl: HTMLElement; private tabId = -1; private tabs: {[id: number]: EmoticonsTab}; private searchButton: HTMLElement; private deleteBtn: HTMLElement; private selectTab: ReturnType; private savedRange: Range; constructor() { super({ element: document.getElementById('emoji-dropdown') as HTMLDivElement }); this.addEventListener('open', async() => { if(IS_TOUCH_SUPPORTED) { //appImManager.chat.input.saveScroll(); if(blurActiveElement()) { await pause(100); } } if(this.element.parentElement !== appImManager.chat.input.chatInput) { appImManager.chat.input.chatInput.append(this.element); } this.savedRange = this.getGoodRange(); EmoticonsDropdown.lazyLoadQueue.lock(); //EmoticonsDropdown.lazyLoadQueue.unlock(); animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP); }); this.addEventListener('opened', () => { animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP); EmoticonsDropdown.lazyLoadQueue.unlock(); EmoticonsDropdown.lazyLoadQueue.refresh(); this.container.classList.remove('disable-hover'); }); this.addEventListener('close', () => { EmoticonsDropdown.lazyLoadQueue.lock(); //EmoticonsDropdown.lazyLoadQueue.lock(); // нужно залочить группу и выключить стикеры animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP); animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP); }); this.addEventListener('closed', () => { // теперь можно убрать visible, чтобы они не включились после фокуса animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP); EmoticonsDropdown.lazyLoadQueue.unlock(); EmoticonsDropdown.lazyLoadQueue.refresh(); this.container.classList.remove('disable-hover'); this.savedRange = undefined; }); } protected init() { this.emojiTab = new EmojiTab(); this.stickersTab = new StickersTab(); this.gifsTab = new GifsTab(); this.tabs = { 0: this.emojiTab, 1: this.stickersTab, 2: this.gifsTab }; this.container = this.element.querySelector('.emoji-container .tabs-container') as HTMLDivElement; this.tabsEl = this.element.querySelector('.emoji-tabs') as HTMLUListElement; this.selectTab = horizontalMenu(this.tabsEl, this.container, this.onSelectTabClick, () => { const tab = this.tabs[this.tabId]; if(tab.init) { tab.init(); } tab.onCloseAfterTimeout && tab.onCloseAfterTimeout(); animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP); }); this.searchButton = this.element.querySelector('.emoji-tabs-search'); this.searchButton.addEventListener('click', () => { if(this.tabId === 1) { if(!appSidebarRight.isTabExists(AppStickersTab)) { new AppStickersTab(appSidebarRight).open(); } } else { if(!appSidebarRight.isTabExists(AppGifsTab)) { new AppGifsTab(appSidebarRight).open(); } } }); this.deleteBtn = this.element.querySelector('.emoji-tabs-delete'); this.deleteBtn.addEventListener('click', (e) => { const input = appImManager.chat.input.messageInput; if((input.lastChild as any)?.tagName) { input.lastElementChild.remove(); } else if(input.lastChild) { if(!input.lastChild.textContent.length) { input.lastChild.remove(); } else { input.lastChild.textContent = input.lastChild.textContent.slice(0, -1); } } const event = new Event('input', {bubbles: true, cancelable: true}); appImManager.chat.input.messageInput.dispatchEvent(event); //appSidebarRight.stickersTab.init(); cancelEvent(e); }); (this.tabsEl.children[1] as HTMLLIElement).click(); // set emoji tab if(this.tabs[0].init) { this.tabs[0].init(); // onTransitionEnd не вызовется, т.к. это первая открытая вкладка } rootScope.addEventListener('peer_changed', this.checkRights); this.checkRights(); return super.init(); } private onSelectTabClick = (id: number) => { if(this.tabId === id) { return; } animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP); this.tabId = id; this.searchButton.classList.toggle('hide', this.tabId === 0); this.deleteBtn.classList.toggle('hide', this.tabId !== 0); }; private checkRights = () => { const {peerId, threadId} = appImManager.chat; const children = this.tabsEl.children; const tabsElements = Array.from(children) as HTMLElement[]; const canSendStickers = appMessagesManager.canSendToPeer(peerId, threadId, 'send_stickers'); tabsElements[2].toggleAttribute('disabled', !canSendStickers); const canSendGifs = appMessagesManager.canSendToPeer(peerId, threadId, 'send_gifs'); tabsElements[3].toggleAttribute('disabled', !canSendGifs); const active = this.tabsEl.querySelector('.active'); if(active && whichChild(active) !== 1 && (!canSendStickers || !canSendGifs)) { this.selectTab(0, false); } }; public static menuOnClick = (menu: HTMLElement, scroll: Scrollable, menuScroll?: ScrollableX, prevId = 0) => { let jumpedTo = -1; const setActive = (id: number) => { if(id === prevId) { return false; } menu.children[prevId].classList.remove('active'); menu.children[id].classList.add('active'); prevId = id; return true; }; const stickyIntersector = new StickyIntersector(scroll.container, (stuck, target) => { //console.log('sticky scrollTOp', stuck, target, scroll.container.scrollTop); if(Math.abs(jumpedTo - scroll.container.scrollTop) <= 1) { return; } else { jumpedTo = -1; } const which = whichChild(target); if(!stuck && which) { // * due to stickyIntersector return; } setActive(which); if(menuScroll) { if(which < menu.childElementCount - 4) { menuScroll.container.scrollLeft = (which - 3) * 47; } else { menuScroll.container.scrollLeft = which * 47; } } }); menu.addEventListener('click', (e) => { let target = e.target as HTMLElement; target = findUpClassName(target, 'menu-horizontal-div-item'); if(!target) { return; } const which = whichChild(target); /* if(menuScroll) { menuScroll.scrollIntoView(target, false, 0); } */ if(!setActive(which)) { return; } const element = (scroll.splitUp || scroll.container).children[which] as HTMLElement; const offsetTop = element.offsetTop + 1; // * due to stickyIntersector scroll.container.scrollTop = jumpedTo = offsetTop; //console.log('set scrollTop:', offsetTop); }); return {stickyIntersector, setActive}; }; public static onMediaClick = (e: {target: EventTarget | Element}, clearDraft = false) => { let target = e.target as HTMLElement; target = findUpTag(target, 'DIV'); if(!target) return false; const fileId = target.dataset.docId; if(!fileId) return false; if(appImManager.chat.input.sendMessageWithDocument(fileId, undefined, clearDraft)) { /* dropdown.classList.remove('active'); toggleEl.classList.remove('active'); */ if(emoticonsDropdown.container) { emoticonsDropdown.forceClose = true; emoticonsDropdown.container.classList.add('disable-hover'); emoticonsDropdown.toggle(false); } return true; } else { console.warn('got no doc by id:', fileId); return false; } }; public addLazyLoadQueueRepeat(lazyLoadQueue: LazyLoadQueueIntersector, processInvisibleDiv: (div: HTMLElement) => void) { this.addEventListener('close', () => { lazyLoadQueue.lock(); }); this.addEventListener('closed', () => { const divs = lazyLoadQueue.intersector.getVisible(); for(const div of divs) { processInvisibleDiv(div); } lazyLoadQueue.intersector.clearVisible(); }); this.addEventListener('opened', () => { lazyLoadQueue.unlockAndRefresh(); }); } public getSavedRange() { return this.getGoodRange() || this.savedRange; } private getGoodRange() { const sel = document.getSelection(); if(sel.rangeCount && document.activeElement === appImManager.chat.input.messageInput) { return sel.getRangeAt(0); } } } const emoticonsDropdown = new EmoticonsDropdown(); MOUNT_CLASS_TO.emoticonsDropdown = emoticonsDropdown; export default emoticonsDropdown;