Browse Source

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
master
Eduard Kuzmenko 2 years ago committed by r4sas
parent
commit
0abf74f57f
  1. 4
      src/components/appMediaViewerBase.ts
  2. 7
      src/components/chat/bubbles.ts
  3. 26
      src/components/chat/reaction.ts
  4. 12
      src/components/chat/selection.ts
  5. 9
      src/components/chat/topbar.ts
  6. 4
      src/components/sidebarLeft/tabs/archivedTab.ts
  7. 3
      src/components/stickerViewer.ts
  8. 4
      src/components/wrappers/sticker.ts
  9. 12
      src/components/wrappers/stickerAnimation.ts
  10. 4
      src/helpers/eventListenerBase.ts
  11. 3
      src/lib/appManagers/appDialogsManager.ts
  12. 10
      src/lib/richTextProcessor/wrapRichText.ts
  13. 6
      src/scss/partials/_chatBubble.scss

4
src/components/appMediaViewerBase.ts

@ -1168,7 +1168,9 @@ export default class AppMediaViewerBase< @@ -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')) {

7
src/components/chat/bubbles.ts

@ -2328,7 +2328,7 @@ export default class ChatBubbles { @@ -2328,7 +2328,7 @@ export default class ChatBubbles {
// * if it's a start, then scroll to start of the group
if(bubble && position !== 'end') {
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;
// if(whichChild(dateGroup) === 0) {
fallbackToElementStartWhenCentering = dateGroup;
@ -3600,7 +3600,8 @@ export default class ChatBubbles { @@ -3600,7 +3600,8 @@ export default class ChatBubbles {
loadPromises,
lazyLoadQueue: this.lazyLoadQueue,
customEmojiSize,
middleware
middleware,
animationGroup: CHAT_ANIMATION_GROUP
});
let canHaveTail = true;
@ -4521,6 +4522,8 @@ export default class ChatBubbles { @@ -4521,6 +4522,8 @@ export default class ChatBubbles {
if(isFooter) {
canHaveTail = true;
} else {
bubble.classList.add('with-beside-replies');
}
}

26
src/components/chat/reaction.ts

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

12
src/components/chat/selection.ts

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

9
src/components/chat/topbar.ts

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

4
src/components/sidebarLeft/tabs/archivedTab.ts

@ -32,8 +32,8 @@ export default class AppArchivedTab extends SliderSuperTab { @@ -32,8 +32,8 @@ export default class AppArchivedTab extends SliderSuperTab {
const scrollable = appDialogsManager.scrollables[AppArchivedTab.filterId];
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}) => {
if(cached) {
return renderPromise;

3
src/components/stickerViewer.ts

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

4
src/components/wrappers/sticker.ts

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

12
src/components/wrappers/stickerAnimation.ts

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

4
src/helpers/eventListenerBase.ts

@ -84,7 +84,7 @@ export default class EventListenerBase<Listeners extends EventListenerListeners> @@ -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) {
(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)) {
callback(...this.listenerResults[name]);
@ -108,7 +108,7 @@ export default class EventListenerBase<Listeners extends EventListenerListeners> @@ -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) {
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);
}

3
src/lib/appManagers/appDialogsManager.ts

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

10
src/lib/richTextProcessor/wrapRichText.ts

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

6
src/scss/partials/_chatBubble.scss

@ -1753,7 +1753,7 @@ $bubble-beside-button-width: 38px; @@ -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;
}
@ -1787,6 +1787,10 @@ $bubble-beside-button-width: 38px; @@ -1787,6 +1787,10 @@ $bubble-beside-button-width: 38px;
border-bottom-right-radius: 0;
}
&.with-beside-replies .bubble-content {
min-height: 5.5rem;
}
.time {
visibility: hidden; // * can't use color transparent here, because in name can be emoji
font-size: 12px;

Loading…
Cancel
Save