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.
 
 
 
 
 

513 lines
17 KiB

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 Scrollable from "./scrollable_new";
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;
setTimeout(() => {
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
lazyLoadQueue.check();
}, 100);
/* window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
lazyLoadQueue.check();
});
}); */
});
};
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((a, b) => a - b));
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');
let container = document.createElement('div');
categoryDiv.append(container);
docs.forEach(doc => {
let div = document.createElement('div');
wrapSticker(doc, div, undefined, lazyLoadQueue, EMOTICONSSTICKERGROUP, true, false, true);
container.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);
}); */
let concated = Array.from(stickersScroll.splitUp.children);
concated.forEach((el, i) => {
heights[i] = (heights[i - 1] || 0) + el.scrollHeight + (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', undefined, undefined, 2);
stickersScroll.container.addEventListener('scroll', (e) => {
lazyLoadQueue.check();
lottieLoader.checkAnimations();
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll);
});
stickersScroll.setVirtualContainer(stickersDiv);
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<MTStickerSet>
} = 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<string>(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);
}
})
]);
};
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 = `<video autoplay="true" muted="true" loop="true" src="${gif.url}" type="video/mp4"></video>`;
});
return promise;
}
});
}
});
gifsInit = undefined;
};
return {dropdown, lazyLoadQueue};
};
export default initEmoticonsDropdown;