Browse Source

Fix 'keyboard is closing on each message send'

Fix stickers categories order
Refactored stickers & emoji heights to stickyIntersector
master
morethanwords 4 years ago
parent
commit
aadcb9732e
  1. 11
      src/components/chatInput.ts
  2. 96
      src/components/emoticonsDropdown/index.ts
  3. 22
      src/components/emoticonsDropdown/tabs/emoji.ts
  4. 99
      src/components/emoticonsDropdown/tabs/stickers.ts
  5. 7
      src/scss/partials/_emojiDropdown.scss

11
src/components/chatInput.ts

@ -3,7 +3,7 @@ import { RichTextProcessor } from "../lib/richtextprocessor";
import apiManager from "../lib/mtproto/mtprotoworker"; import apiManager from "../lib/mtproto/mtprotoworker";
import appWebPagesManager from "../lib/appManagers/appWebPagesManager"; import appWebPagesManager from "../lib/appManagers/appWebPagesManager";
import appImManager from "../lib/appManagers/appImManager"; import appImManager from "../lib/appManagers/appImManager";
import { getRichValue, calcImageInBox } from "../lib/utils"; import { getRichValue, calcImageInBox, cancelEvent } from "../lib/utils";
import { wrapDocument, wrapReply } from "./wrappers"; import { wrapDocument, wrapReply } from "./wrappers";
import appMessagesManager from "../lib/appManagers/appMessagesManager"; import appMessagesManager from "../lib/appManagers/appMessagesManager";
import { Layouter, RectPart } from "./groupedLayout"; import { Layouter, RectPart } from "./groupedLayout";
@ -495,7 +495,9 @@ export class ChatInput {
this.onMessageSent(); this.onMessageSent();
}); });
this.btnSend.addEventListener('click', () => { const onBtnSendClick = (e: Event) => {
cancelEvent(e);
if(this.btnSend.classList.contains('tgico-send') || !this.recorder) { if(this.btnSend.classList.contains('tgico-send') || !this.recorder) {
if(this.recording) { if(this.recording) {
this.recorder.stop(); this.recorder.stop();
@ -553,7 +555,10 @@ export class ChatInput {
console.error('Recorder start error:', e); console.error('Recorder start error:', e);
}); });
} }
}); };
this.btnSend.addEventListener('touchend', onBtnSendClick);
this.btnSend.addEventListener('click', onBtnSendClick);
if(this.recorder) { if(this.recorder) {
this.btnCancelRecord.addEventListener('click', () => { this.btnCancelRecord.addEventListener('click', () => {

96
src/components/emoticonsDropdown/index.ts

@ -9,6 +9,7 @@ import appImManager from "../../lib/appManagers/appImManager";
import Scrollable from "../scrollable_new"; import Scrollable from "../scrollable_new";
import EmojiTab from "./tabs/emoji"; import EmojiTab from "./tabs/emoji";
import StickersTab from "./tabs/stickers"; import StickersTab from "./tabs/stickers";
import StickyIntersector from "../stickyIntersector";
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
@ -17,6 +18,8 @@ export interface EmoticonsTab {
onCloseAfterTimeout?: () => void onCloseAfterTimeout?: () => void
} }
const test = false;
export class EmoticonsDropdown { export class EmoticonsDropdown {
public static lazyLoadQueue = new LazyLoadQueue(); public static lazyLoadQueue = new LazyLoadQueue();
private element: HTMLElement; private element: HTMLElement;
@ -57,6 +60,7 @@ export class EmoticonsDropdown {
//this.displayTimeout = setTimeout(() => { //this.displayTimeout = setTimeout(() => {
if(firstTime) { if(firstTime) {
this.toggleEl.onmouseout = this.element.onmouseout = (e) => { this.toggleEl.onmouseout = this.element.onmouseout = (e) => {
if(test) return;
const toElement = (e as any).toElement as Element; const toElement = (e as any).toElement as Element;
if(toElement && findUpClassName(toElement, 'emoji-dropdown')) { if(toElement && findUpClassName(toElement, 'emoji-dropdown')) {
return; return;
@ -213,68 +217,70 @@ export class EmoticonsDropdown {
//animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP); //animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
}; };
public static menuOnClick = (menu: HTMLUListElement, heights: number[], scroll: Scrollable, menuScroll?: Scrollable) => { public static menuOnClick = (menu: HTMLUListElement, scroll: Scrollable, menuScroll?: Scrollable) => {
menu.addEventListener('click', function(e) { let prevId = 0;
let target = e.target as HTMLElement; let jumpedTo = -1;
target = findUpTag(target, 'LI');
if(!target) { const setActive = (id: number) => {
return; if(id == prevId) {
return false;
} }
let index = whichChild(target); menu.children[prevId].classList.remove('active');
let y = heights[index - 1/* 2 */] || 0; // 10 == padding .scrollable menu.children[id].classList.add('active');
prevId = id;
//console.log('emoticonsMenuOnClick', index, heights, target);
/* if(menuScroll) { return true;
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);
});
}); */
});
}; };
public static contentOnScroll = (menu: HTMLUListElement, heights: number[], prevCategoryIndex: number, scroll: HTMLElement, menuScroll?: Scrollable) => { const stickyIntersector = new StickyIntersector(scroll.container, (stuck, target) => {
let y = Math.round(scroll.scrollTop); //console.log('sticky scrollTOp', stuck, target, scroll.container.scrollTop);
if(jumpedTo == scroll.container.scrollTop) {
return;
} else {
jumpedTo = -1;
}
//console.log(heights, y); const which = whichChild(target);
if(!stuck && which) { // * due to stickyIntersector
return;
}
for(let i = 0; i < heights.length; ++i) { setActive(which);
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(menuScroll) {
if(i < heights.length - 4) { if(which < menu.childElementCount - 4) {
menuScroll.container.scrollLeft = (i - 3) * 47; menuScroll.container.scrollLeft = (which - 3) * 47;
} else { } else {
menuScroll.container.scrollLeft = i * 47; menuScroll.container.scrollLeft = which * 47;
} }
} }
});
menu.addEventListener('click', (e) => {
let target = e.target as HTMLElement;
target = findUpTag(target, 'LI');
break; if(!target) {
return;
} }
const which = whichChild(target);
if(!setActive(which)) {
return;
} }
return prevCategoryIndex; const element = (scroll.splitUp || scroll.container).children[which] as HTMLElement;
const offsetTop = element.offsetTop + 1; // * due to stickyIntersector
scroll.container.scrollTop = jumpedTo = offsetTop;
//console.log('set scrollTop:', offsetTop);
});
return stickyIntersector;
}; };
public static onMediaClick = (e: MouseEvent) => { public static onMediaClick = (e: MouseEvent) => {

22
src/components/emoticonsDropdown/tabs/emoji.ts

@ -5,6 +5,7 @@ import { putPreloader } from "../../misc";
import appStateManager from "../../../lib/appManagers/appStateManager"; import appStateManager from "../../../lib/appManagers/appStateManager";
import { RichTextProcessor } from "../../../lib/richtextprocessor"; import { RichTextProcessor } from "../../../lib/richtextprocessor";
import appImManager from "../../../lib/appManagers/appImManager"; import appImManager from "../../../lib/appManagers/appImManager";
import StickyIntersector from "../../stickyIntersector";
export default class EmojiTab implements EmoticonsTab { export default class EmojiTab implements EmoticonsTab {
public content: HTMLElement; public content: HTMLElement;
@ -12,8 +13,8 @@ export default class EmojiTab implements EmoticonsTab {
private recent: string[] = []; private recent: string[] = [];
private recentItemsDiv: HTMLElement; private recentItemsDiv: HTMLElement;
private heights: number[] = [];
private scroll: Scrollable; private scroll: Scrollable;
private stickyIntersector: StickyIntersector;
init() { init() {
this.content = document.getElementById('content-emoji') as HTMLDivElement; this.content = document.getElementById('content-emoji') as HTMLDivElement;
@ -77,12 +78,9 @@ export default class EmojiTab implements EmoticonsTab {
} }
//console.timeEnd('emojiParse'); //console.timeEnd('emojiParse');
let prevCategoryIndex = 0;
const menu = this.content.previousElementSibling.firstElementChild as HTMLUListElement; const menu = this.content.previousElementSibling.firstElementChild as HTMLUListElement;
const emojiScroll = this.scroll = new Scrollable(this.content, 'y', 'EMOJI', null); const emojiScroll = this.scroll = new Scrollable(this.content, 'y', 'EMOJI', null);
emojiScroll.container.addEventListener('scroll', (e) => {
prevCategoryIndex = EmoticonsDropdown.contentOnScroll(menu, this.heights, prevCategoryIndex, emojiScroll.container);
});
//emojiScroll.setVirtualContainer(emojiScroll.container); //emojiScroll.setVirtualContainer(emojiScroll.container);
const preloader = putPreloader(this.content, true); const preloader = putPreloader(this.content, true);
@ -112,15 +110,13 @@ export default class EmojiTab implements EmoticonsTab {
} }
emojiScroll.append(div); emojiScroll.append(div);
this.stickyIntersector.observeStickyHeaderChanges(div);
return div; return div;
}).forEach(div => {
//console.log('emoji heights push: ', (heights[heights.length - 1] || 0) + div.scrollHeight, div, div.scrollHeight);
this.heights.push((this.heights[this.heights.length - 1] || 0) + div.scrollHeight);
}); });
}); });
this.content.addEventListener('click', this.onContentClick); this.content.addEventListener('click', this.onContentClick);
EmoticonsDropdown.menuOnClick(menu, this.heights, emojiScroll); this.stickyIntersector = EmoticonsDropdown.menuOnClick(menu, emojiScroll);
this.init = null; this.init = null;
} }
@ -182,14 +178,6 @@ export default class EmojiTab implements EmoticonsTab {
const scrollHeight = this.recentItemsDiv.scrollHeight; const scrollHeight = this.recentItemsDiv.scrollHeight;
this.appendEmoji(emoji, this.recentItemsDiv, true); this.appendEmoji(emoji, this.recentItemsDiv, true);
// нужно поставить новые размеры для скролла
if(this.recentItemsDiv.scrollHeight != scrollHeight) {
this.heights.length = 0;
(Array.from(this.scroll.container.children) as HTMLElement[]).forEach(div => {
this.heights.push((this.heights[this.heights.length - 1] || 0) + div.scrollHeight);
});
}
this.recent.findAndSplice(e => e == emoji); this.recent.findAndSplice(e => e == emoji);
this.recent.unshift(emoji); this.recent.unshift(emoji);
if(this.recent.length > 36) { if(this.recent.length > 36) {

99
src/components/emoticonsDropdown/tabs/stickers.ts

@ -2,7 +2,7 @@ import { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from "..";
import { MTDocument } from "../../../types"; import { MTDocument } from "../../../types";
import Scrollable from "../../scrollable_new"; import Scrollable from "../../scrollable_new";
import { wrapSticker } from "../../wrappers"; import { wrapSticker } from "../../wrappers";
import appStickersManager, { MTStickerSet } from "../../../lib/appManagers/appStickersManager"; import appStickersManager, { MTStickerSet, MTStickerSetFull } from "../../../lib/appManagers/appStickersManager";
import appDownloadManager from "../../../lib/appManagers/appDownloadManager"; import appDownloadManager from "../../../lib/appManagers/appDownloadManager";
import { readBlobAsText } from "../../../helpers/blob"; import { readBlobAsText } from "../../../helpers/blob";
import lottieLoader from "../../../lib/lottieLoader"; import lottieLoader from "../../../lib/lottieLoader";
@ -10,6 +10,7 @@ import { renderImageFromUrl, putPreloader } from "../../misc";
import { RichTextProcessor } from "../../../lib/richtextprocessor"; import { RichTextProcessor } from "../../../lib/richtextprocessor";
import { $rootScope } from "../../../lib/utils"; import { $rootScope } from "../../../lib/utils";
import apiManager from "../../../lib/mtproto/mtprotoworker"; import apiManager from "../../../lib/mtproto/mtprotoworker";
import StickyIntersector from "../../stickyIntersector";
export default class StickersTab implements EmoticonsTab { export default class StickersTab implements EmoticonsTab {
public content: HTMLElement; public content: HTMLElement;
@ -22,15 +23,17 @@ export default class StickersTab implements EmoticonsTab {
private recentDiv: HTMLElement; private recentDiv: HTMLElement;
private recentStickers: MTDocument[] = []; private recentStickers: MTDocument[] = [];
private heights: number[] = [];
private heightRAF = 0;
private scroll: Scrollable; private scroll: Scrollable;
private menu: HTMLUListElement; private menu: HTMLUListElement;
private mounted = false; private mounted = false;
categoryPush(categoryDiv: HTMLElement, categoryTitle: string, docs: MTDocument[], prepend?: boolean) { private queueCategoryPush: {element: HTMLElement, prepend: boolean}[] = [];
private stickyIntersector: StickyIntersector;
categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise<MTDocument[]>, prepend?: boolean) {
//if((docs.length % 5) != 0) categoryDiv.classList.add('not-full'); //if((docs.length % 5) != 0) categoryDiv.classList.add('not-full');
const itemsDiv = document.createElement('div'); const itemsDiv = document.createElement('div');
@ -42,62 +45,29 @@ export default class StickersTab implements EmoticonsTab {
categoryDiv.append(titleDiv, itemsDiv); categoryDiv.append(titleDiv, itemsDiv);
docs.forEach(doc => { this.stickyIntersector.observeStickyHeaderChanges(categoryDiv);
this.queueCategoryPush.push({element: categoryDiv, prepend});
promise.then(documents => {
documents.forEach(doc => {
itemsDiv.append(this.renderSticker(doc)); itemsDiv.append(this.renderSticker(doc));
}); });
if(this.queueCategoryPush.length) {
this.queueCategoryPush.forEach(({element, prepend}) => {
if(prepend) { if(prepend) {
if(this.recentDiv.parentElement) { if(this.recentDiv.parentElement) {
this.scroll.prepend(categoryDiv); this.scroll.prepend(element);
this.scroll.prepend(this.recentDiv); this.scroll.prepend(this.recentDiv);
} else { } else {
this.scroll.prepend(categoryDiv); this.scroll.prepend(element);
}
} else this.scroll.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;
} */
this.setNewHeights();
/* Array.from(stickersDiv.children).forEach((div, i) => {
heights[i] = (heights[i - 1] || 0) + div.scrollHeight;
}); */
//this.scroll.onScroll();
//return heights.push(prevHeight + scrollHeight) - 1;
} }
} else this.scroll.append(element);
setNewHeights() {
if(this.heightRAF) return;
//if(this.heightRAF) window.cancelAnimationFrame(this.heightRAF);
this.heightRAF = window.requestAnimationFrame(() => {
this.heightRAF = 0;
const heights = this.heights;
let paddingTop = parseInt(window.getComputedStyle(this.scroll.container).getPropertyValue('padding-top')) || 0;
heights.length = 0;
/* let concated = this.scroll.hiddenElements.up.concat(this.scroll.visibleElements, this.scroll.hiddenElements.down);
concated.forEach((el, i) => {
heights[i] = (heights[i - 1] || 0) + el.height + (i == 0 ? paddingTop : 0);
}); */
let concated = Array.from(this.scroll.splitUp.children) as HTMLElement[];
concated.forEach((el, i) => {
heights[i] = (heights[i - 1] || 0) + el.scrollHeight + (i == 0 ? paddingTop : 0);
}); });
this.scroll.reorder(); this.queueCategoryPush.length = 0;
}
//console.log('stickers concated', concated, heights);
}); });
} }
@ -138,7 +108,9 @@ export default class StickersTab implements EmoticonsTab {
//stickersScroll.append(categoryDiv); //stickersScroll.append(categoryDiv);
const stickerSet = await appStickersManager.getStickerSet(set); const promise = appStickersManager.getStickerSet(set);
this.categoryPush(categoryDiv, RichTextProcessor.wrapEmojiText(set.title), promise.then(stickerSet => stickerSet.documents), prepend);
const stickerSet = await promise;
//console.log('got stickerSet', stickerSet, li); //console.log('got stickerSet', stickerSet, li);
@ -175,8 +147,6 @@ export default class StickersTab implements EmoticonsTab {
group: EMOTICONSSTICKERGROUP group: EMOTICONSSTICKERGROUP
}); // kostil }); // kostil
} }
this.categoryPush(categoryDiv, RichTextProcessor.wrapEmojiText(stickerSet.set.title), stickerSet.documents, prepend);
} }
init() { init() {
@ -227,33 +197,16 @@ export default class StickersTab implements EmoticonsTab {
const elements = this.stickerSets[set.id]; const elements = this.stickerSets[set.id];
elements.stickers.remove(); elements.stickers.remove();
elements.tab.remove(); elements.tab.remove();
this.setNewHeights();
delete this.stickerSets[set.id]; delete this.stickerSets[set.id];
} }
}); });
stickersDiv.addEventListener('click', EmoticonsDropdown.onMediaClick); stickersDiv.addEventListener('click', EmoticonsDropdown.onMediaClick);
let prevCategoryIndex = 0;
this.scroll = new Scrollable(this.content, 'y', 'STICKERS', undefined, undefined, 2); this.scroll = new Scrollable(this.content, 'y', 'STICKERS', undefined, undefined, 2);
this.scroll.container.addEventListener('scroll', (e) => {
//animationIntersector.checkAnimations(false, EMOTICONSSTICKERGROUP);
if(this.heights[1] == 0) {
this.setNewHeights();
}
prevCategoryIndex = EmoticonsDropdown.contentOnScroll(this.menu, this.heights, prevCategoryIndex, this.scroll.container, menuScroll);
});
this.scroll.setVirtualContainer(stickersDiv); this.scroll.setVirtualContainer(stickersDiv);
this.menu.addEventListener('click', () => { this.stickyIntersector = EmoticonsDropdown.menuOnClick(this.menu, this.scroll, menuScroll);
if(this.heights[1] == 0) {
this.setNewHeights();
}
});
EmoticonsDropdown.menuOnClick(this.menu, this.heights, this.scroll, menuScroll);
const preloader = putPreloader(this.content, true); const preloader = putPreloader(this.content, true);
@ -269,7 +222,7 @@ export default class StickersTab implements EmoticonsTab {
}; };
preloader.remove(); preloader.remove();
this.categoryPush(this.recentDiv, 'Recent', this.recentStickers, true); this.categoryPush(this.recentDiv, 'Recent', Promise.resolve(this.recentStickers), true);
}), }),
apiManager.invokeApi('messages.getAllStickers', {hash: 0}).then(async(res) => { apiManager.invokeApi('messages.getAllStickers', {hash: 0}).then(async(res) => {
@ -308,8 +261,6 @@ export default class StickersTab implements EmoticonsTab {
if(items.childElementCount > 20) { if(items.childElementCount > 20) {
(Array.from(items.children) as HTMLElement[]).slice(20).forEach(el => el.remove()); (Array.from(items.children) as HTMLElement[]).slice(20).forEach(el => el.remove());
} }
this.setNewHeights();
} }
onClose() { onClose() {

7
src/scss/partials/_emojiDropdown.scss

@ -80,6 +80,13 @@
width: 100%; width: 100%;
} }
.sticky_sentinel {
&--top {
top: 0;
height: 1px;
}
}
.emoji-category { .emoji-category {
//padding-top: 1px; //padding-top: 1px;
position: relative; position: relative;

Loading…
Cancel
Save