Fix bugged sticker viewer

Fix replies layout with custom emoji
Fix wrapping some custom emojis
Fix opening restricted chat
Fix wrapping encoded spoiler
Fix playing custom emoji interactive animation
Fix loading archived chatlist
Fix reaction effect at top left corner
This commit is contained in:
Eduard Kuzmenko 2022-09-16 21:44:10 +04:00
parent cef70c043a
commit 09d0030de4
13 changed files with 69 additions and 35 deletions

View File

@ -1168,7 +1168,9 @@ export default class AppMediaViewerBase<
} }
} }
renderImageFromUrl(el, url); if((el as HTMLImageElement).src !== url) {
renderImageFromUrl(el, url);
}
// ! костыль, но он тут даже и не нужен // ! костыль, но он тут даже и не нужен
if(el.classList.contains('thumbnail') && el.parentElement.classList.contains('media-container-aspecter')) { if(el.classList.contains('thumbnail') && el.parentElement.classList.contains('media-container-aspecter')) {

View File

@ -2328,7 +2328,7 @@ export default class ChatBubbles {
// * if it's a start, then scroll to start of the group // * if it's a start, then scroll to start of the group
if(bubble && position !== 'end') { if(bubble && position !== 'end') {
const item = this.bubbleGroups.getItemByBubble(bubble); const item = this.bubbleGroups.getItemByBubble(bubble);
if(item.group.firstItem === item && whichChild(item.group.container) === (this.stickyIntersector ? STICKY_OFFSET : 1)) { if(item && item.group.firstItem === item && whichChild(item.group.container) === (this.stickyIntersector ? STICKY_OFFSET : 1)) {
const dateGroup = item.group.container.parentElement; const dateGroup = item.group.container.parentElement;
// if(whichChild(dateGroup) === 0) { // if(whichChild(dateGroup) === 0) {
fallbackToElementStartWhenCentering = dateGroup; fallbackToElementStartWhenCentering = dateGroup;
@ -3600,7 +3600,8 @@ export default class ChatBubbles {
loadPromises, loadPromises,
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
customEmojiSize, customEmojiSize,
middleware middleware,
animationGroup: CHAT_ANIMATION_GROUP
}); });
let canHaveTail = true; let canHaveTail = true;
@ -4521,6 +4522,8 @@ export default class ChatBubbles {
if(isFooter) { if(isFooter) {
canHaveTail = true; canHaveTail = true;
} else {
bubble.classList.add('with-beside-replies');
} }
} }

View File

@ -16,6 +16,7 @@ import SetTransition from '../singleTransition';
import StackedAvatars from '../stackedAvatars'; import StackedAvatars from '../stackedAvatars';
import {wrapSticker, wrapStickerAnimation} from '../wrappers'; import {wrapSticker, wrapStickerAnimation} from '../wrappers';
import {Awaited} from '../../types'; import {Awaited} from '../../types';
import noop from '../../helpers/noop';
const CLASS_NAME = 'reaction'; const CLASS_NAME = 'reaction';
const TAG_NAME = CLASS_NAME + '-element'; const TAG_NAME = CLASS_NAME + '-element';
@ -186,26 +187,33 @@ export default class ReactionElement extends HTMLElement {
skipRatio: 1, skipRatio: 1,
play: false, play: false,
managers: this.managers managers: this.managers
}).stickerPromise }).stickerPromise.catch(noop)
]).then(([iconPlayer, aroundPlayer]) => { ]).then(([iconPlayer, aroundPlayer]) => {
const remove = () => { const remove = () => {
// if(!isInDOM(div)) return; // if(!isInDOM(div)) return;
fastRaf(() => { iconPlayer.remove();
// if(!isInDOM(div)) return; div.remove();
iconPlayer.remove(); this.stickerContainer.classList.remove('has-animation');
div.remove(); };
this.stickerContainer.classList.remove('has-animation');
}); if(!aroundPlayer) {
remove();
return;
}
const removeOnFrame = () => {
// if(!isInDOM(div)) return;
fastRaf(remove);
}; };
iconPlayer.addEventListener('enterFrame', (frameNo) => { iconPlayer.addEventListener('enterFrame', (frameNo) => {
if(frameNo === iconPlayer.maxFrame) { if(frameNo === iconPlayer.maxFrame) {
if(this.wrapStickerPromise) { // wait for fade in animation if(this.wrapStickerPromise) { // wait for fade in animation
this.wrapStickerPromise.then(() => { this.wrapStickerPromise.then(() => {
setTimeout(remove, 1e3); setTimeout(removeOnFrame, 1e3);
}); });
} else { } else {
remove(); removeOnFrame();
} }
} }
}); });

View File

@ -14,7 +14,6 @@ import ButtonIcon from '../buttonIcon';
import CheckboxField from '../checkboxField'; import CheckboxField from '../checkboxField';
import PopupDeleteMessages from '../popups/deleteMessages'; import PopupDeleteMessages from '../popups/deleteMessages';
import PopupForward from '../popups/forward'; import PopupForward from '../popups/forward';
import {toast} from '../toast';
import SetTransition from '../singleTransition'; import SetTransition from '../singleTransition';
import ListenerSetter from '../../helpers/listenerSetter'; import ListenerSetter from '../../helpers/listenerSetter';
import PopupSendNow from '../popups/sendNow'; import PopupSendNow from '../popups/sendNow';
@ -26,7 +25,6 @@ import blurActiveElement from '../../helpers/dom/blurActiveElement';
import cancelEvent from '../../helpers/dom/cancelEvent'; import cancelEvent from '../../helpers/dom/cancelEvent';
import cancelSelection from '../../helpers/dom/cancelSelection'; import cancelSelection from '../../helpers/dom/cancelSelection';
import getSelectedText from '../../helpers/dom/getSelectedText'; import getSelectedText from '../../helpers/dom/getSelectedText';
import rootScope from '../../lib/rootScope';
import replaceContent from '../../helpers/dom/replaceContent'; import replaceContent from '../../helpers/dom/replaceContent';
import AppSearchSuper from '../appSearchSuper.'; import AppSearchSuper from '../appSearchSuper.';
import isInDOM from '../../helpers/dom/isInDOM'; import isInDOM from '../../helpers/dom/isInDOM';
@ -58,7 +56,7 @@ class AppSelection extends EventListenerBase<{
protected isScheduled: boolean; protected isScheduled: boolean;
protected listenElement: HTMLElement; protected listenElement: HTMLElement;
protected onToggleSelection: (forwards: boolean, animate: boolean) => void; protected onToggleSelection: (forwards: boolean, animate: boolean) => void | Promise<void>;
protected onUpdateContainer: (cantForward: boolean, cantDelete: boolean, cantSend: boolean) => void; protected onUpdateContainer: (cantForward: boolean, cantDelete: boolean, cantSend: boolean) => void;
protected onCancelSelection: () => void; protected onCancelSelection: () => void;
protected toggleByMid: (peerId: PeerId, mid: number) => void; protected toggleByMid: (peerId: PeerId, mid: number) => void;
@ -361,7 +359,7 @@ class AppSelection extends EventListenerBase<{
if(cantForward && cantDelete) break; if(cantForward && cantDelete) break;
} }
this.onUpdateContainer && this.onUpdateContainer(cantForward, cantDelete, cantSend); this.onUpdateContainer?.(cantForward, cantDelete, cantSend);
} }
public toggleSelection(toggleCheckboxes = true, forceSelection = false) { public toggleSelection(toggleCheckboxes = true, forceSelection = false) {
@ -404,7 +402,7 @@ class AppSelection extends EventListenerBase<{
blurActiveElement(); blurActiveElement();
const forwards = !!size || forceSelection; const forwards = !!size || forceSelection;
this.onToggleSelection && this.onToggleSelection(forwards, !this.doNotAnimate); const toggleResult = this.onToggleSelection?.(forwards, !this.doNotAnimate);
if(!IS_MOBILE_SAFARI) { if(!IS_MOBILE_SAFARI) {
if(forwards) { if(forwards) {
@ -420,7 +418,7 @@ class AppSelection extends EventListenerBase<{
} }
if(forceSelection) { if(forceSelection) {
this.updateContainer(forceSelection); (toggleResult || Promise.resolve()).then(() => this.updateContainer(forceSelection));
} }
return true; return true;
@ -972,7 +970,7 @@ export default class ChatSelection extends AppSelection {
replaceContent(this.selectionCountEl, i18n('messages', [this.length()])); replaceContent(this.selectionCountEl, i18n('messages', [this.length()]));
this.selectionSendNowBtn && this.selectionSendNowBtn.toggleAttribute('disabled', cantSend); this.selectionSendNowBtn && this.selectionSendNowBtn.toggleAttribute('disabled', cantSend);
this.selectionForwardBtn && this.selectionForwardBtn.toggleAttribute('disabled', cantForward); this.selectionForwardBtn && this.selectionForwardBtn.toggleAttribute('disabled', cantForward);
this.selectionDeleteBtn.toggleAttribute('disabled', cantDelete); this.selectionDeleteBtn && this.selectionDeleteBtn.toggleAttribute('disabled', cantDelete);
}; };
protected onCancelSelection = async() => { protected onCancelSelection = async() => {

View File

@ -708,7 +708,7 @@ export default class ChatTopbar {
return () => { return () => {
this.btnMute && this.btnMute.classList.toggle('hide', !isBroadcast); this.btnMute && this.btnMute.classList.toggle('hide', !isBroadcast);
if(this.btnJoin) { if(this.btnJoin) {
if(isAnyChat) { if(isAnyChat && !this.chat.isRestricted) {
replaceContent(this.btnJoin, i18n(isBroadcast ? 'Chat.Subscribe' : 'ChannelJoin')); replaceContent(this.btnJoin, i18n(isBroadcast ? 'Chat.Subscribe' : 'ChannelJoin'));
this.btnJoin.classList.toggle('hide', !chat?.pFlags?.left); this.btnJoin.classList.toggle('hide', !chat?.pFlags?.left);
} else { } else {
@ -783,10 +783,15 @@ export default class ChatTopbar {
} else if(this.chat.type === 'discussion') { } else if(this.chat.type === 'discussion') {
if(count === undefined) { if(count === undefined) {
const result = await this.managers.acknowledged.appMessagesManager.getHistory(peerId, 0, 1, 0, this.chat.threadId); const result = await this.managers.acknowledged.appMessagesManager.getHistory(peerId, 0, 1, 0, this.chat.threadId);
if(!middleware()) return;
if(result.cached) { if(result.cached) {
const historyResult = await result.result; const historyResult = await result.result;
if(!middleware()) return;
count = historyResult.count; count = historyResult.count;
} else result.result.then((historyResult) => this.setTitle(historyResult.count)); } else result.result.then((historyResult) => {
if(!middleware()) return;
this.setTitle(historyResult.count);
});
} }
if(count === undefined) titleEl = i18n('Loading'); if(count === undefined) titleEl = i18n('Loading');

View File

@ -32,8 +32,8 @@ export default class AppArchivedTab extends SliderSuperTab {
const scrollable = appDialogsManager.scrollables[AppArchivedTab.filterId]; const scrollable = appDialogsManager.scrollables[AppArchivedTab.filterId];
this.scrollable.container.replaceWith(scrollable.container); this.scrollable.container.replaceWith(scrollable.container);
this.scrollable = scrollable; // ! DO NOT UNCOMMENT NEXT LINE - chats will stop loading on scroll after closing the tab
// this.scrollable = scrollable;
return appDialogsManager.setFilterIdAndChangeTab(AppArchivedTab.filterId).then(({cached, renderPromise}) => { return appDialogsManager.setFilterIdAndChangeTab(AppArchivedTab.filterId).then(({cached, renderPromise}) => {
if(cached) { if(cached) {
return renderPromise; return renderPromise;

View File

@ -180,6 +180,7 @@ export default function attachStickerViewerListeners({listenTo, listenerSetter,
player.addEventListener('enterFrame', c); player.addEventListener('enterFrame', c);
}); });
if(!middleware()) return;
player.pause(); player.pause();
} else if(player instanceof HTMLVideoElement) { } else if(player instanceof HTMLVideoElement) {
player.currentTime = (mediaContainer.querySelector('video') as HTMLVideoElement).currentTime; player.currentTime = (mediaContainer.querySelector('video') as HTMLVideoElement).currentTime;
@ -283,7 +284,6 @@ export default function attachStickerViewerListeners({listenTo, listenerSetter,
const onMousePreMove = (e: MouseEvent) => { const onMousePreMove = (e: MouseEvent) => {
if(!findUpAsChild(e.target as HTMLElement, mediaContainer)) { if(!findUpAsChild(e.target as HTMLElement, mediaContainer)) {
document.removeEventListener('mousemove', onMousePreMove);
onMouseUp(); onMouseUp();
} }
}; };
@ -303,6 +303,7 @@ export default function attachStickerViewerListeners({listenTo, listenerSetter,
attachClickEvent(document.body, cancelEvent, {capture: true, once: true}); attachClickEvent(document.body, cancelEvent, {capture: true, once: true});
} }
document.removeEventListener('mousemove', onMousePreMove);
document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp, {capture: true}); document.removeEventListener('mouseup', onMouseUp, {capture: true});
}; };

View File

@ -700,8 +700,8 @@ export async function onEmojiStickerClick({event, container, managers, peerId, m
data.a.length = 0; data.a.length = 0;
}, 1000, false); }, 1000, false);
const animation = lottieLoader.getAnimation(container); const animation = !container.classList.contains('custom-emoji') ? lottieLoader.getAnimation(container) : undefined;
if(animation.paused) { if(animation?.paused) {
const doc = await managers.appStickersManager.getAnimatedEmojiSoundDocument(emoji); const doc = await managers.appStickersManager.getAnimatedEmojiSoundDocument(emoji);
if(doc) { if(doc) {
const audio = document.createElement('audio'); const audio = document.createElement('audio');

View File

@ -7,7 +7,8 @@
import IS_VIBRATE_SUPPORTED from '../../environment/vibrateSupport'; import IS_VIBRATE_SUPPORTED from '../../environment/vibrateSupport';
import assumeType from '../../helpers/assumeType'; import assumeType from '../../helpers/assumeType';
import isInDOM from '../../helpers/dom/isInDOM'; import isInDOM from '../../helpers/dom/isInDOM';
import {Middleware} from '../../helpers/middleware'; import makeError from '../../helpers/makeError';
import {getMiddleware, Middleware} from '../../helpers/middleware';
import throttleWithRaf from '../../helpers/schedulers/throttleWithRaf'; import throttleWithRaf from '../../helpers/schedulers/throttleWithRaf';
import windowSize from '../../helpers/windowSize'; import windowSize from '../../helpers/windowSize';
import {PhotoSize, VideoSize} from '../../layer'; import {PhotoSize, VideoSize} from '../../layer';
@ -53,11 +54,15 @@ export default function wrapStickerAnimation({
let animation: RLottiePlayer; let animation: RLottiePlayer;
const unmountAnimation = () => { const unmountAnimation = () => {
middlewareHelper.clean();
animation?.remove(); animation?.remove();
animationDiv.remove(); animationDiv.remove();
appImManager.chat.bubbles.scrollable.container.removeEventListener('scroll', onScroll); appImManager.chat.bubbles.scrollable.container.removeEventListener('scroll', onScroll);
}; };
const middlewareHelper = middleware?.create() ?? getMiddleware();
middleware = middlewareHelper.get();
const stickerPromise = wrapSticker({ const stickerPromise = wrapSticker({
div: animationDiv, div: animationDiv,
doc, doc,
@ -74,6 +79,11 @@ export default function wrapStickerAnimation({
fullThumb fullThumb
}).then(({render}) => render).then((_animation) => { }).then(({render}) => render).then((_animation) => {
assumeType<RLottiePlayer>(_animation); assumeType<RLottiePlayer>(_animation);
if(!middleware()) {
_animation.remove();
throw makeError('MIDDLEWARE');
}
animation = _animation; animation = _animation;
animation.addEventListener('enterFrame', (frameNo) => { animation.addEventListener('enterFrame', (frameNo) => {
if((!loopEffect && frameNo === animation.maxFrame) || !isInDOM(target)) { if((!loopEffect && frameNo === animation.maxFrame) || !isInDOM(target)) {

View File

@ -84,7 +84,7 @@ export default class EventListenerBase<Listeners extends EventListenerListeners>
} }
public addEventListener<T extends keyof Listeners>(name: T, callback: Listeners[T], options?: boolean | AddEventListenerOptions) { public addEventListener<T extends keyof Listeners>(name: T, callback: Listeners[T], options?: boolean | AddEventListenerOptions) {
(this.listeners[name] ?? (this.listeners[name] = [])).push({callback, options}); // ! add before because if you don't, you won't be able to delete it from callback (this.listeners[name] ??= []).push({callback, options}); // ! add before because if you don't, you won't be able to delete it from callback
if(this.listenerResults.hasOwnProperty(name)) { if(this.listenerResults.hasOwnProperty(name)) {
callback(...this.listenerResults[name]); callback(...this.listenerResults[name]);
@ -108,7 +108,7 @@ export default class EventListenerBase<Listeners extends EventListenerListeners>
public removeEventListener<T extends keyof Listeners>(name: T, callback: Listeners[T], options?: boolean | AddEventListenerOptions) { public removeEventListener<T extends keyof Listeners>(name: T, callback: Listeners[T], options?: boolean | AddEventListenerOptions) {
if(this.listeners[name]) { if(this.listeners[name]) {
findAndSplice(this.listeners[name], l => l.callback === callback); findAndSplice(this.listeners[name], (l) => l.callback === callback);
} }
// e.remove(this, name, callback); // e.remove(this, name, callback);
} }

View File

@ -1003,6 +1003,7 @@ export class AppDialogsManager {
let loadCount = windowSize.height / 72 * 1.25 | 0; let loadCount = windowSize.height / 72 * 1.25 | 0;
let offsetIndex = 0; let offsetIndex = 0;
const doNotRenderChatList = this.doNotRenderChatList; // cache before awaits
const {index: currentOffsetIndex} = this.getOffsetIndex(side); const {index: currentOffsetIndex} = this.getOffsetIndex(side);
if(currentOffsetIndex) { if(currentOffsetIndex) {
if(side === 'top') { if(side === 'top') {
@ -1045,7 +1046,7 @@ export class AppDialogsManager {
const a = await getConversationsResult; const a = await getConversationsResult;
const result = await a.result; const result = await a.result;
if(this.loadDialogsRenderPromise !== renderPromise || this.doNotRenderChatList) { if(this.loadDialogsRenderPromise !== renderPromise || doNotRenderChatList) {
reject(); reject();
cachedInfoPromise.reject(); cachedInfoPromise.reject();
return; return;

View File

@ -437,7 +437,6 @@ export default function wrapRichText(text: string, options: Partial<{
}, },
voodoo?: boolean, voodoo?: boolean,
customEmojis?: {[docId: DocId]: CustomEmojiElement[]}, customEmojis?: {[docId: DocId]: CustomEmojiElement[]},
wrappingSpoiler?: boolean,
loadPromises?: Promise<any>[], loadPromises?: Promise<any>[],
middleware?: Middleware, middleware?: Middleware,
@ -478,7 +477,8 @@ export default function wrapRichText(text: string, options: Partial<{
} }
} else if((entity.offset + entity.length) > textLength) { } else if((entity.offset + entity.length) > textLength) {
entity = copy(entity); entity = copy(entity);
entity.length = entity.offset + entity.length - textLength; // entity.length = entity.offset + entity.length - textLength;
entity.length = textLength - entity.offset;
} }
if(entity.length) { if(entity.length) {
@ -621,7 +621,7 @@ export default function wrapRichText(text: string, options: Partial<{
break; break;
} }
if(nextEntity?._ === 'messageEntityEmoji') { while(nextEntity?._ === 'messageEntityEmoji' && nextEntity.offset < endOffset) {
++nasty.i; ++nasty.i;
nasty.lastEntity = nextEntity; nasty.lastEntity = nextEntity;
nasty.usedLength += nextEntity.length; nasty.usedLength += nextEntity.length;
@ -808,7 +808,9 @@ export default function wrapRichText(text: string, options: Partial<{
const encoded = encodeSpoiler(nasty.text, entity); const encoded = encodeSpoiler(nasty.text, entity);
nasty.text = encoded.text; nasty.text = encoded.text;
partText = encoded.entityText; partText = encoded.entityText;
nasty.usedLength += partText.length; if(endPartOffset !== endOffset) {
nasty.usedLength += endOffset - endPartOffset;
}
let n: MessageEntity; let n: MessageEntity;
for(; n = entities[nasty.i + 1], n && n.offset < endOffset;) { for(; n = entities[nasty.i + 1], n && n.offset < endOffset;) {
// nasty.usedLength += n.length; // nasty.usedLength += n.length;

View File

@ -1753,7 +1753,7 @@ $bubble-beside-button-width: 38px;
} }
} }
&.with-replies:not(.sticker) .message { &.with-replies:not(.sticker):not(.with-beside-replies) .message {
bottom: 55px; bottom: 55px;
} }
@ -1786,6 +1786,10 @@ $bubble-beside-button-width: 38px;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }
&.with-beside-replies .bubble-content {
min-height: 5.5rem;
}
.time { .time {
visibility: hidden; // * can't use color transparent here, because in name can be emoji visibility: hidden; // * can't use color transparent here, because in name can be emoji