diff --git a/package-lock.json b/package-lock.json
index 74656c00..c730b384 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9512,6 +9512,15 @@
}
}
},
+ "mp4box": {
+ "version": "0.3.20",
+ "resolved": "https://registry.npmjs.org/mp4box/-/mp4box-0.3.20.tgz",
+ "integrity": "sha512-9I1wOBql0c9BsIPDGHY97dcH5kT7hG0Tx6SAaJvXf+A6Z0zBfGy7L1vEfjMKgjXSjtdXWL7gO+8a5euikaFTEA==",
+ "dev": true,
+ "requires": {
+ "npm": "^6.9.0"
+ }
+ },
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
diff --git a/package.json b/package.json
index 0d331a9e..0cfdfaeb 100644
--- a/package.json
+++ b/package.json
@@ -47,6 +47,7 @@
"lottie-web": "^5.6.10",
"media-query-plugin": "^1.3.1",
"mini-css-extract-plugin": "^0.9.0",
+ "mp4box": "^0.3.20",
"node-sass": "^4.14.1",
"npm": "^6.14.5",
"on-build-webpack": "^0.1.0",
diff --git a/src/components/appAudio.ts b/src/components/appAudio.ts
index 0602c42d..221ed240 100644
--- a/src/components/appAudio.ts
+++ b/src/components/appAudio.ts
@@ -52,6 +52,14 @@ class AppAudio {
appDocsManager.downloadDoc(doc.id).then(() => {
this.container.append(audio);
source.src = doc.url;
+ }, () => {
+ if(this.nextMid == mid) {
+ this.loadSiblingsAudio(doc.type as 'voice' | 'audio', mid).then(() => {
+ if(this.nextMid && this.audios[this.nextMid]) {
+ this.audios[this.nextMid].play();
+ }
+ })
+ }
});
return this.audios[mid] = audio;
@@ -74,7 +82,7 @@ class AppAudio {
const message = appMessagesManager.getMessage(mid);
this.prevMid = this.nextMid = 0;
- appMessagesManager.getSearch(message.peerID, '', {
+ return appMessagesManager.getSearch(message.peerID, '', {
_: type == 'audio' ? 'inputMessagesFilterMusic' : 'inputMessagesFilterVoice'
}, mid, 3, 0, 2).then(value => {
if(this.playingAudio != audio) {
diff --git a/src/components/appForward.ts b/src/components/appForward.ts
index 83362517..d3e49c2f 100644
--- a/src/components/appForward.ts
+++ b/src/components/appForward.ts
@@ -74,7 +74,7 @@ class AppForward {
this.sendBtn.classList.remove('is-visible');
}
}, 'dialogs', () => {
- console.log('forward rendered:', this.container.querySelector('.selector ul').childElementCount);
+ //console.log('forward rendered:', this.container.querySelector('.selector ul').childElementCount);
this.sidebarWasActive = appSidebarRight.sidebarEl.classList.contains('active');
appSidebarRight.toggleSidebar(true);
});
diff --git a/src/components/appSelectPeers.ts b/src/components/appSelectPeers.ts
index 20e41e0e..df83335a 100644
--- a/src/components/appSelectPeers.ts
+++ b/src/components/appSelectPeers.ts
@@ -98,7 +98,7 @@ export class AppSelectPeers {
this.list.innerHTML = '';
this.query = value;
- console.log('selectPeers input:', this.query);
+ //console.log('selectPeers input:', this.query);
this.getMoreResults();
}
});
@@ -135,7 +135,7 @@ export class AppSelectPeers {
const newOffsetIndex = dialogs[dialogs.length - 1].index || 0;
dialogs = dialogs.filter(d => d.peerID != this.myID);
- if(!this.offsetIndex) {
+ if(!this.offsetIndex && !this.query) {
dialogs.unshift({
peerID: this.myID,
pFlags: {}
@@ -175,7 +175,7 @@ export class AppSelectPeers {
}
private renderResults(peerIDs: number[]) {
- console.log('will renderResults:', peerIDs);
+ //console.log('will renderResults:', peerIDs);
peerIDs.forEach(peerID => {
const {dom} = appDialogsManager.addDialog(peerID, this.scrollable, false, false);
dom.containerEl.insertAdjacentHTML('afterbegin', '
');
diff --git a/src/components/audio.ts b/src/components/audio.ts
index 8c27c62b..7479807c 100644
--- a/src/components/audio.ts
+++ b/src/components/audio.ts
@@ -118,12 +118,10 @@ function wrapVoiceMessage(doc: MTDocument, audioEl: AudioElement) {
rects.slice(0, lastIndex + 1).forEach(node => node.classList.add('active'));
}
- audioEl.addAudioListener('playing', () => {
- //rects.forEach(node => node.classList.remove('active'));
-
+ let start = () => {
clearInterval(interval);
interval = setInterval(() => {
- if(lastIndex > svg.childElementCount || isNaN(audio.duration)) {
+ if(lastIndex > svg.childElementCount || isNaN(audio.duration) || audio.paused) {
clearInterval(interval);
return;
}
@@ -137,6 +135,15 @@ function wrapVoiceMessage(doc: MTDocument, audioEl: AudioElement) {
//console.log('lastIndex:', lastIndex, audio.currentTime);
//}, duration * 1000 / svg.childElementCount | 0/* 63 * duration / 10 */);
}, 20);
+ };
+
+ if(!audio.paused) {
+ start();
+ }
+
+ audioEl.addAudioListener('playing', () => {
+ //rects.forEach(node => node.classList.remove('active'));
+ start();
});
audioEl.addAudioListener('pause', () => {
diff --git a/src/components/avatar.ts b/src/components/avatar.ts
index 1c34732c..1cbb31d5 100644
--- a/src/components/avatar.ts
+++ b/src/components/avatar.ts
@@ -45,7 +45,7 @@ export default class AvatarElement extends HTMLElement {
} else if(name == 'peer-title') {
this.peerTitle = newValue;
} else if(name == 'dialog') {
- this.isDialog = !!newValue;
+ this.isDialog = !!+newValue;
}
}
diff --git a/src/components/chatInput.ts b/src/components/chatInput.ts
index 6a830c54..9f2ea70c 100644
--- a/src/components/chatInput.ts
+++ b/src/components/chatInput.ts
@@ -1,5 +1,4 @@
import Scrollable from "./scrollable_new";
-import LazyLoadQueue from "./lazyLoadQueue";
import { RichTextProcessor } from "../lib/richtextprocessor";
//import apiManager from "../lib/mtproto/apiManager";
import apiManager from "../lib/mtproto/mtprotoworker";
@@ -8,14 +7,14 @@ import appImManager from "../lib/appManagers/appImManager";
import { getRichValue, calcImageInBox } from "../lib/utils";
import { wrapDocument, wrapReply } from "./wrappers";
import appMessagesManager from "../lib/appManagers/appMessagesManager";
-import initEmoticonsDropdown, { EMOTICONSSTICKERGROUP } from "./emoticonsDropdown";
import { Layouter, RectPart } from "./groupedLayout";
import Recorder from '../../public/recorder.min';
//import Recorder from '../opus-recorder/dist/recorder.min';
import opusDecodeController from "../lib/opusDecodeController";
import { touchSupport } from "../lib/config";
-import animationIntersector from "./animationIntersector";
import appDocsManager from "../lib/appManagers/appDocsManager";
+import emoticonsDropdown from "./emoticonsDropdown";
+import PopupCreatePoll from "./popupCreatePoll";
export class ChatInput {
public pageEl = document.getElementById('page-chats') as HTMLDivElement;
@@ -25,10 +24,6 @@ export class ChatInput {
public inputScroll = new Scrollable(this.inputMessageContainer);
public btnSend = document.getElementById('btn-send') as HTMLButtonElement;
public btnCancelRecord = this.btnSend.parentElement.previousElementSibling as HTMLButtonElement;
- public emoticonsDropdown: HTMLDivElement = null;
- public emoticonsTimeout: number = 0;
- public toggleEmoticons: HTMLButtonElement;
- public emoticonsLazyLoadQueue: LazyLoadQueue = null;
public lastUrl = '';
public lastTimeType = 0;
@@ -74,8 +69,6 @@ export class ChatInput {
private scrollDiff = 0;
constructor() {
- this.toggleEmoticons = this.pageEl.querySelector('.toggle-emoticons') as HTMLButtonElement;
-
this.attachMenu.container = document.getElementById('attach-file') as HTMLButtonElement;
this.attachMenu.media = this.attachMenu.container.querySelector('.menu-media') as HTMLDivElement;
this.attachMenu.document = this.attachMenu.container.querySelector('.menu-document') as HTMLDivElement;
@@ -109,7 +102,7 @@ export class ChatInput {
}
this.messageInput.addEventListener('keydown', (e: KeyboardEvent) => {
- if(e.key == 'Enter') {
+ if(e.key == 'Enter' && !touchSupport) {
/* if(e.ctrlKey || e.metaKey) {
this.messageInput.innerHTML += ' ';
placeCaretAtEnd(this.message)
@@ -127,7 +120,7 @@ export class ChatInput {
if(touchSupport) {
this.messageInput.addEventListener('touchend', (e) => {
this.saveScroll();
- toggleEmoticons(false);
+ emoticonsDropdown.toggle(false);
});
window.addEventListener('resize', () => {
@@ -423,6 +416,10 @@ export class ChatInput {
this.fileInput.click();
});
+ this.attachMenu.poll.addEventListener('click', () => {
+ new PopupCreatePoll().show();
+ });
+
document.addEventListener('paste', (event) => {
if(!appImManager.peerID || this.attachMediaPopUp.container.classList.contains('active')) {
return;
@@ -632,95 +629,6 @@ export class ChatInput {
};
}
- let emoticonsDisplayTimeout = 0;
- const toggleEmoticons = async(enable?: boolean) => {
- if(!this.emoticonsDropdown) return;
-
- if(touchSupport) {
- const willBeActive = (!!this.emoticonsDropdown.style.display && enable === undefined) || enable;
- this.toggleEmoticons.classList.toggle('flip-icon', willBeActive);
- if(willBeActive) {
- this.saveScroll();
- // @ts-ignore
- document.activeElement.blur();
- await new Promise((resolve) => {
- setTimeout(resolve, 100);
- });
- }
- } else {
- this.toggleEmoticons.classList.toggle('active', enable);
- }
-
- if((this.emoticonsDropdown.style.display && enable === undefined) || enable) {
- this.emoticonsDropdown.style.display = '';
- void this.emoticonsDropdown.offsetLeft; // reflow
- this.emoticonsDropdown.classList.add('active');
- this.emoticonsLazyLoadQueue.unlock();
- clearTimeout(emoticonsDisplayTimeout);
-
- /* if(touchSupport) {
- this.restoreScroll();
- } */
- } else {
- this.emoticonsDropdown.classList.remove('active');
- animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
- this.emoticonsLazyLoadQueue.lock();
-
- clearTimeout(emoticonsDisplayTimeout);
- emoticonsDisplayTimeout = setTimeout(() => {
- this.emoticonsDropdown.style.display = 'none';
- }, touchSupport ? 0 : 200);
-
- /* if(touchSupport) {
- this.restoreScroll();
- } */
- }
-
- animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
- };
-
- if(touchSupport) {
- this.toggleEmoticons.addEventListener('click', () => {
- if(!this.emoticonsDropdown) {
- let res = initEmoticonsDropdown(this.pageEl, appImManager,
- appMessagesManager, this.messageInput, this.toggleEmoticons, this.btnSend);
-
- this.emoticonsDropdown = res.dropdown;
- this.emoticonsLazyLoadQueue = res.lazyLoadQueue;
-
- toggleEmoticons(true);
- } else {
- toggleEmoticons();
- }
- });
- } else {
- this.toggleEmoticons.onmouseover = (e) => {
- clearTimeout(this.emoticonsTimeout);
- //this.emoticonsTimeout = setTimeout(() => {
- if(!this.emoticonsDropdown) {
- let res = initEmoticonsDropdown(this.pageEl, appImManager,
- appMessagesManager, this.messageInput, this.toggleEmoticons, this.btnSend);
-
- this.emoticonsDropdown = res.dropdown;
- this.emoticonsLazyLoadQueue = res.lazyLoadQueue;
-
- this.toggleEmoticons.onmouseout = this.emoticonsDropdown.onmouseout = (e) => {
- clearTimeout(this.emoticonsTimeout);
- this.emoticonsTimeout = setTimeout(() => {
- toggleEmoticons();
- }, 200);
- };
-
- this.emoticonsDropdown.onmouseover = (e) => {
- clearTimeout(this.emoticonsTimeout);
- };
- }
-
- toggleEmoticons(true);
- //}, 0/* 200 */);
- };
- }
-
this.replyElements.cancelBtn.addEventListener('click', () => {
this.replyElements.container.classList.remove('active');
this.replyToMsgID = 0;
diff --git a/src/components/emoticonsDropdown.ts b/src/components/emoticonsDropdown.ts
index 571a5f91..dc486a10 100644
--- a/src/components/emoticonsDropdown.ts
+++ b/src/components/emoticonsDropdown.ts
@@ -1,6 +1,5 @@
-import { AppImManager } from "../lib/appManagers/appImManager";
-import { AppMessagesManager } from "../lib/appManagers/appMessagesManager";
-import { horizontalMenu, renderImageFromUrl } from "./misc";
+import appImManager from "../lib/appManagers/appImManager";
+import { horizontalMenu, renderImageFromUrl, putPreloader } from "./misc";
import lottieLoader from "../lib/lottieLoader";
//import Scrollable from "./scrollable";
import Scrollable from "./scrollable_new";
@@ -9,125 +8,41 @@ 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 { wrapSticker } from "./wrappers";
import appDocsManager from "../lib/appManagers/appDocsManager";
import ProgressivePreloader from "./preloader";
-import Config from "../lib/config";
+import Config, { touchSupport } from "../lib/config";
import { MTDocument } from "../types";
import animationIntersector from "./animationIntersector";
import appSidebarRight from "../lib/appManagers/appSidebarRight";
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.style.display = '';
- void dropdown.offsetLeft; // reflow
- dropdown.classList.add('active'); // need
-
- let lazyLoadQueue = new LazyLoadQueue(5);
-
- const searchButton = dropdown.querySelector('.emoji-tabs-search');
- searchButton.addEventListener('click', () => {
- appSidebarRight.stickersTab.init();
- });
-
- let container = pageEl.querySelector('.emoji-container .tabs-container') as HTMLDivElement;
- let tabs = pageEl.querySelector('.emoji-dropdown .emoji-tabs') as HTMLUListElement;
- let tabID = -1;
- horizontalMenu(tabs, container, (id) => {
- animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
-
- tabID = id;
- }, () => {
- if(tabID == 1 && stickersInit) {
- stickersInit();
- } else if(tabID == 2 && gifsInit) {
- gifsInit();
- }
-
- animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
- });
+interface EmoticonsTab {
+ init: () => void,
+ onCloseAfterTimeout?: () => void
+}
- (tabs.firstElementChild.children[1] as HTMLLIElement).click(); // set emoji tab
-
- 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');
+class EmojiTab implements EmoticonsTab {
+ public content: HTMLElement;
- let index = whichChild(target);
- let y = heights[index - 1/* 2 */] || 0; // 10 == padding .scrollable
+ init() {
+ this.content = document.getElementById('content-emoji') as HTMLDivElement;
- /* 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(() => {
- animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
- }, 100);
-
- /* window.requestAnimationFrame(() => {
- window.requestAnimationFrame(() => {
- lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
- });
- }); */
- });
- };
-
- 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;
- };
-
- {
const categories = ["Smileys & Emotion", "Animals & Nature", "Food & Drink", "Travel & Places", "Activities", "Objects", /* "Symbols", */"Flags", "Skin Tones"];
- let divs: {
+ const divs: {
[category: string]: HTMLDivElement
} = {};
- let sorted: {
+ const sorted: {
[category: string]: string[]
} = {};
- for(let emoji in Config.Emoji) {
- let details = Config.Emoji[emoji];
- let i = '' + details;
- let category = categories[+i[0] - 1];
+ 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] = [];
@@ -142,27 +57,27 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
delete sorted["Skin Tones"];
//console.time('emojiParse');
- for(let category in sorted) {
- let div = document.createElement('div');
+ for(const category in sorted) {
+ const div = document.createElement('div');
div.classList.add('emoji-category');
- let titleDiv = document.createElement('div');
+ const titleDiv = document.createElement('div');
titleDiv.classList.add('category-title');
titleDiv.innerText = category;
- let itemsDiv = document.createElement('div');
+ const itemsDiv = document.createElement('div');
itemsDiv.classList.add('category-items');
div.append(titleDiv, itemsDiv);
- let emojis = sorted[category];
+ const emojis = sorted[category];
emojis.forEach(emoji => {
- //let emoji = details.unified;
- //let emoji = (details.unified as string).split('-')
+ //const emoji = details.unified;
+ //const 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);
+ const spanEmoji = document.createElement('span');
+ const kek = RichTextProcessor.wrapRichText(emoji);
if(!kek.includes('emoji')) {
console.log(emoji, kek, spanEmoji, emoji.length, new TextEncoder().encode(emoji));
@@ -182,84 +97,77 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
}
//console.timeEnd('emojiParse');
- let contentEmojiDiv = document.getElementById('content-emoji') as HTMLDivElement;
- let heights: number[] = [0];
+ const heights: number[] = [0];
let prevCategoryIndex = 1;
- let menu = contentEmojiDiv.previousElementSibling.firstElementChild as HTMLUListElement;
- let emojiScroll = new Scrollable(contentEmojiDiv, 'y', 'EMOJI', null);
+ const menu = this.content.previousElementSibling.firstElementChild as HTMLUListElement;
+ const emojiScroll = new Scrollable(this.content, 'y', 'EMOJI', null);
emojiScroll.container.addEventListener('scroll', (e) => {
- prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, emojiScroll.container);
+ prevCategoryIndex = EmoticonsDropdown.contentOnScroll(menu, heights, prevCategoryIndex, emojiScroll.container);
});
//emojiScroll.setVirtualContainer(emojiScroll.container);
- categories.map(category => {
- let div = divs[category];
+ const preloader = putPreloader(this.content, true);
- if(!div) {
- console.error('no div by category:', category);
- }
+ setTimeout(() => {
+ preloader.remove();
- 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);
- });
+ categories.map(category => {
+ const 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);
+ });
+ }, 200);
- contentEmojiDiv.addEventListener('click', function(e) {
- let target = e.target as any;
- //if(target.tagName != 'SPAN') return;
+ this.content.addEventListener('click', this.onContentClick);
+ EmoticonsDropdown.menuOnClick(menu, heights, emojiScroll);
+ this.init = null;
+ }
- if(target.tagName == 'SPAN' && !target.classList.contains('emoji')) {
- target = target.firstElementChild;
- } else if(target.tagName == 'DIV') return;
+ onContentClick = (e: MouseEvent) => {
+ let target = e.target as any;
+ //if(target.tagName != 'SPAN') return;
- //console.log('contentEmoji div', target);
+ if(target.tagName == 'SPAN' && !target.classList.contains('emoji')) {
+ target = target.firstElementChild;
+ } else if(target.tagName == 'DIV') return;
- /* if(!target.classList.contains('emoji')) {
- target = target.parentElement as HTMLSpanElement;
+ //console.log('contentEmoji div', target);
- if(!target.classList.contains('emoji')) {
- return;
- }
- } */
+ appImManager.chatInputC.messageInput.innerHTML += target.outerHTML;
- //messageInput.innerHTML += target.innerHTML;
- messageInput.innerHTML += target.outerHTML;
+ const event = new Event('input', {bubbles: true, cancelable: true});
+ appImManager.chatInputC.messageInput.dispatchEvent(event);
+ };
- btnSend.classList.add('tgico-send');
- btnSend.classList.remove('tgico-microphone2');
- });
+ onClose() {
- emoticonsMenuOnClick(menu, heights, emojiScroll);
}
+}
- let onMediaClick = (e: MouseEvent) => {
- let target = e.target as HTMLDivElement;
- target = findUpTag(target, 'DIV');
-
- let fileID = target.dataset.docID;
- if(appImManager.chatInputC.sendMessageWithDocument(fileID)) {
- dropdown.classList.remove('active');
- toggleEl.classList.remove('active');
- } else {
- console.warn('got no doc by id:', fileID);
- }
- };
+class StickersTab implements EmoticonsTab {
+ public content: HTMLElement;
- let stickersInit = () => {
- let contentStickersDiv = document.getElementById('content-stickers') as HTMLDivElement;
+ init() {
+ this.content = document.getElementById('content-stickers');
//let stickersDiv = contentStickersDiv.querySelector('.os-content') as HTMLDivElement;
- let menuWrapper = contentStickersDiv.previousElementSibling as HTMLDivElement;
+ let menuWrapper = this.content.previousElementSibling 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);
+ this.content.append(stickersDiv);
/* stickersDiv.addEventListener('mouseover', (e) => {
let target = e.target as HTMLElement;
@@ -278,7 +186,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
}
}); */
- stickersDiv.addEventListener('click', onMediaClick);
+ stickersDiv.addEventListener('click', EmoticonsDropdown.onMediaClick);
let heights: number[] = [];
@@ -300,7 +208,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
wrapSticker({
doc,
div,
- lazyLoadQueue,
+ lazyLoadQueue: EmoticonsDropdown.lazyLoadQueue,
group: EMOTICONSSTICKERGROUP,
onlyThumb: true
});
@@ -349,17 +257,17 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
};
let prevCategoryIndex = 0;
- let stickersScroll = new Scrollable(contentStickersDiv, 'y', 'STICKERS', undefined, undefined, 2);
+ let stickersScroll = new Scrollable(this.content, 'y', 'STICKERS', undefined, undefined, 2);
stickersScroll.container.addEventListener('scroll', (e) => {
animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
- prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll);
+ prevCategoryIndex = EmoticonsDropdown.contentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll);
});
stickersScroll.setVirtualContainer(stickersDiv);
- emoticonsMenuOnClick(menu, heights, stickersScroll, menuScroll);
+ EmoticonsDropdown.menuOnClick(menu, heights, stickersScroll, menuScroll);
- stickersInit = null;
+ const preloader = putPreloader(this.content, true);
Promise.all([
appStickersManager.getRecentStickers().then(stickers => {
@@ -368,6 +276,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
//stickersScroll.prepend(categoryDiv);
+ preloader.remove();
categoryPush(categoryDiv, 'Recent', stickers.stickers, true);
}),
@@ -378,6 +287,8 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
sets: Array
} = res as any;
+ preloader.remove();
+
for(let set of stickers.sets) {
let categoryDiv = document.createElement('div');
categoryDiv.classList.add('sticker-category');
@@ -434,19 +345,31 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
}
})
]);
- };
- let gifsInit = () => {
- let contentDiv = document.getElementById('content-gifs') as HTMLDivElement;
- let masonry = contentDiv.firstElementChild as HTMLDivElement;
+ this.init = null;
+ }
+
+ onClose() {
- masonry.addEventListener('click', onMediaClick);
+ }
+}
- let scroll = new Scrollable(contentDiv, 'y', 'GIFS', null);
+class GifsTab implements EmoticonsTab {
+ public content: HTMLElement;
- let width = 400;
- let maxSingleWidth = width - 100;
- let height = 100;
+ init() {
+ this.content = document.getElementById('content-gifs');
+ const masonry = this.content.firstElementChild as HTMLDivElement;
+
+ masonry.addEventListener('click', EmoticonsDropdown.onMediaClick);
+
+ const scroll = new Scrollable(this.content, 'y', 'GIFS', null);
+
+ const preloader = putPreloader(this.content, true);
+
+ const width = 400;
+ const maxSingleWidth = width - 100;
+ const height = 100;
apiManager.invokeApi('messages.getSavedGifs', {hash: 0}).then((_res) => {
let res = _res as {
@@ -454,9 +377,9 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
gifs: MTDocument[],
hash: number
};
- console.log('getSavedGifs res:', res);
+ //console.log('getSavedGifs res:', res);
- let line: MTDocument[] = [];
+ //let line: MTDocument[] = [];
let wastedWidth = 0;
@@ -464,6 +387,8 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
res.gifs[idx] = appDocsManager.saveDoc(gif);
});
+ preloader.remove();
+
for(let i = 0, length = res.gifs.length; i < length;) {
let gif = res.gifs[i];
@@ -489,7 +414,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
line.push(gif); */
++i;
- console.log('gif:', gif, w, h);
+ //console.log('gif:', gif, w, h);
let div = document.createElement('div');
div.style.width = w + 'px';
@@ -499,7 +424,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
masonry.append(div);
let preloader = new ProgressivePreloader(div);
- lazyLoadQueue.push({
+ EmoticonsDropdown.lazyLoadQueue.push({
div,
load: () => {
let promise = appDocsManager.downloadDoc(gif);
@@ -517,10 +442,258 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement,
}
});
- gifsInit = undefined;
+ this.init = null;
+ }
+
+ onClose() {
+
+ }
+}
+
+class EmoticonsDropdown {
+ public static lazyLoadQueue = new LazyLoadQueue();
+ private element: HTMLElement;
+
+ private emojiTab: EmojiTab;
+ private stickersTab: StickersTab;
+ private gifsTab: GifsTab;
+
+ private container: HTMLElement;
+ private tabsEl: HTMLElement;
+ private tabID = -1;
+
+ private tabs: {[id: number]: EmoticonsTab};
+
+ public searchButton: HTMLElement;
+ public deleteBtn: HTMLElement;
+
+ public toggleEl: HTMLElement;
+ private displayTimeout: number;
+
+ constructor() {
+ this.element = document.getElementById('emoji-dropdown') as HTMLDivElement;
+
+ let firstTime = true;
+ this.toggleEl = document.getElementById('toggle-emoticons');
+ if(touchSupport) {
+ this.toggleEl.addEventListener('click', () => {
+ if(firstTime) {
+ firstTime = false;
+ this.toggle(true);
+ } else {
+ this.toggle();
+ }
+ });
+ } else {
+ this.toggleEl.onmouseover = (e) => {
+ clearTimeout(this.displayTimeout);
+ //this.displayTimeout = setTimeout(() => {
+ if(firstTime) {
+ this.toggleEl.onmouseout = this.element.onmouseout = (e) => {
+ clearTimeout(this.displayTimeout);
+ this.displayTimeout = setTimeout(() => {
+ this.toggle();
+ }, 200);
+ };
+
+ this.element.onmouseover = (e) => {
+ clearTimeout(this.displayTimeout);
+ };
+
+ firstTime = false;
+ }
+
+ this.toggle(true);
+ //}, 0/* 200 */);
+ };
+ }
+ }
+
+ private 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;
+ horizontalMenu(this.tabsEl, this.container, (id) => {
+ animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
+
+ this.tabID = id;
+ this.searchButton.classList.toggle('hide', this.tabID != 1);
+ this.deleteBtn.classList.toggle('hide', this.tabID != 0);
+ }, () => {
+ 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', () => {
+ appSidebarRight.stickersTab.init();
+ });
+
+ this.deleteBtn = this.element.querySelector('.emoji-tabs-delete');
+ this.deleteBtn.addEventListener('click', () => {
+ const input = appImManager.chatInputC.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.chatInputC.messageInput.dispatchEvent(event);
+ //appSidebarRight.stickersTab.init();
+ });
+
+ (this.tabsEl.firstElementChild.children[1] as HTMLLIElement).click(); // set emoji tab
+ this.tabs[0].init(); // onTransitionEnd не вызовется, т.к. это первая открытая вкладка
+ }
+
+ public toggle = async(enable?: boolean) => {
+ //if(!this.element) return;
+ const willBeActive = (!!this.element.style.display && enable === undefined) || enable;
+ if(this.init) {
+ if(willBeActive) {
+ this.init();
+ this.init = null;
+ } else {
+ return;
+ }
+ }
+
+ if(touchSupport) {
+ this.toggleEl.classList.toggle('flip-icon', willBeActive);
+ if(willBeActive) {
+ appImManager.chatInputC.saveScroll();
+ // @ts-ignore
+ document.activeElement.blur();
+ await new Promise((resolve) => {
+ setTimeout(resolve, 100);
+ });
+ }
+ } else {
+ this.toggleEl.classList.toggle('active', enable);
+ }
+
+ if((this.element.style.display && enable === undefined) || enable) {
+ this.element.style.display = '';
+ void this.element.offsetLeft; // reflow
+ this.element.classList.add('active');
+ EmoticonsDropdown.lazyLoadQueue.unlock();
+ clearTimeout(this.displayTimeout);
+
+ /* if(touchSupport) {
+ this.restoreScroll();
+ } */
+ } else {
+ this.element.classList.remove('active');
+ animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
+ EmoticonsDropdown.lazyLoadQueue.lock();
+
+ clearTimeout(this.displayTimeout);
+ this.displayTimeout = setTimeout(() => {
+ this.element.style.display = 'none';
+ }, touchSupport ? 0 : 200);
+
+ /* if(touchSupport) {
+ this.restoreScroll();
+ } */
+ }
+
+ animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
+ };
+
+ public static menuOnClick = (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(() => {
+ animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
+ }, 100);
+
+ /* window.requestAnimationFrame(() => {
+ window.requestAnimationFrame(() => {
+ lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
+ });
+ }); */
+ });
};
- return {dropdown, lazyLoadQueue};
-};
+ public static contentOnScroll = (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;
+ };
-export default initEmoticonsDropdown;
+ public static onMediaClick = (e: MouseEvent) => {
+ let target = e.target as HTMLDivElement;
+ target = findUpTag(target, 'DIV');
+
+ let fileID = target.dataset.docID;
+ if(appImManager.chatInputC.sendMessageWithDocument(fileID)) {
+ /* dropdown.classList.remove('active');
+ toggleEl.classList.remove('active'); */
+ } else {
+ console.warn('got no doc by id:', fileID);
+ }
+ };
+}
+
+const emoticonsDropdown = new EmoticonsDropdown();
+// @ts-ignore
+if(process.env.NODE_ENV != 'production') {
+ (window as any).emoticonsDropdown = emoticonsDropdown;
+}
+export default emoticonsDropdown;
diff --git a/src/components/emoticonsDropdown_old.ts b/src/components/emoticonsDropdown_old.ts
new file mode 100644
index 00000000..50bdd20d
--- /dev/null
+++ b/src/components/emoticonsDropdown_old.ts
@@ -0,0 +1,526 @@
+import { AppImManager } from "../lib/appManagers/appImManager";
+import { AppMessagesManager } from "../lib/appManagers/appMessagesManager";
+import { horizontalMenu, renderImageFromUrl } 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 { wrapSticker } from "./wrappers";
+import appDocsManager from "../lib/appManagers/appDocsManager";
+import ProgressivePreloader from "./preloader";
+import Config from "../lib/config";
+import { MTDocument } from "../types";
+import animationIntersector from "./animationIntersector";
+import appSidebarRight from "../lib/appManagers/appSidebarRight";
+
+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.style.display = '';
+ void dropdown.offsetLeft; // reflow
+ dropdown.classList.add('active'); // need
+
+ let lazyLoadQueue = new LazyLoadQueue();
+
+ const searchButton = dropdown.querySelector('.emoji-tabs-search');
+ searchButton.addEventListener('click', () => {
+ appSidebarRight.stickersTab.init();
+ });
+
+ let container = pageEl.querySelector('.emoji-container .tabs-container') as HTMLDivElement;
+ let tabs = pageEl.querySelector('.emoji-dropdown .emoji-tabs') as HTMLUListElement;
+ let tabID = -1;
+ horizontalMenu(tabs, container, (id) => {
+ animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
+
+ tabID = id;
+ }, () => {
+ if(tabID == 1 && stickersInit) {
+ stickersInit();
+ } else if(tabID == 2 && gifsInit) {
+ gifsInit();
+ }
+
+ animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
+ });
+
+ (tabs.firstElementChild.children[1] as HTMLLIElement).click(); // set emoji tab
+
+ 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(() => {
+ animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
+ }, 100);
+
+ /* window.requestAnimationFrame(() => {
+ window.requestAnimationFrame(() => {
+ lottieLoader.checkAnimations(true, EMOTICONSSTICKERGROUP);
+ });
+ }); */
+ });
+ };
+
+ 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;
+ };
+
+ {
+ const categories = ["Smileys & Emotion", "Animals & Nature", "Food & Drink", "Travel & Places", "Activities", "Objects", /* "Symbols", */"Flags", "Skin Tones"];
+ let divs: {
+ [category: string]: HTMLDivElement
+ } = {};
+
+ let sorted: {
+ [category: string]: string[]
+ } = {};
+
+ for(let emoji in Config.Emoji) {
+ let details = Config.Emoji[emoji];
+ let i = '' + details;
+ let 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(let category in sorted) {
+ let div = document.createElement('div');
+ div.classList.add('emoji-category');
+
+ let titleDiv = document.createElement('div');
+ titleDiv.classList.add('category-title');
+ titleDiv.innerText = category;
+
+ let itemsDiv = document.createElement('div');
+ itemsDiv.classList.add('category-items');
+
+ div.append(titleDiv, itemsDiv);
+
+ let emojis = sorted[category];
+ emojis.forEach(emoji => {
+ //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(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);
+ itemsDiv.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.previousElementSibling.firstElementChild as HTMLUListElement;
+ let emojiScroll = new Scrollable(contentEmojiDiv, 'y', '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;
+ if(appImManager.chatInputC.sendMessageWithDocument(fileID)) {
+ 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.previousElementSibling 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, categoryTitle: string, docs: MTDocument[], prepend?: boolean) => {
+ //if((docs.length % 5) != 0) categoryDiv.classList.add('not-full');
+
+ let itemsDiv = document.createElement('div');
+ itemsDiv.classList.add('category-items');
+
+ let titleDiv = document.createElement('div');
+ titleDiv.classList.add('category-title');
+ titleDiv.innerText = categoryTitle;
+
+ categoryDiv.append(titleDiv, itemsDiv);
+
+ docs.forEach(doc => {
+ let div = document.createElement('div');
+ wrapSticker({
+ doc,
+ div,
+ lazyLoadQueue,
+ group: EMOTICONSSTICKERGROUP,
+ onlyThumb: true
+ });
+
+ itemsDiv.append(div);
+ });
+
+ if(prepend) stickersScroll.prepend(categoryDiv);
+ else stickersScroll.append(categoryDiv);
+
+ /* 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) as HTMLElement[];
+ 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', 'STICKERS', undefined, undefined, 2);
+ stickersScroll.container.addEventListener('scroll', (e) => {
+ animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
+
+ prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll);
+ });
+ stickersScroll.setVirtualContainer(stickersDiv);
+
+ emoticonsMenuOnClick(menu, heights, stickersScroll, menuScroll);
+
+ stickersInit = null;
+
+ Promise.all([
+ appStickersManager.getRecentStickers().then(stickers => {
+ let categoryDiv = document.createElement('div');
+ categoryDiv.classList.add('sticker-category');
+
+ //stickersScroll.prepend(categoryDiv);
+
+ categoryPush(categoryDiv, 'Recent', 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.loadAnimationWorker({
+ container: li,
+ loop: true,
+ autoplay: false,
+ animationData: JSON.parse(json),
+ width: 40,
+ height: 40
+ }, EMOTICONSSTICKERGROUP);
+ });
+
+ reader.readAsArrayBuffer(blob);
+ } else {
+ let image = new Image();
+ renderImageFromUrl(image, URL.createObjectURL(blob));
+
+ li.append(image);
+ }
+ });
+ } else { // as thumb will be used first sticker
+ wrapSticker({
+ doc: stickerSet.documents[0],
+ div: li as any,
+ group: EMOTICONSSTICKERGROUP
+ }); // kostil
+ }
+
+ categoryPush(categoryDiv, stickerSet.set.title, 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', 'GIFS', null);
+
+ 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;
diff --git a/src/components/lazyLoadQueue.ts b/src/components/lazyLoadQueue.ts
index 89ea4c7f..9c51d8a9 100644
--- a/src/components/lazyLoadQueue.ts
+++ b/src/components/lazyLoadQueue.ts
@@ -8,8 +8,7 @@ type LazyLoadElement = {
export default class LazyLoadQueue {
private lazyLoadMedia: Array = [];
- private loadingMedia = 0;
- private tempID = 0;
+ private inProcess: Array = [];
private lockPromise: Promise = null;
private unlockResolve: () => void = null;
@@ -25,28 +24,32 @@ export default class LazyLoadQueue {
this.observer = new IntersectionObserver(entries => {
if(this.lockPromise) return;
- for(const entry of entries) {
- if(entry.isIntersecting) {
- const target = entry.target as HTMLElement;
+ const intersecting = entries.filter(entry => entry.isIntersecting);
+ intersecting.forEachReverse(entry => {
+ const target = entry.target as HTMLElement;
- this.log('isIntersecting', target);
+ this.log('isIntersecting', target);
- // need for set element first if scrolled
- const item = this.lazyLoadMedia.findAndSplice(i => i.div == target);
- if(item) {
- item.wasSeen = true;
- this.lazyLoadMedia.unshift(item);
- this.processQueue(item);
- }
+ // need for set element first if scrolled
+ const item = this.lazyLoadMedia.findAndSplice(i => i.div == target);
+ if(item) {
+ item.wasSeen = true;
+ this.lazyLoadMedia.unshift(item);
+ //this.processQueue(item);
}
+ });
+
+ if(intersecting.length) {
+ this.processQueue();
}
});
}
public clear() {
- this.tempID--;
this.lazyLoadMedia.length = 0;
- this.loadingMedia = 0;
+ for(let item of this.inProcess) {
+ this.lazyLoadMedia.push(item);
+ }
if(this.observer) {
this.observer.disconnect();
@@ -54,7 +57,7 @@ export default class LazyLoadQueue {
}
public length() {
- return this.lazyLoadMedia.length + this.loadingMedia;
+ return this.lazyLoadMedia.length + this.inProcess.length;
}
public lock() {
@@ -72,7 +75,7 @@ export default class LazyLoadQueue {
}
public async processQueue(item?: LazyLoadElement) {
- if(this.parallelLimit > 0 && this.loadingMedia >= this.parallelLimit) return;
+ if(this.parallelLimit > 0 && this.inProcess.length >= this.parallelLimit) return;
if(item) {
this.lazyLoadMedia.findAndSplice(i => i == item);
@@ -81,9 +84,7 @@ export default class LazyLoadQueue {
}
if(item) {
- this.loadingMedia++;
-
- let tempID = this.tempID;
+ this.inProcess.push(item);
this.log('will load media', this.lockPromise, item);
@@ -95,15 +96,14 @@ export default class LazyLoadQueue {
this.log('waited lock:', performance.now() - perf);
}
+ //await new Promise((resolve) => setTimeout(resolve, 2e3));
//await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));
await item.load();
} catch(err) {
this.log.error('loadMediaQueue error:', err, item);
}
- if(tempID == this.tempID) {
- this.loadingMedia--;
- }
+ this.inProcess.findAndSplice(i => i == item);
this.log('loaded media', item);
diff --git a/src/components/misc.ts b/src/components/misc.ts
index 1f2f10bd..824340e3 100644
--- a/src/components/misc.ts
+++ b/src/components/misc.ts
@@ -18,7 +18,7 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise {
let elapsedTime = Date.now() - startTime;
@@ -328,8 +328,11 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
if(activeStripe) {
const tabsRect = tabs.getBoundingClientRect();
- const textRect = target.firstElementChild.getBoundingClientRect();
- activeStripe.style.cssText = `width: ${textRect.width + (2 * 2)}px; transform: translateX(${textRect.left - tabsRect.left}px);`;
+ const targetRect = target.getBoundingClientRect();
+ const width = 50;
+ activeStripe.style.cssText = `width: ${width}px; transform: translateX(${targetRect.left - tabsRect.left + ((targetRect.width - width) / 2)}px);`;
+ /* const textRect = target.firstElementChild.getBoundingClientRect();
+ activeStripe.style.cssText = `width: ${textRect.width + (2 * 2)}px; transform: translateX(${textRect.left - tabsRect.left}px);`; */
//activeStripe.style.transform = `scaleX(${textRect.width}) translateX(${(textRect.left - tabsRect.left) / textRect.width + 0.5}px)`;
//console.log('tabs click:', tabsRect, textRect);
}
@@ -362,9 +365,9 @@ export function formatPhoneNumber(str: string) {
}
});
- if(country.pattern) {
+ /* if(country.pattern) {
str = str.slice(0, country.pattern.length);
- }
+ } */
}
return {formatted: str, country};
diff --git a/src/components/poll.ts b/src/components/poll.ts
index c2a1bc33..7e461310 100644
--- a/src/components/poll.ts
+++ b/src/components/poll.ts
@@ -1,7 +1,11 @@
import appPollsManager, { PollResults, Poll } from "../lib/appManagers/appPollsManager";
import { RichTextProcessor } from "../lib/richtextprocessor";
-import { findUpClassName, $rootScope } from "../lib/utils";
-import { mediaSizes } from "../lib/config";
+import { findUpClassName, $rootScope, cancelEvent } from "../lib/utils";
+import { mediaSizes, touchSupport } from "../lib/config";
+import { ripple } from "./misc";
+import appSidebarRight from "../lib/appManagers/appSidebarRight";
+import appImManager from "../lib/appManagers/appImManager";
+import serverTimeManager from "../lib/mtproto/serverTimeManager";
let lineTotalLength = 0;
const tailLength = 9;
@@ -9,13 +13,13 @@ const times = 10;
const fullTime = 340;
const oneTime = fullTime / times;
-let roundPercents = (percents: number[]) => {
+export const roundPercents = (percents: number[]) => {
//console.log('roundPercents before percents:', percents);
- let sum = percents.reduce((acc, p) => acc + Math.round(p), 0);
+ const sum = percents.reduce((acc, p) => acc + Math.round(p), 0);
if(sum > 100) {
- let diff = sum - 100;
- let length = percents.length;
+ const diff = sum - 100;
+ const length = percents.length;
for(let i = 0; i < diff; ++i) {
let minIndex = -1, minRemainder = 1;
for(let k = 0; k < length; ++k) {
@@ -27,14 +31,15 @@ let roundPercents = (percents: number[]) => {
}
if(minIndex == -1) {
- throw new Error('lol chto');
+ //throw new Error('lol chto');
+ return;
}
percents[minIndex] -= minRemainder;
}
} else if(sum < 100) {
- let diff = 100 - sum;
- let length = percents.length;
+ const diff = 100 - sum;
+ const length = percents.length;
for(let i = 0; i < diff; ++i) {
let minIndex = -1, maxRemainder = 0;
for(let k = 0; k < length; ++k) {
@@ -46,7 +51,8 @@ let roundPercents = (percents: number[]) => {
}
if(minIndex == -1) {
- throw new Error('lol chto');
+ //throw new Error('lol chto');
+ return;
}
percents[minIndex] += 1 - maxRemainder;
@@ -58,32 +64,103 @@ let roundPercents = (percents: number[]) => {
const connectedPolls: {id: string, element: PollElement}[] = [];
$rootScope.$on('poll_update', (e: CustomEvent) => {
- let {poll, results} = e.detail as {poll: Poll, results: PollResults};
+ const {poll, results} = e.detail as {poll: Poll, results: PollResults};
- for(let connected of connectedPolls) {
+ //console.log('poll_update', poll, results);
+ for(const connected of connectedPolls) {
if(connected.id == poll.id) {
- let pollElement = connected.element;
- pollElement.performResults(results, poll.chosenIndex);
+ const pollElement = connected.element;
+ pollElement.isClosed = !!poll.pFlags.closed;
+ pollElement.performResults(results, poll.chosenIndexes);
}
}
});
+$rootScope.$on('peer_changed', () => {
+ if(prevQuizHint) {
+ hideQuizHint(prevQuizHint, prevQuizHintOnHide, prevQuizHintTimeout);
+ }
+});
+
+const hideQuizHint = (element: HTMLElement, onHide: () => void, timeout: number) => {
+ element.classList.remove('active');
+
+ clearTimeout(timeout);
+ setTimeout(() => {
+ onHide();
+ element.remove();
+
+ if(prevQuizHint == element && prevQuizHintOnHide == onHide && prevQuizHintTimeout == timeout) {
+ prevQuizHint = prevQuizHintOnHide = null;
+ prevQuizHintTimeout = 0;
+ }
+ }, 200);
+};
+
+let prevQuizHint: HTMLElement, prevQuizHintOnHide: () => void, prevQuizHintTimeout: number;
+const setQuizHint = (solution: string, solution_entities: any[], onHide: () => void) => {
+ if(prevQuizHint) {
+ hideQuizHint(prevQuizHint, prevQuizHintOnHide, prevQuizHintTimeout);
+ }
+
+ const element = document.createElement('div');
+ element.classList.add('quiz-hint');
+
+ const container = document.createElement('div');
+ container.classList.add('container', 'tgico');
+
+ const textEl = document.createElement('div');
+ textEl.classList.add('text');
+
+ container.append(textEl);
+ element.append(container);
+
+ textEl.innerHTML = RichTextProcessor.wrapRichText(solution, {entities: solution_entities});
+ appImManager.bubblesContainer.append(element);
+
+ void element.offsetLeft; // reflow
+ element.classList.add('active');
+
+ prevQuizHint = element;
+ prevQuizHintOnHide = onHide;
+ prevQuizHintTimeout = setTimeout(() => {
+ hideQuizHint(element, onHide, prevQuizHintTimeout);
+ }, touchSupport ? 5000 : 7000);
+};
+
export default class PollElement extends HTMLElement {
private svgLines: SVGSVGElement[];
private numberDivs: HTMLDivElement[];
- private selectedSpan: HTMLSpanElement;
private answerDivs: HTMLDivElement[];
+ private descDiv: HTMLElement;
+ private typeDiv: HTMLElement;
+ private avatarsDiv: HTMLElement;
+ private viewResults: HTMLElement;
private votersCountDiv: HTMLDivElement;
private maxOffset = -46.5;
private maxLength: number;
private maxLengths: number[];
+ public isClosed = false;
private isQuiz = false;
private isRetracted = false;
- private chosenIndex = -1;
+ private isPublic = false;
+ private isMultiple = false;
+ private chosenIndexes: number[] = [];
private percents: number[];
+ private pollID: string;
+ private mid: number;
+
+ private quizInterval: number;
+ private quizTimer: SVGSVGElement;
+
+ private sendVoteBtn: HTMLElement;
+ private chosingIndexes: number[] = [];
+
+ private sendVotePromise: Promise;
+
constructor() {
super();
// элемент создан
@@ -98,28 +175,32 @@ export default class PollElement extends HTMLElement {
console.log('line total length:', lineTotalLength);
}
- let pollID = this.getAttribute('poll-id');
- let {poll, results} = appPollsManager.getPoll(pollID);
+ this.pollID = this.getAttribute('poll-id');
+ this.mid = +this.getAttribute('message-id');
+ const {poll, results} = appPollsManager.getPoll(this.pollID);
- connectedPolls.push({id: pollID, element: this});
+ connectedPolls.push({id: this.pollID, element: this});
console.log('pollElement poll:', poll, results);
let desc = '';
if(poll.pFlags) {
- if(poll.pFlags.closed) {
+ this.isPublic = !!poll.pFlags.public_voters;
+ this.isQuiz = !!poll.pFlags.quiz;
+ this.isClosed = !!poll.pFlags.closed;
+ this.isMultiple = !!poll.pFlags.multiple_choice;
+
+ if(this.isClosed) {
desc = 'Final results';
+ this.classList.add('is-closed');
} else {
- if(poll.pFlags.quiz) {
- this.isQuiz = true;
- }
-
let type = this.isQuiz ? 'Quiz' : 'Poll';
- desc = (poll.pFlags.public_voters ? 'Public' : 'Anonymous') + ' ' + type;
+ desc = (this.isPublic ? '' : 'Anonymous ') + type;
}
}
- let votes = poll.answers.map((answer, idx) => {
+ const multipleSelect = this.isMultiple ? ' ' : '';
+ const votes = poll.answers.map((answer, idx) => {
return `
@@ -127,34 +208,127 @@ export default class PollElement extends HTMLElement {
+ ${multipleSelect}
${RichTextProcessor.wrapEmojiText(answer.text)}
+
`;
}).join('');
this.innerHTML = `
${poll.rQuestion}
- ${desc}
+
${votes}
-
+
`;
+ this.descDiv = this.firstElementChild.nextElementSibling as HTMLElement;
+ this.typeDiv = this.descDiv.firstElementChild as HTMLElement;
+ this.avatarsDiv = this.descDiv.lastElementChild as HTMLElement;
+
+ if(this.isQuiz) {
+ this.classList.add('is-quiz');
+
+ if(poll.close_period && poll.close_date) {
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ //svg.setAttributeNS(null, 'viewBox', '0 0 15 15');
+ svg.classList.add('poll-quiz-timer');
+
+ this.quizTimer = svg;
+
+ const strokeWidth = 2;
+ const radius = (15 / 2) - (strokeWidth * 2);
+ const circumference = 2 * Math.PI * radius;
+
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
+ circle.classList.add('poll-quiz-timer-circle');
+ circle.setAttributeNS(null, 'cx', '15');
+ circle.setAttributeNS(null, 'cy', '15');
+ circle.setAttributeNS(null, 'r', '' + radius);
+
+ svg.append(circle);
+
+ this.descDiv.append(svg);
+
+ const period = poll.close_period * 1000;
+ const closeTime = (poll.close_date - serverTimeManager.serverTimeOffset) * 1000;
+ this.quizInterval = setInterval(() => {
+ const time = Date.now();
+
+ const totalLength = circle.getTotalLength();
+ const percents = (closeTime - time) / period;
+ circle.style.strokeDasharray = '' + (percents * totalLength) + ', ' + circumference;
+
+ if(time >= closeTime) {
+ clearInterval(this.quizInterval);
+ this.quizInterval = 0;
+
+ // нужно запросить апдейт чтобы опрос обновился
+ appPollsManager.getResults(this.mid);
+ }
+ }, 1e3);
+ }
+ }
+
this.answerDivs = Array.from(this.querySelectorAll('.poll-answer')) as HTMLDivElement[];
- this.votersCountDiv = this.querySelector('.poll-votes-count') as HTMLDivElement;
this.svgLines = Array.from(this.querySelectorAll('.poll-line')) as SVGSVGElement[];
this.numberDivs = Array.from(this.querySelectorAll('.poll-answer-percents')) as HTMLDivElement[];
- let width = this.getBoundingClientRect().width;
+ const footerDiv = this.lastElementChild;
+ this.viewResults = footerDiv.firstElementChild as HTMLElement;
+ this.votersCountDiv = footerDiv.lastElementChild as HTMLDivElement;
+
+ this.viewResults.addEventListener('click', (e) => {
+ cancelEvent(e);
+ appSidebarRight.pollResultsTab.init(this.pollID, this.mid);
+ });
+ ripple(this.viewResults);
+
+ if(this.isMultiple) {
+ this.sendVoteBtn = document.createElement('div');
+ this.sendVoteBtn.classList.add('poll-footer-button', 'poll-send-vote');
+ this.sendVoteBtn.innerText = 'Vote';
+ ripple(this.sendVoteBtn);
+
+ if(!poll.chosenIndexes.length) {
+ this.votersCountDiv.classList.add('hide');
+ }
+
+ this.sendVoteBtn.addEventListener('click', () => {
+ /* const indexes = this.answerDivs.filter(el => el.classList.contains('is-chosing')).map(el => +el.dataset.index);
+ if(indexes.length) {
+
+ } */
+ if(this.chosingIndexes.length) {
+ this.sendVotes(this.chosingIndexes).then(() => {
+ this.chosingIndexes.length = 0;
+ this.answerDivs.forEach(el => {
+ el.classList.remove('is-chosing');
+ });
+ });
+ }
+ });
+
+ footerDiv.append(this.sendVoteBtn);
+ }
+
+ const width = this.getBoundingClientRect().width;
this.maxLength = width + tailLength + this.maxOffset + -13.7; // 13 - position left
- if(poll.chosenIndex !== -1) {
- this.performResults(results, poll.chosenIndex);
- } else {
+ if(poll.chosenIndexes.length || this.isClosed) {
+ this.performResults(results, poll.chosenIndexes);
+ } else if(!this.isClosed) {
this.setVotersCount(results);
this.addEventListener('click', this.clickHandler);
}
@@ -168,11 +342,16 @@ export default class PollElement extends HTMLElement {
}
static get observedAttributes(): string[] {
- return [/* массив имён атрибутов для отслеживания их изменений */];
+ return ['poll-id', 'message-id'/* массив имён атрибутов для отслеживания их изменений */];
}
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
// вызывается при изменении одного из перечисленных выше атрибутов
+ if(name == 'poll-id') {
+ this.pollID = newValue;
+ } else if(name == 'message-id') {
+ this.mid = +newValue;
+ }
}
adoptedCallback() {
@@ -180,14 +359,45 @@ export default class PollElement extends HTMLElement {
// (происходит в document.adoptNode, используется очень редко)
}
+ initQuizHint(results: PollResults) {
+ if(results.solution && results.solution_entities) {
+ const toggleHint = document.createElement('div');
+ toggleHint.classList.add('tgico-tip', 'poll-hint');
+ this.descDiv.append(toggleHint);
+
+ //let active = false;
+ toggleHint.addEventListener('click', (e) => {
+ cancelEvent(e);
+
+ //active = true;
+ toggleHint.classList.add('active');
+ setQuizHint(results.solution, results.solution_entities, () => {
+ //active = false;
+ toggleHint.classList.remove('active');
+ });
+ });
+ }
+ }
+
clickHandler(e: MouseEvent) {
- let target = findUpClassName(e.target, 'poll-answer') as HTMLElement;
+ const target = findUpClassName(e.target, 'poll-answer') as HTMLElement;
if(!target) {
return;
}
- let answerIndex = +target.dataset.index;
- this.sendVote(answerIndex);
+ const answerIndex = +target.dataset.index;
+ if(this.isMultiple) {
+ target.classList.toggle('is-chosing');
+
+ const foundIndex = this.chosingIndexes.indexOf(answerIndex);
+ if(foundIndex !== -1) {
+ this.chosingIndexes.splice(foundIndex, 1);
+ } else {
+ this.chosingIndexes.push(answerIndex);
+ }
+ } else {
+ this.sendVotes([answerIndex]);
+ }
/* target.classList.add('is-voting');
setTimeout(() => { // simulate
@@ -196,23 +406,57 @@ export default class PollElement extends HTMLElement {
}, 1000); */
}
- sendVote(index: number) {
- let target = this.answerDivs[index];
- target.classList.add('is-voting');
- let mid = +this.getAttribute('message-id');
+ sendVotes(indexes: number[]) {
+ if(this.sendVotePromise) return this.sendVotePromise;
+
+ const targets = this.answerDivs.filter((_, idx) => indexes.includes(idx));
+ targets.forEach(target => {
+ target.classList.add('is-voting');
+ });
this.classList.add('disable-hover');
- appPollsManager.sendVote(mid, [index]).then(() => {
- target.classList.remove('is-voting');
+ return this.sendVotePromise = appPollsManager.sendVote(this.mid, indexes).then(() => {
+ targets.forEach(target => {
+ target.classList.remove('is-voting');
+ });
+
this.classList.remove('disable-hover');
+ }).finally(() => {
+ this.sendVotePromise = null;
});
}
- performResults(results: PollResults, chosenIndex: number) {
- if(this.chosenIndex != chosenIndex) { // if we voted
- this.isRetracted = this.chosenIndex != -1 && chosenIndex == -1;
- this.chosenIndex = chosenIndex;
-
+ performResults(results: PollResults, chosenIndexes: number[]) {
+ if(this.isQuiz && (results.results?.length || this.isClosed)) {
+ this.answerDivs.forEach((el, idx) => {
+ el.classList.toggle('is-correct', !!results.results[idx].pFlags.correct);
+ });
+
+ if(this.initQuizHint) {
+ this.initQuizHint(results);
+ delete this.initQuizHint;
+ }
+
+ if(this.quizInterval) {
+ clearInterval(this.quizInterval);
+ this.quizInterval = 0;
+ }
+
+ if(this.quizTimer?.parentElement) {
+ this.quizTimer.remove();
+ }
+ }
+
+ if(this.isClosed) {
+ this.classList.add('is-closed');
+ this.typeDiv.innerText = 'Final results';
+ }
+
+ // set chosen
+ if(this.chosenIndexes.length != chosenIndexes.length || this.isClosed) { // if we voted
+ this.isRetracted = this.chosenIndexes.length && !chosenIndexes.length;
+ this.chosenIndexes = chosenIndexes.slice();
+
if(this.isRetracted) {
this.addEventListener('click', this.clickHandler);
} else {
@@ -221,29 +465,56 @@ export default class PollElement extends HTMLElement {
}
// is need update
- if(this.chosenIndex != -1 || this.isRetracted) {
- const percents = results.results.map(v => v.voters / results.total_voters * 100);
- this.setResults(this.isRetracted ? this.percents : percents, chosenIndex);
+ if(this.chosenIndexes.length || this.isRetracted || this.isClosed) {
+ const percents = results.results.map(v => results.total_voters ? v.voters / results.total_voters * 100 : 0);
+ this.setResults(this.isRetracted ? this.percents : percents, this.chosenIndexes);
this.percents = percents;
this.isRetracted = false;
}
this.setVotersCount(results);
- }
- setResults(percents: number[], chosenIndex: number) {
- this.svgLines.forEach(svg => svg.style.display = '');
+ if(this.isPublic) {
+ if(!this.isMultiple) {
+ this.viewResults.classList.toggle('hide', !results.total_voters || !this.chosenIndexes.length);
+ this.votersCountDiv.classList.toggle('hide', !!this.chosenIndexes.length);
+ }
+
+ let html = '';
+ /**
+ * MACOS, ANDROID - без реверса
+ * WINDOWS DESKTOP - реверс
+ * все приложения накладывают аватарку первую на вторую, а в макете зато вторая на первую, ЛОЛ!
+ */
+ results.recent_voters/* .slice().reverse() */.forEach((userID, idx) => {
+ const style = idx == 0 ? '' : `style="transform: translateX(-${idx * 5}px);"`;
+ html += ` `;
+ });
+ this.avatarsDiv.innerHTML = html;
+ }
- if(chosenIndex !== -1) {
- let answerDiv = this.answerDivs[chosenIndex];
- if(!this.selectedSpan) {
- this.selectedSpan = document.createElement('span');
- this.selectedSpan.classList.add('poll-answer-selected', 'tgico-check');
+ if(this.isMultiple) {
+ this.sendVoteBtn.classList.toggle('hide', !!this.chosenIndexes.length);
+ if(!this.chosenIndexes.length) {
+ this.votersCountDiv.classList.add('hide');
+ this.viewResults.classList.add('hide');
+ } else if(this.isPublic) {
+ this.viewResults.classList.toggle('hide', !results.total_voters || !this.chosenIndexes.length);
+ this.votersCountDiv.classList.toggle('hide', !!this.chosenIndexes.length);
+ } else {
+ this.votersCountDiv.classList.toggle('hide', !this.chosenIndexes.length);
}
- answerDiv.append(this.selectedSpan);
}
+ }
+
+ setResults(percents: number[], chosenIndexes: number[]) {
+ this.svgLines.forEach(svg => svg.style.display = '');
+
+ this.answerDivs.forEach((el, idx) => {
+ el.classList.toggle('is-chosen', chosenIndexes.includes(idx));
+ });
- let maxValue = Math.max(...percents);
+ const maxValue = Math.max(...percents);
this.maxLengths = percents.map(p => p / maxValue * this.maxLength);
// line
@@ -265,7 +536,7 @@ export default class PollElement extends HTMLElement {
for(let i = (times - 1), k = 0; i >= 0; --i, ++k) {
setTimeout(() => {
percents.forEach((percents, idx) => {
- let value = Math.round(percents / times * i);
+ const value = Math.round(percents / times * i);
this.numberDivs[idx].innerText = value + '%';
});
}, oneTime * k);
@@ -274,7 +545,7 @@ export default class PollElement extends HTMLElement {
for(let i = 0; i < times; ++i) {
setTimeout(() => {
percents.forEach((percents, idx) => {
- let value = Math.round(percents / times * (i + 1));
+ const value = Math.round(percents / times * (i + 1));
this.numberDivs[idx].innerText = value + '%';
});
}, oneTime * i);
@@ -294,14 +565,14 @@ export default class PollElement extends HTMLElement {
}
setVotersCount(results: PollResults) {
- let votersCount = results.total_voters || 0;
- let votersOrAnswers = this.isQuiz ? (votersCount > 1 || !votersCount ? 'answers' : 'answer') : (votersCount > 1 || !votersCount ? 'votes' : 'vote');
+ const votersCount = results.total_voters || 0;
+ const votersOrAnswers = this.isQuiz ? (votersCount > 1 || !votersCount ? 'answers' : 'answer') : (votersCount > 1 || !votersCount ? 'votes' : 'vote');
this.votersCountDiv.innerText = `${results.total_voters ? results.total_voters + ' ' + votersOrAnswers : 'No ' + votersOrAnswers}`;
}
setLineProgress(index: number, percents: number) {
- let svg = this.svgLines[index];
+ const svg = this.svgLines[index];
if(percents == -1) {
svg.style.strokeDasharray = '';
diff --git a/src/components/popup.ts b/src/components/popup.ts
index ee5ce780..0245165d 100644
--- a/src/components/popup.ts
+++ b/src/components/popup.ts
@@ -6,8 +6,14 @@ export class PopupElement {
protected container = document.createElement('div');
protected header = document.createElement('div');
protected title = document.createElement('div');
+ protected closeBtn: HTMLElement;
+ protected confirmBtn: HTMLElement;
+ protected body: HTMLElement;
- constructor(className: string, buttons?: Array) {
+ protected onClose: () => void;
+ protected onCloseAfterTimeout: () => void;
+
+ constructor(className: string, buttons?: Array, options: Partial<{closable: boolean, withConfirm: string, body: boolean}> = {}) {
this.element.classList.add('popup');
this.element.className = 'popup' + (className ? ' ' + className : '');
this.container.classList.add('popup-container', 'z-depth-1');
@@ -16,7 +22,32 @@ export class PopupElement {
this.title.classList.add('popup-title');
this.header.append(this.title);
+
+ if(options.closable) {
+ this.closeBtn = document.createElement('span');
+ this.closeBtn.classList.add('btn-icon', 'popup-close', 'tgico-close');
+ ripple(this.closeBtn);
+ this.header.prepend(this.closeBtn);
+
+ this.closeBtn.addEventListener('click', () => {
+ this.destroy();
+ }, {once: true});
+ }
+
+ if(options.withConfirm) {
+ this.confirmBtn = document.createElement('button');
+ this.confirmBtn.classList.add('btn-primary');
+ this.confirmBtn.innerText = options.withConfirm;
+ this.header.append(this.confirmBtn);
+ ripple(this.confirmBtn);
+ }
+
this.container.append(this.header);
+ if(options.body) {
+ this.body = document.createElement('div');
+ this.body.classList.add('popup-body');
+ this.container.append(this.body);
+ }
if(buttons && buttons.length) {
const buttonsDiv = document.createElement('div');
@@ -56,9 +87,11 @@ export class PopupElement {
}
public destroy() {
+ this.onClose && this.onClose();
this.element.classList.remove('active');
setTimeout(() => {
this.element.remove();
+ this.onCloseAfterTimeout && this.onCloseAfterTimeout();
}, 1000);
}
}
diff --git a/src/components/popupCreatePoll.ts b/src/components/popupCreatePoll.ts
new file mode 100644
index 00000000..1799af11
--- /dev/null
+++ b/src/components/popupCreatePoll.ts
@@ -0,0 +1,134 @@
+import { PopupElement } from "./popup";
+import Scrollable from "./scrollable_new";
+import appMessagesManager from "../lib/appManagers/appMessagesManager";
+import { $rootScope } from "../lib/utils";
+import { Poll } from "../lib/appManagers/appPollsManager";
+import { nextRandomInt, bigint } from "../lib/bin_utils";
+import { toast } from "./misc";
+
+const InputField = (placeholder: string, label: string, name: string) => {
+ const div = document.createElement('div');
+ div.classList.add('input-field');
+
+ div.innerHTML = `
+
+ ${label}
+ `;
+
+ return div;
+};
+
+export default class PopupCreatePoll extends PopupElement {
+ private questionInput: HTMLInputElement;
+ private questions: HTMLElement;
+ private scrollable: Scrollable;
+ private tempID = 0;
+
+ constructor() {
+ super('popup-create-poll popup-new-media', null, {closable: true, withConfirm: 'CREATE', body: true});
+
+ this.title.innerText = 'New Poll';
+
+ const questionField = InputField('Ask a question', 'Ask a question', 'question');
+ this.questionInput = questionField.firstElementChild as HTMLInputElement;
+
+ this.header.append(questionField);
+
+ const d = document.createElement('div');
+ d.innerText = 'Options';
+
+ this.questions = document.createElement('div');
+ this.questions.classList.add('poll-create-questions');
+
+ this.body.append(d, this.questions);
+
+ this.confirmBtn.addEventListener('click', this.onSubmitClick);
+
+ this.scrollable = new Scrollable(this.body, 'y', undefined);
+ this.appendMoreField();
+ }
+
+ onSubmitClick = (e: MouseEvent) => {
+ const question = this.questionInput.value;
+
+ if(!question.trim()) {
+ toast('Please enter a question');
+ return;
+ }
+
+ const answers = Array.from(this.questions.children).map((el, idx) => {
+ const input = (el.firstElementChild as HTMLInputElement);
+ return input.value;
+ }).filter(v => !!v.trim());
+
+ if(answers.length < 2) {
+ toast('Please enter at least two options');
+ return;
+ }
+
+ this.closeBtn.click();
+ this.confirmBtn.removeEventListener('click', this.onSubmitClick);
+
+ //const randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)];
+ //const randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString();
+
+ const poll: Partial = {};
+ poll._ = 'poll';
+ //poll.id = randomIDS;
+ poll.flags = 0;
+ poll.question = question;
+
+ poll.answers = answers.map((value, idx) => {
+ return {
+ _: 'pollAnswer',
+ text: value,
+ option: new Uint8Array([idx])
+ };
+ });
+
+ appMessagesManager.sendOther($rootScope.selectedPeerID, {
+ _: 'inputMediaPoll',
+ flags: 0,
+ poll
+ });
+ };
+
+ onInput = (e: Event) => {
+ const target = e.target as HTMLInputElement;
+
+ if(target.value.length) {
+ target.parentElement.classList.add('is-filled');
+ }
+
+ const isLast = !target.parentElement.nextElementSibling;
+ if(isLast && target.value.length && this.questions.childElementCount < 10) {
+ this.appendMoreField();
+ }
+ };
+
+ onDeleteClick = (e: MouseEvent) => {
+ const target = e.target as HTMLSpanElement;
+ target.parentElement.remove();
+
+ Array.from(this.questions.children).forEach((el, idx) => {
+ const label = el.firstElementChild.nextElementSibling as HTMLLabelElement;
+ label.innerText = 'Option ' + (idx + 1);
+ });
+ };
+
+ private appendMoreField() {
+ const idx = this.questions.childElementCount + 1;
+ const questionField = InputField('Add an Option', 'Option ' + idx, 'question-' + this.tempID++);
+ (questionField.firstElementChild as HTMLInputElement).addEventListener('input', this.onInput);
+
+ const deleteBtn = document.createElement('span');
+ deleteBtn.classList.add('btn-icon', 'tgico-close');
+ questionField.append(deleteBtn);
+
+ deleteBtn.addEventListener('click', this.onDeleteClick, {once: true});
+
+ this.questions.append(questionField);
+
+ this.scrollable.scrollTo(this.scrollable.scrollHeight, true, true);
+ }
+}
\ No newline at end of file
diff --git a/src/components/popupStickers.ts b/src/components/popupStickers.ts
index a1ead0a1..4cb60027 100644
--- a/src/components/popupStickers.ts
+++ b/src/components/popupStickers.ts
@@ -4,17 +4,15 @@ import { RichTextProcessor } from "../lib/richtextprocessor";
import Scrollable from "./scrollable_new";
import { wrapSticker } from "./wrappers";
import LazyLoadQueue from "./lazyLoadQueue";
-import { putPreloader, ripple } from "./misc";
+import { putPreloader } from "./misc";
import animationIntersector from "./animationIntersector";
import { findUpClassName } from "../lib/utils";
-import appDocsManager from "../lib/appManagers/appDocsManager";
import appImManager from "../lib/appManagers/appImManager";
export default class PopupStickers extends PopupElement {
private stickersFooter: HTMLElement;
private stickersDiv: HTMLElement;
private h6: HTMLElement;
- private closeBtn: HTMLElement;
private set: MTStickerSet;
@@ -23,30 +21,23 @@ export default class PopupStickers extends PopupElement {
id: string,
access_hash: string
}) {
- super('popup-stickers');
+ super('popup-stickers', null, {closable: true, body: true});
this.h6 = document.createElement('h6');
this.h6.innerText = 'Loading...';
- const popupBody = document.createElement('div');
- popupBody.classList.add('popup-body');
+ this.header.append(this.h6);
- this.closeBtn = document.createElement('span');
- this.closeBtn.classList.add('btn-icon', 'popup-close', 'tgico-close');
- this.header.append(this.closeBtn, this.h6);
-
- this.closeBtn.addEventListener('click', () => {
- this.destroy();
+ this.onClose = () => {
animationIntersector.checkAnimations(false);
this.stickersFooter.removeEventListener('click', this.onFooterClick);
this.stickersDiv.removeEventListener('click', this.onStickersClick);
this.element.removeEventListener('click', onOverlayClick);
+ };
- setTimeout(() => {
- animationIntersector.checkAnimations(undefined, 'STICKERS-POPUP');
- }, 1001);
- }, {once: true});
- ripple(this.closeBtn);
+ this.onCloseAfterTimeout = () => {
+ animationIntersector.checkAnimations(undefined, 'STICKERS-POPUP');
+ };
const onOverlayClick = (e: MouseEvent) => {
if(!findUpClassName(e.target, 'popup-container')) {
@@ -71,12 +62,9 @@ export default class PopupStickers extends PopupElement {
this.stickersFooter.innerText = 'Loading...';
- popupBody.append(div);
- this.container.append(popupBody);
-
- const scrollable = new Scrollable(popupBody, 'y', undefined);
-
- popupBody.append(this.stickersFooter);
+ this.body.append(div);
+ const scrollable = new Scrollable(this.body, 'y', undefined);
+ this.body.append(this.stickersFooter);
// const editButton = document.createElement('button');
// editButton.classList.add('btn-primary');
@@ -111,11 +99,6 @@ export default class PopupStickers extends PopupElement {
private loadStickerSet() {
return appStickersManager.getStickerSet(this.stickerSetInput).then(set => {
//console.log('PopupStickers loadStickerSet got set:', set);
- //JOPA SGORELA SUKA, kak zdes mojet bit false esli u menya etot nabor dobavlen i otobrajaetsya v telege ???
- //koroche sut v tom, chto esli ti dobavil nabor a potom ctrl + f5 , i najimaesh na popup stikera v chate, to ono dumaet chto nabora net, potomu chto installed_date kakogoto huya false, i ego pravda tam net.
- //razberis brat.. a tak vrode vse rabotaet namana. ya gavnokoder i gavnoverstker
- //testiroval na stikere v dialoge viti (zeleniy dinozavr) http://i.piccy.info/i9/71cbc718bedb6d8a33da9bff775d8316/1591669743/204119/1382638/Snymok_ekrana_2020_06_09_v_05_28_25.jpg
- //console.log('hasOwnProperty got set installed_date ????', set.set.hasOwnProperty('installed_date'));
this.set = set.set;
diff --git a/src/components/preloader.ts b/src/components/preloader.ts
index 8c2d1d62..083a6252 100644
--- a/src/components/preloader.ts
+++ b/src/components/preloader.ts
@@ -58,9 +58,14 @@ export default class ProgressivePreloader {
this.promise = promise = null;
}
};
+ //promise.catch(onEnd);
promise.finally(onEnd);
promise.notify = (details: {done: number, total: number}) => {
+ /* if(details.done >= details.total) {
+ onEnd();
+ } */
+
if(tempID != this.tempID) return;
//console.log('preloader download', promise, details);
diff --git a/src/components/scrollable_new.ts b/src/components/scrollable_new.ts
index b1207c62..34c56acb 100644
--- a/src/components/scrollable_new.ts
+++ b/src/components/scrollable_new.ts
@@ -1,5 +1,6 @@
import { logger, LogLevels } from "../lib/polyfill";
import smoothscroll from '../lib/smoothscroll';
+import { touchSupport, isSafari } from "../lib/config";
//import { isInDOM } from "../lib/utils";
(window as any).__forceSmoothScrollPolyfill__ = true;
smoothscroll.polyfill();
@@ -103,22 +104,22 @@ export default class Scrollable {
this.visible = new Set();
this.observer = new IntersectionObserver(entries => {
- let filtered = entries.filter(entry => entry.isIntersecting);
+ const filtered = entries.filter(entry => entry.isIntersecting);
//return;
//this.log('entries:', entries);
entries.forEach(entry => {
- let target = entry.target as HTMLElement;
+ const target = entry.target as HTMLElement;
if(entry.isIntersecting) {
this.setVisible(target);
this.log.debug('intersection entry:', entry, this.lastTopID, this.lastBottomID);
} else {
- let id = +target.dataset.virtual;
- let isTop = entry.boundingClientRect.top < 0;
+ const id = +target.dataset.virtual;
+ const isTop = entry.boundingClientRect.top < 0;
if(isTop) {
this.lastTopID = id + 1;
@@ -159,10 +160,10 @@ export default class Scrollable {
this.log.debug('entries:', entries, filtered, this.lastScrollDirection, this.lastTopID, this.lastBottomID);
- let minVisibleID = this.lastTopID - this.splitCount;
- let maxVisibleID = this.lastBottomID + this.splitCount;
- for(let target of this.visible) {
- let id = +target.dataset.virtual;
+ const minVisibleID = this.lastTopID - this.splitCount;
+ const maxVisibleID = this.lastBottomID + this.splitCount;
+ for(const target of this.visible) {
+ const id = +target.dataset.virtual;
if(id < minVisibleID || id > maxVisibleID) {
this.setHidden(target);
}
@@ -178,9 +179,9 @@ export default class Scrollable {
if(axis == 'x') {
this.container.classList.add('scrollable-x');
- let scrollHorizontally = (e: any) => {
+ const scrollHorizontally = (e: any) => {
e = window.event || e;
- var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
+ const delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
this.container.scrollLeft -= (delta * 20);
e.preventDefault();
};
@@ -217,6 +218,27 @@ export default class Scrollable {
this.overflowContainer = window.innerWidth <= 720 && false ? document.documentElement : this.container;
+ if(touchSupport && isSafari) {
+ let allowUp: boolean, allowDown: boolean, slideBeginY: number;
+ this.container.addEventListener('touchstart', (event) => {
+ allowUp = this.container.scrollTop > 0;
+ allowDown = (this.container.scrollTop < this.container.scrollHeight - this.container.clientHeight);
+ // @ts-ignore
+ slideBeginY = event.pageY;
+ });
+
+ this.container.addEventListener('touchmove', (event: any) => {
+ var up = (event.pageY > slideBeginY);
+ var down = (event.pageY < slideBeginY);
+ slideBeginY = event.pageY;
+ if((up && allowUp) || (down && allowDown)) {
+ event.stopPropagation();
+ } else {
+ event.preventDefault();
+ }
+ });
+ }
+
/* scrollables.set(this.container, this);
scrollsIntersector.observe(this.container); */
}
@@ -323,7 +345,7 @@ export default class Scrollable {
this.onScrollMeasure = 0;
if(!this.splitUp) return;
- let scrollTop = this.overflowContainer.scrollTop;
+ const scrollTop = this.overflowContainer.scrollTop;
if(this.lastScrollTop != scrollTop) {
this.lastScrollDirection = this.lastScrollTop < scrollTop ? 1 : -1;
this.lastScrollTop = scrollTop;
@@ -336,8 +358,8 @@ export default class Scrollable {
public checkForTriggers(container: HTMLElement) {
if(this.scrollLocked || (!this.onScrolledTop && !this.onScrolledBottom)) return;
- let scrollTop = container.scrollTop;
- let maxScrollTop = container.scrollHeight - container.clientHeight;
+ const scrollTop = container.scrollTop;
+ const maxScrollTop = container.scrollHeight - container.clientHeight;
//this.log('checkForTriggers:', scrollTop, maxScrollTop);
@@ -359,7 +381,7 @@ export default class Scrollable {
public updateElement(element: HTMLElement) {
element.style.minHeight = '';
window.requestAnimationFrame(() => {
- let height = element.scrollHeight;
+ const height = element.scrollHeight;
window.requestAnimationFrame(() => {
element.style.minHeight = height + 'px';
@@ -368,12 +390,13 @@ export default class Scrollable {
}
public prepareElement(element: HTMLElement, append = true) {
+ //return;
element.dataset.virtual = '' + (append ? this.virtualTempIDBottom++ : this.virtualTempIDTop--);
this.log.debug('prepareElement: prepared');
window.requestAnimationFrame(() => {
- let {scrollHeight/* , scrollWidth */} = element;
+ const {scrollHeight/* , scrollWidth */} = element;
this.log.debug('prepareElement: first rAF');
@@ -413,7 +436,7 @@ export default class Scrollable {
public scrollIntoView(element: HTMLElement, smooth = true) {
if(element.parentElement && !this.scrollLocked) {
- let isFirstUnread = element.classList.contains('is-first-unread');
+ const isFirstUnread = element.classList.contains('is-first-unread');
let offsetTop = element.getBoundingClientRect().top - this.container.getBoundingClientRect().top;
offsetTop = this.container.scrollTop + offsetTop;
@@ -423,10 +446,10 @@ export default class Scrollable {
return;
}
- let clientHeight = this.container.clientHeight;
- let height = element.scrollHeight;
+ const clientHeight = this.container.clientHeight;
+ const height = element.scrollHeight;
- let d = (clientHeight - height) / 2;
+ const d = (clientHeight - height) / 2;
offsetTop -= d;
this.scrollTo(offsetTop, smooth);
@@ -436,7 +459,7 @@ export default class Scrollable {
public scrollTo(top: number, smooth = true, important = false) {
if(this.scrollLocked && !important) return;
- let scrollTop = this.scrollTop;
+ const scrollTop = this.scrollTop;
if(scrollTop == Math.floor(top)) {
return;
}
diff --git a/src/components/slider.ts b/src/components/slider.ts
index e464798f..293f0b80 100644
--- a/src/components/slider.ts
+++ b/src/components/slider.ts
@@ -14,7 +14,7 @@ export default class SidebarSlider {
this._selectTab(0);
let onCloseBtnClick = () => {
- console.log('sidebar-close-button click:', this.historyTabIDs);
+ //console.log('sidebar-close-button click:', this.historyTabIDs);
let closingID = this.historyTabIDs.pop(); // pop current
this.onCloseTab(closingID);
this._selectTab(this.historyTabIDs[this.historyTabIDs.length - 1] || 0);
@@ -25,6 +25,10 @@ export default class SidebarSlider {
}
public selectTab(id: number) {
+ if(this.historyTabIDs[this.historyTabIDs.length - 1] == id) {
+ return;
+ }
+
this.historyTabIDs.push(id);
this._selectTab(id);
}
diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts
index fe1a2445..70368cdf 100644
--- a/src/components/wrappers.ts
+++ b/src/components/wrappers.ts
@@ -18,6 +18,7 @@ import { mediaSizes } from '../lib/config';
import { MTDocument, MTPhotoSize } from '../types';
import animationIntersector from './animationIntersector';
import AudioElement from './audio';
+import MP4Source from '../lib/MP4Sourcee';
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue}: {
doc: MTDocument,
@@ -30,6 +31,23 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
middleware: () => boolean,
lazyLoadQueue: LazyLoadQueue
}) {
+ let span: HTMLSpanElement, spanPlay: HTMLSpanElement;
+ if(doc.type != 'round') {
+ span = document.createElement('span');
+ span.classList.add('video-time');
+ container.append(span);
+
+ if(doc.type != 'gif') {
+ span.innerText = (doc.duration + '').toHHMMSS(false);
+
+ spanPlay = document.createElement('span');
+ spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
+ container.append(spanPlay);
+ } else {
+ span.innerText = 'GIF';
+ }
+ }
+
if(doc.type == 'video') {
return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware);
}
@@ -55,19 +73,16 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
}
}
- let video = document.createElement('video');
- let source = document.createElement('source');
- video.append(source);
-
- if(doc.type == 'gif') {
- video.addEventListener('loadeddata', () => {
- video.pause();
- animationIntersector.addAnimation(video, 'chat');
- }, {once: true});
+ if(img) {
+ img.classList.add('thumbnail');
}
+
+ const video = document.createElement('video');
+ const source = document.createElement('source');
+ video.append(source);
if(withTail) {
- let foreignObject = img.parentElement;
+ const foreignObject = img.parentElement;
video.width = +foreignObject.getAttributeNS(null, 'width');
video.height = +foreignObject.getAttributeNS(null, 'height');
foreignObject.append(video);
@@ -75,31 +90,26 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
container.append(video);
}
- let span: HTMLSpanElement, spanPlay: HTMLSpanElement;
- if(doc.type != 'round') {
- span = document.createElement('span');
- span.classList.add('video-time');
- container.append(span);
-
- if(doc.type != 'gif') {
- span.innerText = (doc.duration + '').toHHMMSS(false);
-
- spanPlay = document.createElement('span');
- spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
- container.append(spanPlay);
- } else {
- span.innerText = 'GIF';
- }
- }
-
let loadVideo = async() => {
+ let url: string;
if(message.media.preloader) { // means upload
(message.media.preloader as ProgressivePreloader).attach(container, undefined, undefined, false);
} else if(!doc.downloaded) {
- let preloader = new ProgressivePreloader(container, true);
- let promise = appDocsManager.downloadDoc(doc);
- preloader.attach(container, true, promise, false);
- await promise;
+ const promise = appDocsManager.downloadVideo(doc.id);
+
+ //if(!doc.supportsStreaming) {
+ const preloader = new ProgressivePreloader(container, true);
+ preloader.attach(container, true, promise, false);
+ //}
+
+ const mp4Source: MP4Source = await (promise as Promise);
+ if(mp4Source instanceof MP4Source) {
+ url = mp4Source.getURL();
+ }
+ }
+
+ if(!url) {
+ url = doc.url;
}
if(middleware && !middleware()) {
@@ -107,17 +117,30 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
}
//console.log('loaded doc:', doc, doc.url, container);
+
+ if(doc.type == 'gif'/* || true */) {
+ video.addEventListener('canplay', () => {
+ if(img && img.parentElement) {
+ img.remove();
+ }
+
+ /* if(!video.paused) {
+ video.pause();
+ } */
+ animationIntersector.addAnimation(video, 'chat');
+ }, {once: true});
+ }
- renderImageFromUrl(source, doc.url);
+ renderImageFromUrl(source, url);
source.type = doc.mime_type;
video.append(source);
video.setAttribute('playsinline', '');
- if(img && img.parentElement) {
- img.remove();
- }
+ /* if(!container.parentElement) {
+ container.append(video);
+ } */
- if(doc.type == 'gif') {
+ if(doc.type == 'gif'/* || true */) {
video.muted = true;
video.loop = true;
//video.play();
@@ -130,7 +153,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
}
};
- if(doc.size >= 20e6 && !doc.downloaded) {
+ /* if(doc.size >= 20e6 && !doc.downloaded) {
let downloadDiv = document.createElement('div');
downloadDiv.classList.add('download');
@@ -146,9 +169,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
container.prepend(downloadDiv);
return;
- }
+ } */
- return doc.downloaded ? loadVideo() : lazyLoadQueue.push({div: container, load: loadVideo/* , wasSeen: true */});
+ //return;
+ return doc.downloaded/* && false */ ? loadVideo() : lazyLoadQueue.push({div: container, load: loadVideo/* , wasSeen: true */});
}
export const formatDate = (timestamp: number, monthShort = false, withYear = true) => {
@@ -402,7 +426,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
if(thumb.bytes) {
let img = new Image();
- if(appWebpManager.isSupported() || doc.stickerThumbConverted) {
+ if((appWebpManager.isSupported() || doc.stickerThumbConverted)/* && false */) {
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true));
div.append(img);
@@ -415,7 +439,9 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
if(!div.childElementCount) {
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true)).then(() => {
- div.append(img);
+ if(!div.childElementCount) {
+ div.append(img);
+ }
});
}
});
@@ -427,12 +453,14 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
} else if(!onlyThumb && stickerType == 2 && withThumb && toneIndex <= 0) {
let img = new Image();
let load = () => appDocsManager.downloadDocThumb(doc, thumb.type).then(url => {
- if(!img.parentElement || (middleware && !middleware())) return;
+ if(div.childElementCount || (middleware && !middleware())) return;
let promise = renderImageFromUrl(img, url);
if(!downloaded) {
promise.then(() => {
- div.append(img);
+ if(!div.childElementCount) {
+ div.append(img);
+ }
});
}
});
diff --git a/src/index.hbs b/src/index.hbs
index e15e04d3..57dad505 100644
--- a/src/index.hbs
+++ b/src/index.hbs
@@ -176,7 +176,7 @@
-