Telegram Web K with changes to work inside I2P https://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

382 lines
12 KiB

/*
* 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, {AnimationItemGroup} from '../animationIntersector';
import {horizontalMenu} from '../horizontalMenu';
import LazyLoadQueue 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 {IS_APPLE_MOBILE} from '../../environment/userAgent';
import {AppManagers} from '../../lib/appManagers/managers';
import type LazyLoadQueueIntersector from '../lazyLoadQueueIntersector';
import {simulateClickEvent} from '../../helpers/dom/clickEvent';
import overlayCounter from '../../helpers/overlayCounter';
export const EMOTICONSSTICKERGROUP: AnimationItemGroup = '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<typeof horizontalMenu>;
private savedRange: Range;
private managers: AppManagers;
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.managers = rootScope.managers;
this.emojiTab = new EmojiTab(this.managers);
this.stickersTab = new StickersTab(this.managers);
this.gifsTab = new GifsTab(this.managers);
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)) {
appSidebarRight.createTab(AppStickersTab).open();
}
} else {
if(!appSidebarRight.isTabExists(AppGifsTab)) {
appSidebarRight.createTab(AppGifsTab).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);
});
const HIDE_EMOJI_TAB = IS_APPLE_MOBILE;
const INIT_TAB_ID = HIDE_EMOJI_TAB ? 1 : 0;
if(HIDE_EMOJI_TAB) {
(this.tabsEl.children[1] as HTMLElement).classList.add('hide');
}
simulateClickEvent(this.tabsEl.children[INIT_TAB_ID + 1] as HTMLElement); // set emoji tab
if(this.tabs[INIT_TAB_ID].init) {
this.tabs[INIT_TAB_ID].init(); // onTransitionEnd не вызовется, т.к. это первая открытая вкладка
}
if(!IS_TOUCH_SUPPORTED) {
let lastMouseMoveEvent: MouseEvent, mouseMoveEventAttached = false;
const onMouseMove = (e: MouseEvent) => {
lastMouseMoveEvent = e;
};
overlayCounter.addEventListener('change', (isActive) => {
if(isActive) {
if(!mouseMoveEventAttached) {
document.body.addEventListener('mousemove', onMouseMove);
mouseMoveEventAttached = true;
}
} else if(mouseMoveEventAttached) {
document.body.removeEventListener('mousemove', onMouseMove);
if(lastMouseMoveEvent) {
this.onMouseOut(lastMouseMoveEvent);
}
}
});
}
appImManager.addEventListener('peer_changed', this.checkRights);
this.checkRights();
return super.init();
}
public getElement() {
return this.element;
}
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 = async() => {
const {peerId, threadId} = appImManager.chat;
const children = this.tabsEl.children;
const tabsElements = Array.from(children) as HTMLElement[];
const [canSendStickers, canSendGifs] = await Promise.all([
this.managers.appMessagesManager.canSendToPeer(peerId, threadId, 'send_stickers'),
this.managers.appMessagesManager.canSendToPeer(peerId, threadId, 'send_gifs')
]);
tabsElements[2].toggleAttribute('disabled', !canSendStickers);
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) {
menuScroll.scrollIntoViewNew({
element: menu.children[which] as HTMLElement,
position: 'center',
axis: 'x'
});
}
});
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;
}
let offsetTop = 0;
if(which > 0) {
const element = (scroll.splitUp || scroll.container).children[which] as HTMLElement;
offsetTop = element.offsetTop + 1; // * due to stickyIntersector
}
scroll.container.scrollTop = jumpedTo = offsetTop;
// console.log('set scrollTop:', offsetTop);
});
return {stickyIntersector, setActive};
};
public static onMediaClick = async(e: {target: EventTarget | Element}, clearDraft = false, silent?: boolean) => {
let target = e.target as HTMLElement;
target = findUpTag(target, 'DIV');
if(!target) return false;
const docId = target.dataset.docId;
if(!docId) return false;
return this.sendDocId(docId, clearDraft, silent);
};
public static async sendDocId(docId: DocId, clearDraft?: boolean, silent?: boolean) {
if(await appImManager.chat.input.sendMessageWithDocument(docId, undefined, clearDraft, silent)) {
/* 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:', docId);
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;