Browse Source

some fixes

master
Eduard Kuzmenko 2 years ago
parent
commit
5aa64caecc
  1. 4
      src/components/chat/bubbles.ts
  2. 2
      src/components/chat/input.ts
  3. 1
      src/components/chat/reactionsMenu.ts
  4. 9
      src/components/chat/stickersHelper.ts
  5. 4
      src/components/emoticonsDropdown/tabs/stickers.ts
  6. 3
      src/components/popups/stickers.ts
  7. 9
      src/components/preloader.ts
  8. 3
      src/components/sidebarRight/tabs/stickers.ts
  9. 303
      src/components/stickerViewer.ts
  10. 12
      src/components/wrappers/document.ts
  11. 3
      src/lib/appManagers/appDialogsManager.ts
  12. 261
      src/lib/appManagers/appImManager.ts
  13. 98
      src/lib/appManagers/appMessagesManager.ts
  14. 4
      src/lib/appManagers/appStickersManager.ts
  15. 2
      src/lib/rootScope.ts
  16. 2
      src/lib/serviceWorker/download.ts
  17. 2
      src/lib/serviceWorker/index.service.ts
  18. 2
      src/lib/serviceWorker/stream.ts
  19. 32
      src/lib/storages/dialogs.ts

4
src/components/chat/bubbles.ts

@ -112,6 +112,7 @@ import paymentsWrapCurrencyAmount from '../../helpers/paymentsWrapCurrencyAmount @@ -112,6 +112,7 @@ import paymentsWrapCurrencyAmount from '../../helpers/paymentsWrapCurrencyAmount
import PopupPayment from '../popups/payment';
import isInDOM from '../../helpers/dom/isInDOM';
import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb';
import attachStickerViewerListeners from '../stickerViewer';
const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
@ -667,6 +668,7 @@ export default class ChatBubbles { @@ -667,6 +668,7 @@ export default class ChatBubbles {
});
});
attachStickerViewerListeners({listenTo: this.scrollable.container, listenerSetter: this.listenerSetter});
attachClickEvent(this.scrollable.container, this.onBubblesClick, {listenerSetter: this.listenerSetter});
// this.listenerSetter.add(this.bubblesContainer)('click', this.onBubblesClick/* , {capture: true, passive: false} */);
@ -902,7 +904,7 @@ export default class ChatBubbles { @@ -902,7 +904,7 @@ export default class ChatBubbles {
});
this.listenerSetter.add(rootScope)('dialogs_multiupdate', (dialogs) => {
if(dialogs[this.peerId]) {
if(dialogs.has(this.peerId)) {
this.chat.input.setUnreadCount();
}
});

2
src/components/chat/input.ts

@ -735,7 +735,7 @@ export default class ChatInput { @@ -735,7 +735,7 @@ export default class ChatInput {
});
this.listenerSetter.add(rootScope)('dialogs_multiupdate', (dialogs) => {
if(dialogs[this.chat.peerId]) {
if(dialogs.has(this.chat.peerId)) {
if(this.startParam === BOT_START_PARAM) {
this.setStartParam();
} else { // updateNewMessage comes earlier than dialog appers

1
src/components/chat/reactionsMenu.ts

@ -105,6 +105,7 @@ export class ChatReactionsMenu { @@ -105,6 +105,7 @@ export class ChatReactionsMenu {
callbackify(result, (reactions) => {
if(!middleware() || !reactions.length) return;
reactions.forEach((reaction) => {
if(reaction.pFlags.premium && !rootScope.premium) return;
this.renderReaction(reaction);
});

9
src/components/chat/stickersHelper.ts

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import ListenerSetter from '../../helpers/listenerSetter';
import mediaSizes from '../../helpers/mediaSizes';
import preloadAnimatedEmojiSticker from '../../helpers/preloadAnimatedEmojiSticker';
import {MyDocument} from '../../lib/appManagers/appDocsManager';
@ -14,6 +15,7 @@ import {EmoticonsDropdown} from '../emoticonsDropdown'; @@ -14,6 +15,7 @@ import {EmoticonsDropdown} from '../emoticonsDropdown';
import {SuperStickerRenderer} from '../emoticonsDropdown/tabs/stickers';
import LazyLoadQueue from '../lazyLoadQueue';
import Scrollable from '../scrollable';
import attachStickerViewerListeners from '../stickerViewer';
import AutocompleteHelper from './autocompleteHelper';
import AutocompleteHelperController from './autocompleteHelperController';
@ -22,6 +24,7 @@ export default class StickersHelper extends AutocompleteHelper { @@ -22,6 +24,7 @@ export default class StickersHelper extends AutocompleteHelper {
private superStickerRenderer: SuperStickerRenderer;
private lazyLoadQueue: LazyLoadQueue;
private onChangeScreen: () => void;
private listenerSetter: ListenerSetter;
constructor(
appendTo: HTMLElement,
@ -52,6 +55,9 @@ export default class StickersHelper extends AutocompleteHelper { @@ -52,6 +55,9 @@ export default class StickersHelper extends AutocompleteHelper {
if(this.onChangeScreen) {
mediaSizes.removeEventListener('changeScreen', this.onChangeScreen);
this.onChangeScreen = undefined;
this.listenerSetter.removeAll();
this.listenerSetter = undefined;
}
rootScope.dispatchEvent('choosing_sticker', false);
@ -105,6 +111,9 @@ export default class StickersHelper extends AutocompleteHelper { @@ -105,6 +111,9 @@ export default class StickersHelper extends AutocompleteHelper {
this.list.style.width = width + 'px';
};
mediaSizes.addEventListener('changeScreen', this.onChangeScreen);
this.listenerSetter = new ListenerSetter();
attachStickerViewerListeners({listenTo: this.container, listenerSetter: this.listenerSetter});
}
this.onChangeScreen();

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

@ -32,6 +32,8 @@ import createStickersContextMenu from '../../../helpers/dom/createStickersContex @@ -32,6 +32,8 @@ import createStickersContextMenu from '../../../helpers/dom/createStickersContex
import findUpAsChild from '../../../helpers/dom/findUpAsChild';
import forEachReverse from '../../../helpers/array/forEachReverse';
import {MTAppConfig} from '../../../lib/mtproto/appConfig';
import attachStickerViewerListeners from '../../stickerViewer';
import ListenerSetter from '../../../helpers/listenerSetter';
export class SuperStickerRenderer {
public lazyLoadQueue: LazyLoadQueueRepeat;
@ -567,6 +569,8 @@ export default class StickersTab implements EmoticonsTab { @@ -567,6 +569,8 @@ export default class StickersTab implements EmoticonsTab {
emoticonsDropdown.addEventListener('opened', resizeCategories);
attachStickerViewerListeners({listenTo: this.content, listenerSetter: new ListenerSetter()});
createStickersContextMenu({
listenTo: this.content,
verifyRecent: (target) => !!findUpAsChild(target, this.categories['recent'].elements.items),

3
src/components/popups/stickers.ts

@ -21,6 +21,7 @@ import {toastNew} from '../toast'; @@ -21,6 +21,7 @@ import {toastNew} from '../toast';
import setInnerHTML from '../../helpers/dom/setInnerHTML';
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
import createStickersContextMenu from '../../helpers/dom/createStickersContextMenu';
import attachStickerViewerListeners from '../stickerViewer';
const ANIMATION_GROUP: AnimationItemGroup = 'STICKERS-POPUP';
@ -64,6 +65,8 @@ export default class PopupStickers extends PopupElement { @@ -64,6 +65,8 @@ export default class PopupStickers extends PopupElement {
isStickerPack: true
});
attachStickerViewerListeners({listenTo: this.stickersDiv, listenerSetter: this.listenerSetter});
this.loadStickerSet();
}

9
src/components/preloader.ts

@ -229,15 +229,16 @@ export default class ProgressivePreloader { @@ -229,15 +229,16 @@ export default class ProgressivePreloader {
this.attachPromise(promise);
}
let useRafs = 0;
if(this.detached || this.preloader.parentElement !== elem) {
const useRafs = isInDOM(this.preloader) ? 1 : 2;
useRafs = isInDOM(this.preloader) ? 1 : 2;
if(this.preloader.parentElement !== elem) {
elem[this.attachMethod](this.preloader);
}
SetTransition(this.preloader, 'is-visible', true, TRANSITION_TIME, undefined, useRafs);
}
SetTransition(this.preloader, 'is-visible', true, TRANSITION_TIME, undefined, useRafs);
if(this.cancelable && reset) {
this.setProgress(0);
}
@ -253,7 +254,7 @@ export default class ProgressivePreloader { @@ -253,7 +254,7 @@ export default class ProgressivePreloader {
// return;
if(this.preloader && this.preloader.parentElement) {
if(this.preloader?.parentElement) {
/* setTimeout(() => */// fastRaf(() => {
/* if(!this.detached) return;
this.detached = true; */

3
src/components/sidebarRight/tabs/stickers.ts

@ -19,6 +19,7 @@ import {attachClickEvent} from '../../../helpers/dom/clickEvent'; @@ -19,6 +19,7 @@ import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import forEachReverse from '../../../helpers/array/forEachReverse';
import setInnerHTML from '../../../helpers/dom/setInnerHTML';
import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
import attachStickerViewerListeners from '../../stickerViewer';
export default class AppStickersTab extends SliderSuperTab {
private inputSearch: InputSearch;
@ -41,6 +42,8 @@ export default class AppStickersTab extends SliderSuperTab { @@ -41,6 +42,8 @@ export default class AppStickersTab extends SliderSuperTab {
this.setsDiv.classList.add('sticker-sets');
this.scrollable.append(this.setsDiv);
attachStickerViewerListeners({listenTo: this.setsDiv, listenerSetter: this.listenerSetter});
attachClickEvent(this.setsDiv, (e) => {
const sticker = findUpClassName(e.target, 'sticker-set-sticker');
if(sticker) {

303
src/components/stickerViewer.ts

@ -0,0 +1,303 @@ @@ -0,0 +1,303 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import IS_TOUCH_SUPPORTED from '../environment/touchSupport';
import cancelEvent from '../helpers/dom/cancelEvent';
import {simulateClickEvent, attachClickEvent} from '../helpers/dom/clickEvent';
import findUpClassName from '../helpers/dom/findUpClassName';
import getVisibleRect from '../helpers/dom/getVisibleRect';
import ListenerSetter from '../helpers/listenerSetter';
import {makeMediaSize} from '../helpers/mediaSize';
import {getMiddleware} from '../helpers/middleware';
import {doubleRaf} from '../helpers/schedulers';
import pause from '../helpers/schedulers/pause';
import windowSize from '../helpers/windowSize';
import {MyDocument} from '../lib/appManagers/appDocsManager';
import getStickerEffectThumb from '../lib/appManagers/utils/stickers/getStickerEffectThumb';
import wrapEmojiText from '../lib/richTextProcessor/wrapEmojiText';
import lottieLoader from '../lib/rlottie/lottieLoader';
import RLottiePlayer from '../lib/rlottie/rlottiePlayer';
import rootScope from '../lib/rootScope';
import animationIntersector, {AnimationItemGroup} from './animationIntersector';
import SetTransition from './singleTransition';
import {wrapSticker} from './wrappers';
import {STICKER_EFFECT_MULTIPLIER} from './wrappers/sticker';
let hasViewer = false;
export default function attachStickerViewerListeners({listenTo, listenerSetter}: {
listenerSetter: ListenerSetter,
listenTo: HTMLElement
}) {
if(IS_TOUCH_SUPPORTED) {
return;
}
const managers = rootScope.managers;
const findClassName = 'media-sticker-wrapper';
listenerSetter.add(listenTo)('mousedown', (e) => {
if(hasViewer || e.buttons > 1 || e.button !== 0) return;
let mediaContainer = findUpClassName(e.target, findClassName);
if(!mediaContainer) {
return;
}
// const img: HTMLImageElement = mediaContainer.querySelector('img.media-sticker');
const docId = mediaContainer.dataset.docId;
if(!docId) {
return;
}
const className = 'sticker-viewer';
const group: AnimationItemGroup = 'STICKER-VIEWER';
const openDuration = 200;
const switchDuration = 200;
const previousGroup = animationIntersector.getOnlyOnePlayableGroup();
const _middleware = getMiddleware();
let container: HTMLElement, previousTransformer: HTMLElement;
const doThatSticker = async({mediaContainer, doc, middleware, lockGroups, isSwitching}: {
mediaContainer: HTMLElement,
doc: MyDocument,
middleware: () => boolean,
lockGroups?: boolean,
isSwitching?: boolean
}) => {
const effectThumb = getStickerEffectThumb(doc);
const mediaRect: DOMRect = mediaContainer.getBoundingClientRect();
const s = makeMediaSize(doc.w, doc.h);
const size = effectThumb ? 280 : 360;
const boxSize = makeMediaSize(size, size);
const fitted = mediaRect.width === mediaRect.height ? boxSize : s.aspectFitted(boxSize);
const bubble = findUpClassName(mediaContainer, 'bubble');
const isOut = bubble ? bubble.classList.contains('is-out') : true;
const transformer = document.createElement('div');
transformer.classList.add(className + '-transformer');
const stickerContainer = document.createElement('div');
stickerContainer.classList.add(className + '-sticker');
/* transformer.style.width = */stickerContainer.style.width = fitted.width + 'px';
/* transformer.style.height = */stickerContainer.style.height = fitted.height + 'px';
const stickerEmoji = document.createElement('div');
stickerEmoji.classList.add(className + '-emoji');
stickerEmoji.append(wrapEmojiText(doc.stickerEmojiRaw));
if(effectThumb) {
const margin = (size * STICKER_EFFECT_MULTIPLIER - size) / 3 * (isOut ? 1 : -1);
transformer.classList.add('has-effect');
// const property = `--margin-${isOut ? 'right' : 'left'}`;
// stickerContainer.style.setProperty(property, `${margin * 2}px`);
transformer.style.setProperty('--translateX', `${margin}px`);
stickerEmoji.style.setProperty('--translateX', `${-margin}px`);
}
const overflowElement = findUpClassName(mediaContainer, 'scrollable');
const visibleRect = getVisibleRect(mediaContainer, overflowElement, true, mediaRect);
if(visibleRect.overflow.vertical || visibleRect.overflow.horizontal) {
stickerContainer.classList.add('is-overflow');
}
// if(img) {
// const ratio = img.naturalWidth / img.naturalHeight;
// if((mediaRect.width / mediaRect.height).toFixed(1) !== ratio.toFixed(1)) {
// mediaRect = mediaRect.toJSON();
// }
// }
const rect = mediaContainer.getBoundingClientRect();
const scaleX = rect.width / fitted.width;
const scaleY = rect.height / fitted.height;
const transformX = rect.left - (windowSize.width - rect.width) / 2;
const transformY = rect.top - (windowSize.height - rect.height) / 2;
transformer.style.transform = `translate(${transformX}px, ${transformY}px) scale(${scaleX}, ${scaleY})`;
if(isSwitching) transformer.classList.add('is-switching');
transformer.append(stickerContainer, stickerEmoji);
container.append(transformer);
const player = await wrapSticker({
doc,
div: stickerContainer,
group,
width: fitted.width,
height: fitted.height,
play: false,
loop: true,
middleware,
managers,
needFadeIn: false,
isOut,
withThumb: false,
relativeEffect: true,
loopEffect: true
}).then(({render}) => render);
if(!middleware()) return;
if(!container.parentElement) {
document.body.append(container);
}
const firstFramePromise = player instanceof RLottiePlayer ?
new Promise<void>((resolve) => player.addEventListener('firstFrame', resolve, {once: true})) :
Promise.resolve();
await Promise.all([firstFramePromise, doubleRaf()]);
await pause(0); // ! need it because firstFrame will be called just from the loop
if(!middleware()) return;
if(lockGroups) {
animationIntersector.setOnlyOnePlayableGroup(group);
animationIntersector.checkAnimations(true);
}
if(player instanceof RLottiePlayer) {
const prevPlayer = lottieLoader.getAnimation(mediaContainer);
player.curFrame = prevPlayer.curFrame;
player.play();
await new Promise<void>((resolve) => {
let i = 0;
const c = () => {
if(++i === 2) {
resolve();
player.removeEventListener('enterFrame', c);
}
};
player.addEventListener('enterFrame', c);
});
player.pause();
} else if(player instanceof HTMLVideoElement) {
player.currentTime = (mediaContainer.querySelector('video') as HTMLVideoElement).currentTime;
}
return {
ready: () => {
if(player instanceof RLottiePlayer || player instanceof HTMLVideoElement) {
player.play();
}
if(effectThumb) {
simulateClickEvent(stickerContainer);
}
},
transformer
};
};
const timeout = window.setTimeout(async() => {
document.removeEventListener('mousemove', onMousePreMove);
container = document.createElement('div');
container.classList.add(className);
hasViewer = true;
const middleware = _middleware.get();
const doc = await managers.appDocsManager.getDoc(docId);
if(!middleware()) return;
let result: Awaited<ReturnType<typeof doThatSticker>>;
try {
result = await doThatSticker({
doc,
mediaContainer,
middleware,
lockGroups: true
});
if(!result) return;
} catch(err) {
return;
}
const {ready, transformer} = result;
previousTransformer = transformer;
SetTransition(container, 'is-visible', true, openDuration, () => {
if(!middleware()) return;
ready();
});
document.addEventListener('mousemove', onMouseMove);
}, 125);
const onMouseMove = async(e: MouseEvent) => {
const newMediaContainer = findUpClassName(e.target, 'media-sticker-wrapper');
if(!newMediaContainer || mediaContainer === newMediaContainer) {
return;
}
const docId = newMediaContainer.dataset.docId;
if(!docId) {
return;
}
mediaContainer = newMediaContainer;
_middleware.clean();
const middleware = _middleware.get();
const doc = await managers.appDocsManager.getDoc(docId);
if(!middleware()) return;
let r: Awaited<ReturnType<typeof doThatSticker>>;
try {
r = await doThatSticker({
doc,
mediaContainer,
middleware,
isSwitching: true
});
if(!r) return;
} catch(err) {
return;
}
const {ready, transformer} = r;
const _previousTransformer = previousTransformer;
SetTransition(_previousTransformer, 'is-switching', true, switchDuration, () => {
_previousTransformer.remove();
});
previousTransformer = transformer;
SetTransition(transformer, 'is-switching', false, switchDuration, () => {
if(!middleware()) return;
ready();
});
};
const onMousePreMove = (e: MouseEvent) => {
if(!findUpClassName(e.target, findClassName)) {
onMouseUp();
}
};
const onMouseUp = () => {
clearTimeout(timeout);
_middleware.clean();
if(container) {
SetTransition(container, 'is-visible', false, openDuration, () => {
container.remove();
animationIntersector.setOnlyOnePlayableGroup(previousGroup);
animationIntersector.checkAnimations(false);
hasViewer = false;
});
attachClickEvent(document.body, cancelEvent, {capture: true, once: true});
}
document.removeEventListener('mousemove', onMousePreMove);
document.removeEventListener('mousemove', onMouseMove);
};
document.addEventListener('mousemove', onMousePreMove);
document.addEventListener('mouseup', onMouseUp, {once: true});
});
}

12
src/components/wrappers/document.ts

@ -199,12 +199,20 @@ export default async function wrapDocument({message, withTime, fontWeight, voice @@ -199,12 +199,20 @@ export default async function wrapDocument({message, withTime, fontWeight, voice
let downloadDiv: HTMLElement, preloader: ProgressivePreloader = null;
const onLoad = () => {
docDiv.classList.remove('downloading');
if(/* !hasThumb || */(doc.size > MAX_FILE_SAVE_SIZE && !uploadFileName)) {
preloader.setManual();
preloader.attach(downloadDiv);
preloader.preloader.classList.add('manual');
preloader.setDownloadFunction(load);
return;
}
if(doc.size <= MAX_FILE_SAVE_SIZE) {
docDiv.classList.add('downloaded');
}
docDiv.classList.remove('downloading');
if(downloadDiv) {
if(downloadDiv !== icoDiv) {
const _downloadDiv = downloadDiv;

3
src/lib/appManagers/appDialogsManager.ts

@ -518,8 +518,7 @@ export class AppDialogsManager { @@ -518,8 +518,7 @@ export class AppDialogsManager {
});
rootScope.addEventListener('dialogs_multiupdate', (dialogs) => {
for(const peerId in dialogs) {
const dialog = dialogs[peerId];
for(const [peerId, dialog] of dialogs) {
this.updateDialog(dialog);
if(this.processContact) {

261
src/lib/appManagers/appImManager.ts

@ -415,267 +415,6 @@ export class AppImManager extends EventListenerBase<{ @@ -415,267 +415,6 @@ export class AppImManager extends EventListenerBase<{
}, useRafs);
};
let hasViewer = false;
!IS_TOUCH_SUPPORTED && document.addEventListener('mousedown', (e) => {
if(hasViewer || e.buttons > 1 || e.button !== 0) return;
let mediaContainer = findUpClassName(e.target, 'media-sticker-wrapper');
if(!mediaContainer) {
return;
}
// const img: HTMLImageElement = mediaContainer.querySelector('img.media-sticker');
const docId = mediaContainer.dataset.docId;
if(!docId) {
return;
}
const className = 'sticker-viewer';
const group: AnimationItemGroup = 'STICKER-VIEWER';
const openDuration = 200;
const switchDuration = 200;
const previousGroup = animationIntersector.getOnlyOnePlayableGroup();
const _middleware = getMiddleware();
let container: HTMLElement, previousTransformer: HTMLElement;
const doThatSticker = async({mediaContainer, doc, middleware, lockGroups, isSwitching}: {
mediaContainer: HTMLElement,
doc: MyDocument,
middleware: () => boolean,
lockGroups?: boolean,
isSwitching?: boolean
}) => {
const effectThumb = getStickerEffectThumb(doc);
const mediaRect: DOMRect = mediaContainer.getBoundingClientRect();
const s = makeMediaSize(doc.w, doc.h);
const size = effectThumb ? 280 : 360;
const boxSize = makeMediaSize(size, size);
const fitted = mediaRect.width === mediaRect.height ? boxSize : s.aspectFitted(boxSize);
const bubble = findUpClassName(mediaContainer, 'bubble');
const isOut = bubble ? bubble.classList.contains('is-out') : true;
const transformer = document.createElement('div');
transformer.classList.add(className + '-transformer');
const stickerContainer = document.createElement('div');
stickerContainer.classList.add(className + '-sticker');
/* transformer.style.width = */stickerContainer.style.width = fitted.width + 'px';
/* transformer.style.height = */stickerContainer.style.height = fitted.height + 'px';
const stickerEmoji = document.createElement('div');
stickerEmoji.classList.add(className + '-emoji');
stickerEmoji.append(wrapEmojiText(doc.stickerEmojiRaw));
if(effectThumb) {
const margin = (size * STICKER_EFFECT_MULTIPLIER - size) / 3 * (isOut ? 1 : -1);
transformer.classList.add('has-effect');
// const property = `--margin-${isOut ? 'right' : 'left'}`;
// stickerContainer.style.setProperty(property, `${margin * 2}px`);
transformer.style.setProperty('--translateX', `${margin}px`);
stickerEmoji.style.setProperty('--translateX', `${-margin}px`);
}
const overflowElement = findUpClassName(mediaContainer, 'scrollable');
const visibleRect = getVisibleRect(mediaContainer, overflowElement, true, mediaRect);
if(visibleRect.overflow.vertical || visibleRect.overflow.horizontal) {
stickerContainer.classList.add('is-overflow');
}
// if(img) {
// const ratio = img.naturalWidth / img.naturalHeight;
// if((mediaRect.width / mediaRect.height).toFixed(1) !== ratio.toFixed(1)) {
// mediaRect = mediaRect.toJSON();
// }
// }
const rect = mediaContainer.getBoundingClientRect();
const scaleX = rect.width / fitted.width;
const scaleY = rect.height / fitted.height;
const transformX = rect.left - (windowSize.width - rect.width) / 2;
const transformY = rect.top - (windowSize.height - rect.height) / 2;
transformer.style.transform = `translate(${transformX}px, ${transformY}px) scale(${scaleX}, ${scaleY})`;
if(isSwitching) transformer.classList.add('is-switching');
transformer.append(stickerContainer, stickerEmoji);
container.append(transformer);
const player = await wrapSticker({
doc,
div: stickerContainer,
group,
width: fitted.width,
height: fitted.height,
play: false,
loop: true,
middleware,
managers: this.managers,
needFadeIn: false,
isOut,
withThumb: false,
relativeEffect: true,
loopEffect: true
}).then(({render}) => render);
if(!middleware()) return;
if(!container.parentElement) {
document.body.append(container);
}
const firstFramePromise = player instanceof RLottiePlayer ?
new Promise<void>((resolve) => player.addEventListener('firstFrame', resolve, {once: true})) :
Promise.resolve();
await Promise.all([firstFramePromise, doubleRaf()]);
await pause(0); // ! need it because firstFrame will be called just from the loop
if(!middleware()) return;
if(lockGroups) {
animationIntersector.setOnlyOnePlayableGroup(group);
animationIntersector.checkAnimations(true);
}
if(player instanceof RLottiePlayer) {
const prevPlayer = lottieLoader.getAnimation(mediaContainer);
player.curFrame = prevPlayer.curFrame;
player.play();
await new Promise<void>((resolve) => {
let i = 0;
const c = () => {
if(++i === 2) {
resolve();
player.removeEventListener('enterFrame', c);
}
};
player.addEventListener('enterFrame', c);
});
player.pause();
} else if(player instanceof HTMLVideoElement) {
player.currentTime = (mediaContainer.querySelector('video') as HTMLVideoElement).currentTime;
}
return {
ready: () => {
if(player instanceof RLottiePlayer || player instanceof HTMLVideoElement) {
player.play();
}
if(effectThumb) {
simulateClickEvent(stickerContainer);
}
},
transformer
};
};
const timeout = window.setTimeout(async() => {
document.removeEventListener('mousemove', onMousePreMove);
container = document.createElement('div');
container.classList.add(className);
hasViewer = true;
const middleware = _middleware.get();
const doc = await this.managers.appDocsManager.getDoc(docId);
if(!middleware()) return;
let result: Awaited<ReturnType<typeof doThatSticker>>;
try {
result = await doThatSticker({
doc,
mediaContainer,
middleware,
lockGroups: true
});
if(!result) return;
} catch(err) {
return;
}
const {ready, transformer} = result;
previousTransformer = transformer;
SetTransition(container, 'is-visible', true, openDuration, () => {
if(!middleware()) return;
ready();
});
document.addEventListener('mousemove', onMouseMove);
}, 100);
const onMouseMove = async(e: MouseEvent) => {
const newMediaContainer = findUpClassName(e.target, 'media-sticker-wrapper');
if(!newMediaContainer || mediaContainer === newMediaContainer) {
return;
}
const docId = newMediaContainer.dataset.docId;
if(!docId) {
return;
}
mediaContainer = newMediaContainer;
_middleware.clean();
const middleware = _middleware.get();
const doc = await this.managers.appDocsManager.getDoc(docId);
if(!middleware()) return;
let r: Awaited<ReturnType<typeof doThatSticker>>;
try {
r = await doThatSticker({
doc,
mediaContainer,
middleware,
isSwitching: true
});
if(!r) return;
} catch(err) {
return;
}
const {ready, transformer} = r;
const _previousTransformer = previousTransformer;
SetTransition(_previousTransformer, 'is-switching', true, switchDuration, () => {
_previousTransformer.remove();
});
previousTransformer = transformer;
SetTransition(transformer, 'is-switching', false, switchDuration, () => {
if(!middleware()) return;
ready();
});
};
const onMousePreMove = () => {
clearTimeout(timeout);
};
const onMouseUp = () => {
clearTimeout(timeout);
_middleware.clean();
if(container) {
SetTransition(container, 'is-visible', false, openDuration, () => {
container.remove();
animationIntersector.setOnlyOnePlayableGroup(previousGroup);
animationIntersector.checkAnimations(false);
hasViewer = false;
});
attachClickEvent(document.body, cancelEvent, {capture: true, once: true});
}
document.removeEventListener('mousemove', onMouseMove);
};
document.addEventListener('mousemove', onMousePreMove, {once: true});
document.addEventListener('mouseup', onMouseUp, {once: true});
});
rootScope.addEventListener('sticker_updated', ({type, faved}) => {
if(type === 'faved') {
toastNew({

98
src/lib/appManagers/appMessagesManager.ts

@ -196,7 +196,7 @@ export class AppMessagesManager extends AppManager { @@ -196,7 +196,7 @@ export class AppMessagesManager extends AppManager {
public migratedToFrom: {[peerId: PeerId]: PeerId} = {};
private newDialogsHandlePromise: Promise<any>;
private newDialogsToHandle: {[peerId: PeerId]: Dialog} = {};
private newDialogsToHandle: Map<PeerId, Dialog> = new Map();
public newUpdatesAfterReloadToHandle: {[peerId: PeerId]: Set<Update>} = {};
private notificationsHandlePromise = 0;
@ -1907,7 +1907,7 @@ export class AppMessagesManager extends AppManager { @@ -1907,7 +1907,7 @@ export class AppMessagesManager extends AppManager {
let maxSeenIdIncremented = offsetDate ? true : false;
let hasPrepend = false;
const noIdsDialogs: {[peerId: PeerId]: Dialog} = {};
const noIdsDialogs: Map<PeerId, Dialog> = new Map();
const setFolderId: REAL_FOLDER_ID = folderId === GLOBAL_FOLDER_ID ? FOLDER_ID_ALL : folderId;
const saveGlobalOffset = folderId === GLOBAL_FOLDER_ID;
forEachReverse((dialogsResult.dialogs as Dialog[]), (dialog) => {
@ -1941,16 +1941,15 @@ export class AppMessagesManager extends AppManager { @@ -1941,16 +1941,15 @@ export class AppMessagesManager extends AppManager {
// ! это может случиться, если запрос идёт не по папке 0, а по 1. почему-то read'ов нет
// ! в итоге, чтобы получить 1 диалог, делается первый запрос по папке 0, потом запрос для архивных по папке 1, и потом ещё перезагрузка архивного диалога
if(!getServerMessageId(dialog.read_inbox_max_id) && !getServerMessageId(dialog.read_outbox_max_id)) {
noIdsDialogs[dialog.peerId] = dialog;
noIdsDialogs.set(dialog.peerId, dialog);
this.log.error('noIdsDialogs', dialog, params);
}
});
const keys = Object.keys(noIdsDialogs);
if(keys.length) {
if(noIdsDialogs.size) {
// setTimeout(() => { // test bad situation
const peerIds = keys.map((key) => key.toPeerId());
const peerIds = [...noIdsDialogs.keys()];
const promises = peerIds.map((peerId) => this.reloadConversation(peerId));
Promise.all(promises).then(() => {
this.rootScope.dispatchEvent('dialogs_multiupdate', noIdsDialogs);
@ -1988,7 +1987,7 @@ export class AppMessagesManager extends AppManager { @@ -1988,7 +1987,7 @@ export class AppMessagesManager extends AppManager {
if(hasPrepend) {
this.scheduleHandleNewDialogs();
} else {
this.rootScope.dispatchEvent('dialogs_multiupdate', {});
this.rootScope.dispatchEvent('dialogs_multiupdate', new Map());
}
const dialogs = (dialogsResult as MessagesDialogs.messagesDialogsSlice).dialogs;
@ -2402,31 +2401,7 @@ export class AppMessagesManager extends AppManager { @@ -2402,31 +2401,7 @@ export class AppMessagesManager extends AppManager {
}
return this.doFlushHistory(this.appPeersManager.getInputPeerById(peerId), justClear, revoke).then(() => {
[
this.historiesStorage,
this.threadsStorage,
this.searchesStorage,
this.pinnedMessages,
this.pendingAfterMsgs,
this.pendingTopMsgs
].forEach((s) => {
delete s[peerId];
});
const m = this.needSingleMessages.get(peerId);
if(m) {
m.clear();
}
[
this.messagesStorageByPeerId,
this.scheduledMessagesStorage
].forEach((s) => {
const ss = s[peerId];
if(ss) {
ss.clear();
}
});
this.flushStoragesByPeerId(peerId);
if(justClear) {
this.rootScope.dispatchEvent('dialog_flush', {peerId, dialog: this.getDialogOnly(peerId)});
@ -2445,6 +2420,38 @@ export class AppMessagesManager extends AppManager { @@ -2445,6 +2420,38 @@ export class AppMessagesManager extends AppManager {
});
}
private flushStoragesByPeerId(peerId: PeerId) {
[
this.historiesStorage,
this.threadsStorage,
this.searchesStorage,
this.pinnedMessages,
this.pendingAfterMsgs,
this.pendingTopMsgs
].forEach((s) => {
delete s[peerId];
});
const needSingleMessages = this.needSingleMessages.get(peerId);
if(needSingleMessages) {
for(const [mid, promise] of needSingleMessages) {
promise.resolve(this.generateEmptyMessage(mid));
}
needSingleMessages.clear();
}
[
this.messagesStorageByPeerId,
this.scheduledMessagesStorage
].forEach((s) => {
const ss = s[peerId];
if(ss) {
ss.clear();
}
});
}
public hidePinnedMessages(peerId: PeerId) {
return Promise.all([
this.appStateManager.getState(),
@ -3558,12 +3565,11 @@ export class AppMessagesManager extends AppManager { @@ -3558,12 +3565,11 @@ export class AppMessagesManager extends AppManager {
private handleNewDialogs = () => {
let newMaxSeenId = 0;
const obj = this.newDialogsToHandle;
for(const peerId in obj) {
const dialog = obj[peerId];
const map = this.newDialogsToHandle;
for(const [peerId, dialog] of map) {
if(!dialog) {
this.reloadConversation(peerId.toPeerId());
delete obj[peerId];
map.delete(peerId);
} else {
this.dialogsStorage.pushDialog(dialog);
if(!this.appPeersManager.isChannel(peerId.toPeerId())) {
@ -3578,13 +3584,13 @@ export class AppMessagesManager extends AppManager { @@ -3578,13 +3584,13 @@ export class AppMessagesManager extends AppManager {
this.incrementMaxSeenId(newMaxSeenId);
}
this.rootScope.dispatchEvent('dialogs_multiupdate', obj);
this.newDialogsToHandle = {};
this.rootScope.dispatchEvent('dialogs_multiupdate', map);
this.newDialogsToHandle.clear();
};
public scheduleHandleNewDialogs(peerId?: PeerId, dialog?: Dialog) {
if(peerId !== undefined) {
this.newDialogsToHandle[peerId] = dialog;
this.newDialogsToHandle.set(peerId, dialog);
}
if(this.newDialogsHandlePromise) return this.newDialogsHandlePromise;
@ -4263,7 +4269,7 @@ export class AppMessagesManager extends AppManager { @@ -4263,7 +4269,7 @@ export class AppMessagesManager extends AppManager {
releaseUnreadCount();
this.dialogsStorage.setDialogToState(dialog);
this.rootScope.dispatchEvent('dialogs_multiupdate', {[peerId]: dialog});
this.rootScope.dispatchEvent('dialogs_multiupdate', new Map([[peerId, dialog]]));
}
};
@ -4321,10 +4327,8 @@ export class AppMessagesManager extends AppManager { @@ -4321,10 +4327,8 @@ export class AppMessagesManager extends AppManager {
});
if(isTopMessage || (message as Message.message).grouped_id) {
const updatedDialogs: {[peerId: PeerId]: Dialog} = {};
updatedDialogs[peerId] = dialog;
this.dialogsStorage.setDialogToState(dialog);
this.rootScope.dispatchEvent('dialogs_multiupdate', updatedDialogs);
this.rootScope.dispatchEvent('dialogs_multiupdate', new Map([[peerId, dialog]]));
}
}
};
@ -4599,9 +4603,7 @@ export class AppMessagesManager extends AppManager { @@ -4599,9 +4603,7 @@ export class AppMessagesManager extends AppManager {
private onUpdateChannelReload = (update: Update.updateChannelReload) => {
const peerId = update.channel_id.toPeerId(true);
this.dialogsStorage.dropDialog(peerId);
delete this.historiesStorage[peerId];
this.flushStoragesByPeerId(peerId);
this.reloadConversation(peerId).then(() => {
this.rootScope.dispatchEvent('history_reload', peerId);
});
@ -5651,9 +5653,7 @@ export class AppMessagesManager extends AppManager { @@ -5651,9 +5653,7 @@ export class AppMessagesManager extends AppManager {
});
if(this.isMessageIsTopMessage(message)) {
this.rootScope.dispatchEvent('dialogs_multiupdate', {
[peerId]: this.getDialogOnly(peerId)
});
this.rootScope.dispatchEvent('dialogs_multiupdate', new Map([[peerId, this.getDialogOnly(peerId)]]));
}
}

4
src/lib/appManagers/appStickersManager.ts

@ -18,6 +18,7 @@ import fixEmoji from '../richTextProcessor/fixEmoji'; @@ -18,6 +18,7 @@ import fixEmoji from '../richTextProcessor/fixEmoji';
import ctx from '../../environment/ctx';
import {getEnvironment} from '../../environment/utils';
import getDocumentInput from './utils/docs/getDocumentInput';
import getStickerEffectThumb from './utils/stickers/getStickerEffectThumb';
const CACHE_TIME = 3600e3;
@ -673,7 +674,8 @@ export class AppStickersManager extends AppManager { @@ -673,7 +674,8 @@ export class AppStickersManager extends AppManager {
const stickers = [...new Set(cachedStickersAnimated.concat(cachedStickersStatic, foundStickers))]/* .filter((doc) => !doc.animated) */;
forEachReverse(stickers, (sticker, idx, arr) => {
if(sticker.sticker === 3 && !getEnvironment().IS_WEBM_SUPPORTED) {
if((sticker.sticker === 3 && !getEnvironment().IS_WEBM_SUPPORTED) ||
(!this.rootScope.premium && getStickerEffectThumb(sticker))) {
arr.splice(idx, 1);
}
});

2
src/lib/rootScope.ts

@ -58,7 +58,7 @@ export type BroadcastEvents = { @@ -58,7 +58,7 @@ export type BroadcastEvents = {
// 'dialog_top': Dialog,
'dialog_notify_settings': Dialog,
// 'dialog_order': {dialog: Dialog, pos: number},
'dialogs_multiupdate': {[peerId: PeerId]: Dialog},
'dialogs_multiupdate': Map<PeerId, Dialog>,
'history_append': {storageKey: MessagesStorageKey, message: Message.message},
'history_update': {storageKey: MessagesStorageKey, message: MyMessage, sequential?: boolean},

2
src/lib/serviceWorker/download.ts

@ -26,6 +26,8 @@ const downloadMap: Map<string, DownloadItem> = new Map(); @@ -26,6 +26,8 @@ const downloadMap: Map<string, DownloadItem> = new Map();
const DOWNLOAD_ERROR = makeError('UNKNOWN');
const DOWNLOAD_TEST = false;
(self as any).downloadMap = downloadMap;
type A = Parameters<ServiceMessagePort<false>['addMultipleEventsListeners']>[0];
const events: A = {

2
src/lib/serviceWorker/index.service.ts

@ -20,7 +20,7 @@ import {getWindowClients} from '../../helpers/context'; @@ -20,7 +20,7 @@ import {getWindowClients} from '../../helpers/context';
import {MessageSendPort} from '../mtproto/superMessagePort';
import handleDownload from './download';
export const log = logger('SW', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn);
export const log = logger('SW', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn, true);
const ctx = self as any as ServiceWorkerGlobalScope;
// #if !MTPROTO_SW

2
src/lib/serviceWorker/stream.ts

@ -18,7 +18,7 @@ const deferredPromises: Map<MessagePort, {[taskId: string]: CancellablePromise<M @@ -18,7 +18,7 @@ const deferredPromises: Map<MessagePort, {[taskId: string]: CancellablePromise<M
const cacheStorage = new CacheStorageController('cachedStreamChunks');
const CHUNK_TTL = 86400;
const CHUNK_CACHED_TIME_HEADER = 'Time-Cached';
const USE_CACHE = false;
const USE_CACHE = true;
const clearOldChunks = () => {
return cacheStorage.timeoutOperation((cache) => {

32
src/lib/storages/dialogs.ts

@ -361,16 +361,18 @@ export default class DialogsStorage extends AppManager { @@ -361,16 +361,18 @@ export default class DialogsStorage extends AppManager {
private setDialogIndexInFilter(dialog: Dialog, indexKey: ReturnType<typeof getDialogIndexKey>, filter: MyDialogFilter) {
let index: number;
if(REAL_FOLDERS.has(filter.id)) {
index = getDialogIndex(dialog, indexKey);
} else if(this.filtersStorage.testDialogForFilter(dialog, filter)) {
const isRealFolder = REAL_FOLDERS.has(filter.id);
/* if(isRealFolder) {
// index = getDialogIndex(dialog, indexKey);
index = this.generateIndexForDialog(dialog, true);
} else */if(this.filtersStorage.testDialogForFilter(dialog, filter)) {
const pinnedIndex = filter.pinnedPeerIds.indexOf(dialog.peerId);
if(pinnedIndex !== -1) {
index = this.generateDialogIndex(this.generateDialogPinnedDateByIndex(filter.pinnedPeerIds.length - 1 - pinnedIndex), true);
} else if(dialog.pFlags?.pinned) {
index = this.generateIndexForDialog(dialog, true);
} else if(dialog.pFlags?.pinned || isRealFolder) {
index = this.generateIndexForDialog(dialog, true, undefined, !isRealFolder);
} else {
index = getDialogIndex(dialog);
index = getDialogIndex(dialog) ?? this.generateIndexForDialog(dialog, true);
}
}
@ -541,13 +543,13 @@ export default class DialogsStorage extends AppManager { @@ -541,13 +543,13 @@ export default class DialogsStorage extends AppManager {
}
}
public generateIndexForDialog(dialog: Dialog, justReturn = false, message?: MyMessage) {
// if(!justReturn) {
// return;
// }
public generateIndexForDialog(dialog: Dialog, justReturn?: boolean, message?: MyMessage, noPinnedOrderUpdate?: boolean) {
if(!justReturn) {
return;
}
let topDate = 0, isPinned: boolean;
if(dialog.pFlags.pinned && !justReturn) {
if(dialog.pFlags.pinned && !noPinnedOrderUpdate) {
topDate = this.generateDialogPinnedDate(dialog);
isPinned = true;
} else {
@ -784,7 +786,7 @@ export default class DialogsStorage extends AppManager { @@ -784,7 +786,7 @@ export default class DialogsStorage extends AppManager {
// this.appMessagesManager.log('applyConversation', dialogsResult);
const updatedDialogs: {[peerId: PeerId]: Dialog} = {};
const updatedDialogs: Map<PeerId, Dialog> = new Map();
(dialogsResult.dialogs as Dialog[]).forEach((dialog) => {
const peerId = this.appPeersManager.getPeerId(dialog.peer);
let topMessage = dialog.top_message;
@ -805,7 +807,7 @@ export default class DialogsStorage extends AppManager { @@ -805,7 +807,7 @@ export default class DialogsStorage extends AppManager {
if(topMessage || dialog.draft?._ === 'draftMessage') {
this.saveDialog(dialog);
updatedDialogs[peerId] = dialog;
updatedDialogs.set(peerId, dialog);
} else {
this.dropDialogWithEvent(peerId);
}
@ -823,7 +825,7 @@ export default class DialogsStorage extends AppManager { @@ -823,7 +825,7 @@ export default class DialogsStorage extends AppManager {
}
});
if(Object.keys(updatedDialogs).length) {
if(updatedDialogs.size) {
this.rootScope.dispatchEvent('dialogs_multiupdate', updatedDialogs);
}
}
@ -1158,6 +1160,8 @@ export default class DialogsStorage extends AppManager { @@ -1158,6 +1160,8 @@ export default class DialogsStorage extends AppManager {
const handleOrder = (order: PeerId[]) => {
this.resetPinnedOrder(folderId);
this.pinnedOrders[folderId].push(...order);
this.savePinnedOrders();
order.reverse(); // index must be higher
order.forEach((peerId) => {
newPinned[peerId] = true;

Loading…
Cancel
Save