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

96
src/components/emoticonsDropdown/index.ts

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

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

@ -5,6 +5,7 @@ import { putPreloader } from "../../misc"; @@ -5,6 +5,7 @@ import { putPreloader } from "../../misc";
import appStateManager from "../../../lib/appManagers/appStateManager";
import { RichTextProcessor } from "../../../lib/richtextprocessor";
import appImManager from "../../../lib/appManagers/appImManager";
import StickyIntersector from "../../stickyIntersector";
export default class EmojiTab implements EmoticonsTab {
public content: HTMLElement;
@ -12,8 +13,8 @@ export default class EmojiTab implements EmoticonsTab { @@ -12,8 +13,8 @@ export default class EmojiTab implements EmoticonsTab {
private recent: string[] = [];
private recentItemsDiv: HTMLElement;
private heights: number[] = [];
private scroll: Scrollable;
private stickyIntersector: StickyIntersector;
init() {
this.content = document.getElementById('content-emoji') as HTMLDivElement;
@ -77,12 +78,9 @@ export default class EmojiTab implements EmoticonsTab { @@ -77,12 +78,9 @@ export default class EmojiTab implements EmoticonsTab {
}
//console.timeEnd('emojiParse');
let prevCategoryIndex = 0;
const menu = this.content.previousElementSibling.firstElementChild as HTMLUListElement;
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);
const preloader = putPreloader(this.content, true);
@ -112,15 +110,13 @@ export default class EmojiTab implements EmoticonsTab { @@ -112,15 +110,13 @@ export default class EmojiTab implements EmoticonsTab {
}
emojiScroll.append(div);
this.stickyIntersector.observeStickyHeaderChanges(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);
EmoticonsDropdown.menuOnClick(menu, this.heights, emojiScroll);
this.stickyIntersector = EmoticonsDropdown.menuOnClick(menu, emojiScroll);
this.init = null;
}
@ -182,14 +178,6 @@ export default class EmojiTab implements EmoticonsTab { @@ -182,14 +178,6 @@ export default class EmojiTab implements EmoticonsTab {
const scrollHeight = this.recentItemsDiv.scrollHeight;
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.unshift(emoji);
if(this.recent.length > 36) {

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

@ -2,7 +2,7 @@ import { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from ".."; @@ -2,7 +2,7 @@ import { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from "..";
import { MTDocument } from "../../../types";
import Scrollable from "../../scrollable_new";
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 { readBlobAsText } from "../../../helpers/blob";
import lottieLoader from "../../../lib/lottieLoader";
@ -10,6 +10,7 @@ import { renderImageFromUrl, putPreloader } from "../../misc"; @@ -10,6 +10,7 @@ import { renderImageFromUrl, putPreloader } from "../../misc";
import { RichTextProcessor } from "../../../lib/richtextprocessor";
import { $rootScope } from "../../../lib/utils";
import apiManager from "../../../lib/mtproto/mtprotoworker";
import StickyIntersector from "../../stickyIntersector";
export default class StickersTab implements EmoticonsTab {
public content: HTMLElement;
@ -22,15 +23,17 @@ export default class StickersTab implements EmoticonsTab { @@ -22,15 +23,17 @@ export default class StickersTab implements EmoticonsTab {
private recentDiv: HTMLElement;
private recentStickers: MTDocument[] = [];
private heights: number[] = [];
private heightRAF = 0;
private scroll: Scrollable;
private menu: HTMLUListElement;
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');
const itemsDiv = document.createElement('div');
@ -42,62 +45,29 @@ export default class StickersTab implements EmoticonsTab { @@ -42,62 +45,29 @@ export default class StickersTab implements EmoticonsTab {
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));
});
if(this.queueCategoryPush.length) {
this.queueCategoryPush.forEach(({element, prepend}) => {
if(prepend) {
if(this.recentDiv.parentElement) {
this.scroll.prepend(categoryDiv);
this.scroll.prepend(element);
this.scroll.prepend(this.recentDiv);
} else {
this.scroll.prepend(categoryDiv);
}
} 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;
this.scroll.prepend(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);
} else this.scroll.append(element);
});
this.scroll.reorder();
//console.log('stickers concated', concated, heights);
this.queueCategoryPush.length = 0;
}
});
}
@ -138,7 +108,9 @@ export default class StickersTab implements EmoticonsTab { @@ -138,7 +108,9 @@ export default class StickersTab implements EmoticonsTab {
//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);
@ -175,8 +147,6 @@ export default class StickersTab implements EmoticonsTab { @@ -175,8 +147,6 @@ export default class StickersTab implements EmoticonsTab {
group: EMOTICONSSTICKERGROUP
}); // kostil
}
this.categoryPush(categoryDiv, RichTextProcessor.wrapEmojiText(stickerSet.set.title), stickerSet.documents, prepend);
}
init() {
@ -227,33 +197,16 @@ export default class StickersTab implements EmoticonsTab { @@ -227,33 +197,16 @@ export default class StickersTab implements EmoticonsTab {
const elements = this.stickerSets[set.id];
elements.stickers.remove();
elements.tab.remove();
this.setNewHeights();
delete this.stickerSets[set.id];
}
});
stickersDiv.addEventListener('click', EmoticonsDropdown.onMediaClick);
let prevCategoryIndex = 0;
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.menu.addEventListener('click', () => {
if(this.heights[1] == 0) {
this.setNewHeights();
}
});
EmoticonsDropdown.menuOnClick(this.menu, this.heights, this.scroll, menuScroll);
this.stickyIntersector = EmoticonsDropdown.menuOnClick(this.menu, this.scroll, menuScroll);
const preloader = putPreloader(this.content, true);
@ -269,7 +222,7 @@ export default class StickersTab implements EmoticonsTab { @@ -269,7 +222,7 @@ export default class StickersTab implements EmoticonsTab {
};
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) => {
@ -308,8 +261,6 @@ export default class StickersTab implements EmoticonsTab { @@ -308,8 +261,6 @@ export default class StickersTab implements EmoticonsTab {
if(items.childElementCount > 20) {
(Array.from(items.children) as HTMLElement[]).slice(20).forEach(el => el.remove());
}
this.setNewHeights();
}
onClose() {

7
src/scss/partials/_emojiDropdown.scss

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

Loading…
Cancel
Save