diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index a92e13ca..015bdfaa 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -49,6 +49,13 @@ import { MiddleEllipsisElement } from './middleEllipsis'; import { joinElementsWith } from '../lib/langPack'; import throttleWithRaf from '../helpers/schedulers/throttleWithRaf'; import { NULL_PEER_ID } from '../lib/mtproto/mtproto_config'; +import findUpClassName from '../helpers/dom/findUpClassName'; +import RLottiePlayer from '../lib/rlottie/rlottiePlayer'; +import assumeType from '../helpers/assumeType'; +import appMessagesIdsManager from '../lib/appManagers/appMessagesIdsManager'; +import throttle from '../helpers/schedulers/throttle'; +import { SendMessageEmojiInteractionData } from '../types'; +import IS_VIBRATE_SUPPORTED from '../environment/vibrateSupport'; const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB @@ -1127,7 +1134,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o loadPromises?: Promise[], needFadeIn?: boolean, needUpscale?: boolean -}) { +}): Promise { const stickerType = doc.sticker; if(!width) { @@ -1160,7 +1167,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o let loadThumbPromise = deferredPromise(); let haveThumbCached = false; - if((doc.thumbs?.length || doc.stickerCachedThumbs) && !div.firstElementChild && (!downloaded || stickerType === 2 || onlyThumb)/* && doc.thumbs[0]._ !== 'photoSizeEmpty' */) { + if((doc.thumbs?.length || doc.stickerCachedThumbs) && !div.firstElementChild && (!downloaded || stickerType === 2 || onlyThumb) && withThumb !== false/* && doc.thumbs[0]._ !== 'photoSizeEmpty' */) { let thumb = doc.stickerCachedThumbs && doc.stickerCachedThumbs[toneIndex] || doc.thumbs[0]; //console.log('wrap sticker', thumb, div); @@ -1267,7 +1274,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o //appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => { //fetch(doc.url).then(res => res.json()).then(async(json) => { - /* return */ await appDocsManager.downloadDoc(doc, /* undefined, */lazyLoadQueue?.queueId) + return await appDocsManager.downloadDoc(doc, /* undefined, */lazyLoadQueue?.queueId) .then(readBlobAsText) //.then(JSON.parse) .then(async(json) => { @@ -1324,6 +1331,13 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o }, {once: true}); if(emoji) { + const data: SendMessageEmojiInteractionData = { + a: [], + v: 1 + }; + + let sendInteractionThrottled: () => void; + attachClickEvent(div, (e) => { cancelEvent(e); let animation = LottieLoader.getAnimation(div); @@ -1332,9 +1346,134 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o animation.autoplay = true; animation.restart(); } + + const doc = appStickersManager.getAnimatedEmojiSticker(emoji, true); + if(!doc) { + return; + } + + const animationDiv = document.createElement('div'); + animationDiv.classList.add('emoji-animation'); + + const size = 280; + animationDiv.style.width = size + 'px'; + animationDiv.style.height = size + 'px'; + + wrapSticker({ + div: animationDiv, + doc, + middleware, + withThumb: false, + needFadeIn: false, + loop: false, + width: size, + height: size, + play: true, + group: 'none' + }).then(animation => { + assumeType(animation); + animation.addEventListener('enterFrame', (frameNo) => { + if(frameNo === animation.maxFrame) { + animation.remove(); + // animationDiv.remove(); + appImManager.chat.bubbles.scrollable.container.removeEventListener('scroll', onScroll); + } + }); + + if(IS_VIBRATE_SUPPORTED) { + animation.addEventListener('firstFrame', () => { + navigator.vibrate(100); + }, {once: true}); + } + }); + + const generateRandomSigned = (max: number) => { + const r = Math.random() * max * 2; + return r > max ? -r % max : r; + }; + + const bubble = findUpClassName(div, 'bubble'); + const isOut = bubble.classList.contains('is-out'); + + const randomOffsetX = generateRandomSigned(16); + const randomOffsetY = generateRandomSigned(4); + const stableOffsetX = size / 8 * (isOut ? 1 : -1); + const setPosition = () => { + const rect = div.getBoundingClientRect(); + /* const boxWidth = Math.max(rect.width, rect.height); + const boxHeight = Math.max(rect.width, rect.height); + const x = rect.left + ((boxWidth - size) / 2); + const y = rect.top + ((boxHeight - size) / 2); */ + + const rectX = isOut ? rect.right : rect.left; + + const addOffsetX = (isOut ? -size : 0) + stableOffsetX + randomOffsetX; + const x = rectX + addOffsetX; + // const y = rect.bottom - size + size / 4; + const y = rect.top + ((rect.height - size) / 2) + randomOffsetY; + // animationDiv.style.transform = `translate(${x}px, ${y}px)`; + animationDiv.style.top = y + 'px'; + animationDiv.style.left = x + 'px'; + }; + + const onScroll = throttleWithRaf(setPosition); + + appImManager.chat.bubbles.scrollable.container.addEventListener('scroll', onScroll); + + setPosition(); + + if(bubble) { + if(isOut) { + animationDiv.classList.add('is-out'); + } else { + animationDiv.classList.add('is-in'); + } + } + + appImManager.emojiAnimationContainer.append(animationDiv); + + if(!sendInteractionThrottled) { + sendInteractionThrottled = throttle(() => { + const length = data.a.length; + if(!length) { + return; + } + + const firstTime = data.a[0].t; + + data.a.forEach((a) => { + a.t = (a.t - firstTime) / 1000; + }); + + const bubble = findUpClassName(div, 'bubble'); + appMessagesManager.setTyping(appImManager.chat.peerId, { + _: 'sendMessageEmojiInteraction', + msg_id: appMessagesIdsManager.getServerMessageId(+bubble.dataset.mid), + emoticon: emoji, + interaction: { + _: 'dataJSON', + data: JSON.stringify(data) + } + }, true); + + data.a.length = 0; + }, 1000, false); + } + + // using a trick here: simulated event from interlocutor's interaction won't fire ours + if(e.isTrusted) { + data.a.push({ + i: 1, + t: Date.now() + }); + + sendInteractionThrottled(); + } }); } + return animation; + //return deferred; //await new Promise((resolve) => setTimeout(resolve, 5e3)); }); @@ -1384,12 +1523,12 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o } }; - const loadPromise: Promise = lazyLoadQueue && (!downloaded || stickerType === 2) ? + const loadPromise: Promise = lazyLoadQueue && (!downloaded || stickerType === 2) ? (lazyLoadQueue.push({div, load}), Promise.resolve()) : load(); if(downloaded && stickerType === 1) { - loadThumbPromise = loadPromise; + loadThumbPromise = loadPromise as any; if(loadPromises) { loadPromises.push(loadThumbPromise); } diff --git a/src/environment/vibrateSupport.ts b/src/environment/vibrateSupport.ts new file mode 100644 index 00000000..d3ebb671 --- /dev/null +++ b/src/environment/vibrateSupport.ts @@ -0,0 +1,3 @@ +const IS_VIBRATE_SUPPORTED = !!navigator?.vibrate; + +export default IS_VIBRATE_SUPPORTED; diff --git a/src/lang.ts b/src/lang.ts index 589d6f4b..1dde97b8 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -854,6 +854,7 @@ const lang = { "Peer.Activity.User.RecordingAudio": "recording voice", "Peer.Activity.User.SendingFile": "sending file", "Peer.Activity.User.ChoosingSticker": "choosing a sticker", + "Peer.Activity.User.EnjoyingAnimations": "watching %@", "Peer.Activity.Chat.PlayingGame": "%@ is playing a game", "Peer.Activity.Chat.TypingText": "%@ is typing", "Peer.Activity.Chat.SendingPhoto": "%@ is sending a photo", @@ -862,6 +863,7 @@ const lang = { "Peer.Activity.Chat.RecordingAudio": "%@ is recording voice", "Peer.Activity.Chat.SendingFile": "%@ is sending a file", "Peer.Activity.Chat.ChoosingSticker": "%@ is choosing a sticker", + "Peer.Activity.Chat.EnjoyingAnimations": "%@ is watching %@", "Peer.Activity.Chat.Multi.PlayingGame1": "%@ and %d others are playing a game", "Peer.Activity.Chat.Multi.TypingText1": "%@ and %d others are typing", "Peer.Activity.Chat.Multi.SendingPhoto1": "%@ and %d others are sending photos", diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 50923365..33edcc9d 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -1899,12 +1899,10 @@ export class AppDialogsManager { return; } - let typingElement = dom.lastMessageSpan.querySelector('.peer-typing-container') as HTMLElement; - if(typingElement) { - appImManager.getPeerTyping(dialog.peerId, typingElement); - } else { - typingElement = appImManager.getPeerTyping(dialog.peerId); - replaceContent(dom.lastMessageSpan, typingElement); + const oldTypingElement = dom.lastMessageSpan.querySelector('.peer-typing-container') as HTMLElement; + const newTypingElement = appImManager.getPeerTyping(dialog.peerId, oldTypingElement); + if(!oldTypingElement && newTypingElement) { + replaceContent(dom.lastMessageSpan, newTypingElement); dom.lastMessageSpan.classList.add('user-typing'); } } diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 8436cb76..d39b5515 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -79,7 +79,10 @@ import IS_GROUP_CALL_SUPPORTED from '../../environment/groupCallSupport'; import appAvatarsManager from './appAvatarsManager'; import IS_CALL_SUPPORTED from '../../environment/callSupport'; import { CallType } from '../calls/types'; -import { Modify } from '../../types'; +import { Modify, SendMessageEmojiInteractionData } from '../../types'; +import htmlToSpan from '../../helpers/dom/htmlToSpan'; +import getVisibleRect from '../../helpers/dom/getVisibleRect'; +import { simulateClickEvent } from '../../helpers/dom/clickEvent'; //console.log('appImManager included33!'); @@ -127,6 +130,7 @@ export class AppImManager { private backgroundPromises: {[slug: string]: Promise} = {}; private topbarCall: TopbarCall; + emojiAnimationContainer: HTMLDivElement; get myId() { return rootScope.myId; @@ -177,6 +181,10 @@ export class AppImManager { this.chatsContainer.classList.add('chats-container', 'tabs-container'); this.chatsContainer.dataset.animation = 'navigation'; + this.emojiAnimationContainer = document.createElement('div'); + this.emojiAnimationContainer.classList.add('emoji-animation-container'); + document.body.append(this.emojiAnimationContainer); + this.columnEl.append(this.chatsContainer); this.createNewChat(); @@ -204,6 +212,12 @@ export class AppImManager { && document.body.classList.contains(RIGHT_COLUMN_ACTIVE_CLASSNAME)) { appSidebarRight.toggleSidebar(false); } + + if(from === ScreenSize.mobile) { + document.body.append(this.emojiAnimationContainer); + } else if(to === ScreenSize.mobile) { + this.columnEl.append(this.emojiAnimationContainer); + } }); rootScope.addEventListener('history_focus', (e) => { @@ -232,6 +246,41 @@ export class AppImManager { this.setChoosingStickerTyping(!choosing); }); + rootScope.addEventListener('peer_typings', ({peerId, typings}) => { + const chat = this.chat; + if( + !chat || + chat.peerId !== peerId || + rootScope.overlaysActive || ( + mediaSizes.activeScreen === ScreenSize.mobile && + this.tabId !== 1 + ) + ) { + return; + } + + const typing = typings.find(typing => typing.action._ === 'sendMessageEmojiInteraction'); + if(typing?.action?._ === 'sendMessageEmojiInteraction') { + const action = typing.action; + const bubble = chat.bubbles.bubbles[appMessagesIdsManager.generateMessageId(typing.action.msg_id)]; + if(bubble && getVisibleRect(bubble, chat.bubbles.scrollable.container)) { + const stickerWrapper: HTMLElement = bubble.querySelector('.media-sticker-wrapper'); + + const data: SendMessageEmojiInteractionData = JSON.parse(action.interaction.data); + data.a.forEach(a => { + setTimeout(() => { + simulateClickEvent(stickerWrapper); + }, a.t * 1000); + }); + + appMessagesManager.setTyping(peerId, { + _: 'sendMessageEmojiInteractionSeen', + emoticon: action.emoticon + }); + } + } + }); + rootScope.addEventListener('instance_deactivated', () => { const popup = new PopupElement('popup-instance-deactivated', undefined, {overlayClosable: true}); const c = document.createElement('div'); @@ -1599,6 +1648,7 @@ export class AppImManager { break; } + case 'sendMessageEmojiInteractionSeen': case 'sendMessageChooseStickerAction': { c += '-choosing-sticker'; for(let i = 0; i < 2; ++i) { @@ -1638,7 +1688,8 @@ export class AppImManager { 'sendMessageRecordAudioAction': 'Peer.Activity.User.RecordingAudio', 'sendMessageRecordRoundAction': 'Peer.Activity.User.RecordingVideo', 'sendMessageGamePlayAction': 'Peer.Activity.User.PlayingGame', - 'sendMessageChooseStickerAction': 'Peer.Activity.User.ChoosingSticker' + 'sendMessageChooseStickerAction': 'Peer.Activity.User.ChoosingSticker', + 'sendMessageEmojiInteractionSeen': 'Peer.Activity.User.EnjoyingAnimations' }, chat: { 'sendMessageTypingAction': 'Peer.Activity.Chat.TypingText', @@ -1651,7 +1702,8 @@ export class AppImManager { 'sendMessageRecordAudioAction': 'Peer.Activity.Chat.RecordingAudio', 'sendMessageRecordRoundAction': 'Peer.Activity.Chat.RecordingVideo', 'sendMessageGamePlayAction': 'Peer.Activity.Chat.PlayingGame', - 'sendMessageChooseStickerAction': 'Peer.Activity.Chat.ChoosingSticker' + 'sendMessageChooseStickerAction': 'Peer.Activity.Chat.ChoosingSticker', + 'sendMessageEmojiInteractionSeen': 'Peer.Activity.Chat.EnjoyingAnimations' }, multi: { 'sendMessageTypingAction': 'Peer.Activity.Chat.Multi.TypingText1', @@ -1696,9 +1748,7 @@ export class AppImManager { container.classList.add('online', 'peer-typing-container'); } - if(action._ === 'sendMessageChooseStickerAction') { - container.classList.add('peer-typing-flex'); - } + container.classList.toggle('peer-typing-flex', action._ === 'sendMessageChooseStickerAction' || action._ === 'sendMessageEmojiInteractionSeen'); let typingElement = container.firstElementChild as HTMLElement; if(!typingElement) { @@ -1717,6 +1767,18 @@ export class AppImManager { typings.length - 1 ]; } + + if(action._ === 'sendMessageEmojiInteractionSeen') { + if(args) { + args.pop(); + } else { + args = []; + } + + const span = htmlToSpan(RichTextProcessor.wrapEmojiText(action.emoticon)); + args.push(span); + } + const descriptionElement = i18n(langPackKey, args); descriptionElement.classList.add('peer-typing-description'); diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 33f7fe58..dfa22b06 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -201,7 +201,7 @@ export class AppMessagesManager { private groupedTempId = 0; - private typings: {[peerId: PeerId]: {type: SendMessageAction['_'], timeout?: number}} = {}; + private typings: {[peerId: PeerId]: {action: SendMessageAction, timeout?: number}} = {}; private middleware: ReturnType; @@ -5806,13 +5806,14 @@ export class AppMessagesManager { }); } - public setTyping(peerId: PeerId, action: SendMessageAction): Promise { + public setTyping(peerId: PeerId, action: SendMessageAction, force?: boolean): Promise { let typing = this.typings[peerId]; if(!rootScope.myId || !peerId || !this.canSendToPeer(peerId) || peerId === rootScope.myId || - typing?.type === action._ + // (!force && deepEqual(typing?.action, action)) + (!force && typing?.action?._ === action._) ) { return Promise.resolve(false); } @@ -5822,7 +5823,7 @@ export class AppMessagesManager { } typing = this.typings[peerId] = { - type: action._ + action }; return apiManager.invokeApi('messages.setTyping', { diff --git a/src/lib/appManagers/appNotificationsManager.ts b/src/lib/appManagers/appNotificationsManager.ts index 1e4da054..fbcb4c8f 100644 --- a/src/lib/appManagers/appNotificationsManager.ts +++ b/src/lib/appManagers/appNotificationsManager.ts @@ -28,6 +28,7 @@ import appPeersManager from "./appPeersManager"; import appRuntimeManager from "./appRuntimeManager"; import appStateManager from "./appStateManager"; import appUsersManager from "./appUsersManager"; +import IS_VIBRATE_SUPPORTED from "../../environment/vibrateSupport"; type MyNotification = Notification & { hidden?: boolean, @@ -60,7 +61,7 @@ export class AppNotificationsManager { private notificationIndex = 0; private notificationsCount = 0; private soundsPlayed: {[tag: string]: number} = {}; - private vibrateSupport = !!navigator.vibrate; + private vibrateSupport = IS_VIBRATE_SUPPORTED; private nextSoundAt: number; private prevSoundVolume: number; private peerSettings = { diff --git a/src/lib/appManagers/appStickersManager.ts b/src/lib/appManagers/appStickersManager.ts index 385b1322..773dc14e 100644 --- a/src/lib/appManagers/appStickersManager.ts +++ b/src/lib/appManagers/appStickersManager.ts @@ -22,6 +22,13 @@ import assumeType from '../../helpers/assumeType'; const CACHE_TIME = 3600e3; +const EMOJI_SET_LOCAL_ID = 'emoji'; +const EMOJI_ANIMATIONS_SET_LOCAL_ID = 'emojiAnimations'; +const LOCAL_IDS_SET = new Set([ + EMOJI_SET_LOCAL_ID, + EMOJI_ANIMATIONS_SET_LOCAL_ID +]); + export type MyStickerSetInput = { id: StickerSet.stickerSet['id'], access_hash?: StickerSet.stickerSet['access_hash'] @@ -104,11 +111,13 @@ export class AppStickersManager { return this.getStickerSetPromises[id] = new Promise(async(resolve) => { if(!params.overwrite) { + // const perf = performance.now(); const cachedSet = await this.storage.get(id); if(cachedSet && cachedSet.documents?.length && ((Date.now() - cachedSet.refreshTime) < CACHE_TIME || params.useCache)) { this.saveStickers(cachedSet.documents); resolve(cachedSet); delete this.getStickerSetPromises[id]; + // console.log('get sticker set from cache time', id, performance.now() - perf); return; } } @@ -132,7 +141,12 @@ export class AppStickersManager { } public getAnimatedEmojiStickerSet() { - return this.getStickerSet({id: 'emoji'}, {saveById: true}); + return Promise.all([ + this.getStickerSet({id: EMOJI_SET_LOCAL_ID}, {saveById: true}), + this.getStickerSet({id: EMOJI_ANIMATIONS_SET_LOCAL_ID}, {saveById: true}) + ]).then(([emoji, animations]) => { + return {emoji, animations}; + }); } public async getRecentStickers(): Promise, T extends D //private cache: Partial<{[key: string]: Storage[typeof key]}> = {}; private cache: Partial = {}; - private useStorage = true; + private useStorage: boolean; + private savingFreezed: boolean; private getPromises: Map> = new Map(); private getThrottled: () => void; @@ -59,8 +60,12 @@ export default class AppStorage, T extends D if(AppStorage.STORAGES.length) { this.useStorage = AppStorage.STORAGES[0].useStorage; + } else { + this.useStorage = true; } + this.savingFreezed = false; + AppStorage.STORAGES.push(this); this.saveThrottled = throttle(async() => { @@ -140,6 +145,7 @@ export default class AppStorage, T extends D this.getThrottled = throttle(async() => { const keys = Array.from(this.getPromises.keys()); + // const perf = performance.now(); this.storage.get(keys as string[]).then(values => { for(let i = 0, length = keys.length; i < length; ++i) { const key = keys[i]; @@ -150,6 +156,8 @@ export default class AppStorage, T extends D this.getPromises.delete(key); } } + + // console.log('[AS]: get time', keys, performance.now() - perf); }, (error) => { if(!['NO_ENTRY_FOUND', 'STORAGE_OFFLINE'].includes(error)) { this.useStorage = false; @@ -214,6 +222,7 @@ export default class AppStorage, T extends D public set(obj: Partial, onlyLocal = false) { //console.log('storageSetValue', obj, callback, arguments); + const canUseStorage = this.useStorage && !onlyLocal && !this.savingFreezed; for(const key in obj) { if(obj.hasOwnProperty(key)) { const value = obj[key]; @@ -233,7 +242,7 @@ export default class AppStorage, T extends D value = stringify(value); console.log('LocalStorage set: stringify time by own stringify:', performance.now() - perf); */ - if(this.useStorage && !onlyLocal) { + if(canUseStorage) { this.keysToSet.add(key); this.keysToDelete.delete(key); this.saveThrottled(); @@ -241,7 +250,7 @@ export default class AppStorage, T extends D } } - return this.useStorage ? this.saveDeferred : Promise.resolve(); + return canUseStorage ? this.saveDeferred : Promise.resolve(); } public delete(key: keyof Storage, saveLocal = false) { @@ -291,6 +300,14 @@ export default class AppStorage, T extends D })).catch(noop); } + public static freezeSaving>(callback: () => any, names: T['stores'][number]['name'][]) { + this.STORAGES.forEach(storage => storage.savingFreezed = true); + try { + callback(); + } catch(err) {} + this.STORAGES.forEach(storage => storage.savingFreezed = false); + } + /* public deleteDatabase() { return IDBStorage.deleteDatabase().catch(noop); } */ diff --git a/src/lib/storages/dialogs.ts b/src/lib/storages/dialogs.ts index df3a9641..85fa5932 100644 --- a/src/lib/storages/dialogs.ts +++ b/src/lib/storages/dialogs.ts @@ -31,6 +31,8 @@ import { MyDialogFilter } from "./filters"; import { NULL_PEER_ID } from "../mtproto/mtproto_config"; import { NoneToVoidFunction } from "../../types"; import ctx from "../../environment/ctx"; +import AppStorage from "../storage"; +import type DATABASE_STATE from "../../config/databases/state"; export type FolderDialog = { dialog: Dialog, @@ -159,37 +161,41 @@ export default class DialogsStorage { const dialogs = appStateManager.storagesResults.dialogs; if(dialogs.length) { - for(let i = 0, length = dialogs.length; i < length; ++i) { - const dialog = dialogs[i]; - if(dialog) { - // if(dialog.peerId !== SERVICE_PEER_ID) { - dialog.top_message = this.appMessagesIdsManager.getServerMessageId(dialog.top_message); // * fix outgoing message to avoid copying dialog - // } - - if(dialog.topMessage) { - this.appMessagesManager.saveMessages([dialog.topMessage]); - } - - for(let i = 0; i <= 10; ++i) { - // @ts-ignore - delete dialog[`index_${i}`]; - } - - this.saveDialog(dialog, undefined, true); - - // ! WARNING, убрать это когда нужно будет делать чтобы pending сообщения сохранялись - const message = this.appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message); - if(message.deleted) { - this.appMessagesManager.reloadConversation(dialog.peerId); - } - } - } + AppStorage.freezeSaving(this.setDialogsFromState.bind(this, dialogs), ['chats', 'dialogs', 'messages', 'users']); } this.allDialogsLoaded = state.allDialogsLoaded || {}; }); } + private setDialogsFromState(dialogs: Dialog[]) { + for(let i = 0, length = dialogs.length; i < length; ++i) { + const dialog = dialogs[i]; + if(dialog) { + // if(dialog.peerId !== SERVICE_PEER_ID) { + dialog.top_message = this.appMessagesIdsManager.getServerMessageId(dialog.top_message); // * fix outgoing message to avoid copying dialog + // } + + if(dialog.topMessage) { + this.appMessagesManager.saveMessages([dialog.topMessage]); + } + + for(let i = 0; i <= 10; ++i) { + // @ts-ignore + delete dialog[`index_${i}`]; + } + + this.saveDialog(dialog, undefined, true); + + // ! WARNING, убрать это когда нужно будет делать чтобы pending сообщения сохранялись + const message = this.appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message); + if(message.deleted) { + this.appMessagesManager.reloadConversation(dialog.peerId); + } + } + } + } + public isDialogsLoaded(folderId: number) { return !!this.allDialogsLoaded[folderId]; } diff --git a/src/scss/partials/_emojiAnimation.scss b/src/scss/partials/_emojiAnimation.scss new file mode 100644 index 00000000..f978f002 --- /dev/null +++ b/src/scss/partials/_emojiAnimation.scss @@ -0,0 +1,29 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +.emoji-animation { + position: absolute; + pointer-events: none; + + // @include sidebar-transform(true); + + &.is-in { + .rlottie { + transform: scaleX(-1); + } + } + + &-container { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + pointer-events: none; + overflow: hidden; + z-index: 3; + } +} diff --git a/src/scss/style.scss b/src/scss/style.scss index 2ac9f84d..20f27580 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -305,6 +305,7 @@ $chat-input-inner-padding-handhelds: .25rem; @import "partials/replyKeyboard"; @import "partials/peopleNearby"; @import "partials/spoiler"; +@import "partials/emojiAnimation"; @import "partials/popups/popup"; @import "partials/popups/editAvatar"; diff --git a/src/types.d.ts b/src/types.d.ts index 316739ab..61634512 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -111,3 +111,8 @@ export namespace AuthState { _: 'authStateSignedIn' }; } + +export type SendMessageEmojiInteractionData = { + a: {t: number, i: 1}[], + v: 1 +};