From cd4b8db2d1a43a56200b91bae10477282fbe2f81 Mon Sep 17 00:00:00 2001 From: morethanwords Date: Sat, 6 Jun 2020 23:38:48 +0300 Subject: [PATCH] Wrap pinned photo Video render photo, no video Fix iOS Safari scroll Stickers sizes for devicePixelRatio --- src/components/chatInput.ts | 3 +- src/components/emoticonsDropdown.ts | 2 +- src/components/lazyLoadQueue.ts | 2 +- src/components/preloader.ts | 4 +- src/components/wrappers.ts | 51 ++++++----- src/index.hbs | 7 -- src/lib/appManagers/apiUpdatesManager.ts | 37 ++++---- src/lib/appManagers/appDialogsManager.ts | 5 ++ src/lib/appManagers/appImManager.ts | 92 ++++++++++++------- src/lib/appManagers/appMediaViewer.ts | 2 + src/lib/appManagers/appMessagesManager.ts | 10 +-- src/lib/appManagers/appWebpManager.ts | 6 +- src/lib/cacheStorage.ts | 6 +- src/lib/config.ts | 5 +- src/lib/lottieLoader.ts | 103 +++++++++++----------- src/lib/polyfill.ts | 17 ++-- src/scss/partials/_chat.scss | 2 +- src/scss/style.scss | 5 ++ 18 files changed, 205 insertions(+), 154 deletions(-) diff --git a/src/components/chatInput.ts b/src/components/chatInput.ts index ed52315f..bbec650b 100644 --- a/src/components/chatInput.ts +++ b/src/components/chatInput.ts @@ -92,7 +92,8 @@ export class ChatInput { encoderSampleRate: 48000, monitorGain: 0, numberOfChannels: 1, - recordingGain: 1 + recordingGain: 1, + reuseWorker: true }); } catch(err) { this.btnSend.classList.remove('tgico-microphone2'); diff --git a/src/components/emoticonsDropdown.ts b/src/components/emoticonsDropdown.ts index 2deae6b3..0ff36f0c 100644 --- a/src/components/emoticonsDropdown.ts +++ b/src/components/emoticonsDropdown.ts @@ -345,7 +345,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, let prevCategoryIndex = 0; let stickersScroll = new Scrollable(contentStickersDiv, 'y', 'STICKERS', undefined, undefined, 2); stickersScroll.container.addEventListener('scroll', (e) => { - lottieLoader.checkAnimations(); + lottieLoader.checkAnimations(false, EMOTICONSSTICKERGROUP); prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, stickersScroll.container, menuScroll); }); diff --git a/src/components/lazyLoadQueue.ts b/src/components/lazyLoadQueue.ts index 4bdb5871..a9ce6f26 100644 --- a/src/components/lazyLoadQueue.ts +++ b/src/components/lazyLoadQueue.ts @@ -13,7 +13,7 @@ export default class LazyLoadQueue { private unlockResolve: () => void = null; private log = console.log.bind(console, '[LL]:'); - private debug = true; + private debug = false; private observer: IntersectionObserver; diff --git a/src/components/preloader.ts b/src/components/preloader.ts index 12994891..55974900 100644 --- a/src/components/preloader.ts +++ b/src/components/preloader.ts @@ -61,7 +61,7 @@ export default class ProgressivePreloader { promise.notify = (details: {done: number, total: number}) => { if(tempID != this.tempID) return; - console.log('preloader download', promise, details); + //console.log('preloader download', promise, details); let percents = details.done / details.total * 100; this.setProgress(percents); }; @@ -116,7 +116,7 @@ export default class ProgressivePreloader { } let totalLength = this.circle.getTotalLength(); - console.log('setProgress', (percents / 100 * totalLength)); + //console.log('setProgress', (percents / 100 * totalLength)); this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', 200'; } } diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index a38f7369..fe46e25b 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -2,12 +2,10 @@ import appPhotosManager from '../lib/appManagers/appPhotosManager'; //import CryptoWorker from '../lib/crypto/cryptoworker'; import apiManager from '../lib/mtproto/mtprotoworker'; import LottieLoader from '../lib/lottieLoader'; -import appStickersManager from "../lib/appManagers/appStickersManager"; import appDocsManager from "../lib/appManagers/appDocsManager"; import { formatBytes, getEmojiToneIndex } from "../lib/utils"; import ProgressivePreloader from './preloader'; import LazyLoadQueue from './lazyLoadQueue'; -import apiFileManager from '../lib/mtproto/apiFileManager'; import VideoPlayer, { MediaProgressLine } from '../lib/mediaPlayer'; import { RichTextProcessor } from '../lib/richtextprocessor'; import { CancellablePromise } from '../lib/polyfill'; @@ -30,6 +28,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai middleware: () => boolean, lazyLoadQueue: LazyLoadQueue }) { + if(doc.type == 'video') { + return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware); + } + let img: HTMLImageElement; if(withTail) { img = wrapMediaWithTail(doc, message, container, boxWidth, boxHeight, isOut); @@ -49,7 +51,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai if(!img || img.tagName != 'IMG') { container.append(img = new Image()); } - } + } let video = document.createElement('video'); let source = document.createElement('source'); @@ -95,17 +97,19 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai return; } - console.log('loaded doc:', doc, doc.url, container); + //console.log('loaded doc:', doc, doc.url, container); renderImageFromUrl(source, doc.url); source.type = doc.mime_type; video.append(source); + video.setAttribute('playsinline', ''); if(img && img.parentElement) { img.remove(); } if(doc.type == 'gif') { + video.muted = true; video.autoplay = true; video.loop = true; video.play(); @@ -687,7 +691,7 @@ function wrapMediaWithTail(photo: any, message: {mid: number, message: string}, return img; } -export function wrapPhoto(photoID: string, message: any, container: HTMLDivElement, boxWidth = mediaSizes.active.regular.width, boxHeight = mediaSizes.active.regular.height, withTail = true, isOut = false, lazyLoadQueue: LazyLoadQueue, middleware: () => boolean, size: MTPhotoSize = null) { +export function wrapPhoto(photoID: any, message: any, container: HTMLDivElement, boxWidth = mediaSizes.active.regular.width, boxHeight = mediaSizes.active.regular.height, withTail = true, isOut = false, lazyLoadQueue: LazyLoadQueue, middleware: () => boolean, size: MTPhotoSize = null) { let photo = appPhotosManager.getPhoto(photoID); let image: HTMLImageElement; @@ -734,7 +738,7 @@ export function wrapPhoto(photoID: string, message: any, container: HTMLDivEleme return promise.then(() => { if(middleware && !middleware()) return; - renderImageFromUrl(image || container, photo.url); + renderImageFromUrl(image || container, photo._ == 'photo' ? photo.url : appPhotosManager.getDocumentCachedThumb(photo.id).url); }); }; @@ -865,8 +869,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o loop: !emoji, autoplay: true, animationData: JSON.parse(json), - width: !emoji ? 200 : 140, - height: !emoji ? 200 : 140 + width: !emoji ? 200 : undefined, + height: !emoji ? 200 : undefined }, group, toneIndex); animation.addListener('ready', () => { @@ -932,31 +936,34 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat' && stickerType != 2}), Promise.resolve()) : load(); } -export function wrapReply(title: string, subtitle: string, message?: any) { - let div = document.createElement('div'); - div.classList.add('reply'); +export function wrapReply(title: string, subtitle: string, message?: any, isPinned?: boolean) { + const prefix = isPinned ? 'pinned-message' : 'reply'; + const div = document.createElement('div'); + div.classList.add(prefix); - let replyBorder = document.createElement('div'); - replyBorder.classList.add('reply-border'); + const replyBorder = document.createElement('div'); + replyBorder.classList.add(prefix + '-border'); - let replyContent = document.createElement('div'); - replyContent.classList.add('reply-content'); + const replyContent = document.createElement('div'); + replyContent.classList.add(prefix + '-content'); - let replyTitle = document.createElement('div'); - replyTitle.classList.add('reply-title'); + const replyTitle = document.createElement('div'); + replyTitle.classList.add(prefix + '-title'); - let replySubtitle = document.createElement('div'); - replySubtitle.classList.add('reply-subtitle'); + const replySubtitle = document.createElement('div'); + replySubtitle.classList.add(prefix + '-subtitle'); replyTitle.innerHTML = title ? RichTextProcessor.wrapEmojiText(title) : ''; - let media = message && message.media; + const media = message && message.media; if(media) { replySubtitle.innerHTML = message.rReply; + + console.log('wrap reply', media); if(media.photo || (media.document && ['video'].indexOf(media.document.type) !== -1)) { let replyMedia = document.createElement('div'); - replyMedia.classList.add('reply-media'); + replyMedia.classList.add(prefix + '-media'); let photo = media.photo || media.document; @@ -971,7 +978,7 @@ export function wrapReply(title: string, subtitle: string, message?: any) { }); replyContent.append(replyMedia); - div.classList.add('is-reply-media'); + div.classList.add('is-media'); } } else { replySubtitle.innerHTML = subtitle ? RichTextProcessor.wrapEmojiText(subtitle) : ''; diff --git a/src/index.hbs b/src/index.hbs index aad17ae3..3d37be1a 100644 --- a/src/index.hbs +++ b/src/index.hbs @@ -394,13 +394,6 @@ -
-
-
-
Pinned Message
-
-
-
diff --git a/src/lib/appManagers/apiUpdatesManager.ts b/src/lib/appManagers/apiUpdatesManager.ts index 654eccc1..31e60533 100644 --- a/src/lib/appManagers/apiUpdatesManager.ts +++ b/src/lib/appManagers/apiUpdatesManager.ts @@ -5,6 +5,7 @@ import { dT, $rootScope, tsNow } from "../utils"; import appPeersManager from "./appPeersManager"; import appUsersManager from "./appUsersManager"; import appChatsManager from "./appChatsManager"; +import { logger, LogLevels } from '../polyfill'; export class ApiUpdatesManager { public updatesState: { @@ -25,6 +26,8 @@ export class ApiUpdatesManager { public channelStates: any = {}; private attached = false; + + private log = logger('UPDATES', LogLevels.error); public popPendingSeqUpdate() { var nextSeq = this.updatesState.seq + 1; @@ -67,7 +70,7 @@ export class ApiUpdatesManager { curState.pendingPtsUpdates.sort((a: any, b: any) => { return a.pts - b.pts; }); - // console.log(dT(), 'pop update', channelID, curState.pendingPtsUpdates) + // this.log(dT(), 'pop update', channelID, curState.pendingPtsUpdates) var curPts = curState.pts; var goodPts = false; @@ -86,7 +89,7 @@ export class ApiUpdatesManager { return false; } - console.log(dT(), 'pop pending pts updates', goodPts, curState.pendingPtsUpdates.slice(0, goodIndex + 1)); + this.log(dT(), 'pop pending pts updates', goodPts, curState.pendingPtsUpdates.slice(0, goodIndex + 1)); curState.pts = goodPts; for(i = 0; i <= goodIndex; i++) { @@ -170,12 +173,12 @@ export class ApiUpdatesManager { break; default: - console.warn(dT(), 'Unknown update message', updateMessage); + this.log.warn(dT(), 'Unknown update message', updateMessage); } } public getDifference() { - // console.trace(dT(), 'Get full diff') + // this.trace(dT(), 'Get full diff') const updatesState = this.updatesState; if(!updatesState.syncLoading) { updatesState.syncLoading = true; @@ -196,7 +199,7 @@ export class ApiUpdatesManager { timeout: 0x7fffffff }).then((differenceResult: any) => { if(differenceResult._ == 'updates.differenceEmpty') { - console.log(dT(), 'apply empty diff', differenceResult.seq); + this.log(dT(), 'apply empty diff', differenceResult.seq); updatesState.date = differenceResult.date; updatesState.seq = differenceResult.seq; updatesState.syncLoading = false; @@ -208,7 +211,7 @@ export class ApiUpdatesManager { appChatsManager.saveApiChats(differenceResult.chats); // Should be first because of updateMessageID - // console.log(dT(), 'applying', differenceResult.other_updates.length, 'other updates') + // this.log(dT(), 'applying', differenceResult.other_updates.length, 'other updates') differenceResult.other_updates.forEach((update: any) => { switch(update._) { @@ -222,7 +225,7 @@ export class ApiUpdatesManager { this.saveUpdate(update); }); - // console.log(dT(), 'applying', differenceResult.new_messages.length, 'new messages') + // this.log(dT(), 'applying', differenceResult.new_messages.length, 'new messages') differenceResult.new_messages.forEach((apiMessage: any) => { this.saveUpdate({ _: 'updateNewMessage', @@ -237,12 +240,12 @@ export class ApiUpdatesManager { updatesState.pts = nextState.pts; updatesState.date = nextState.date; - // console.log(dT(), 'apply diff', updatesState.seq, updatesState.pts) + // this.log(dT(), 'apply diff', updatesState.seq, updatesState.pts) if(differenceResult._ == 'updates.differenceSlice') { this.getDifference(); } else { - // console.log(dT(), 'finished get diff') + // this.log(dT(), 'finished get diff') $rootScope.$broadcast('stateSynchronized'); updatesState.syncLoading = false; } @@ -261,25 +264,25 @@ export class ApiUpdatesManager { clearTimeout(channelState.syncPending.timeout); channelState.syncPending = false; } - // console.log(dT(), 'Get channel diff', appChatsManager.getChat(channelID), channelState.pts) + // this.log(dT(), 'Get channel diff', appChatsManager.getChat(channelID), channelState.pts) apiManager.invokeApi('updates.getChannelDifference', { channel: appChatsManager.getChannelInput(channelID), filter: {_: 'channelMessagesFilterEmpty'}, pts: channelState.pts, limit: 30 }, {timeout: 0x7fffffff}).then((differenceResult: any) => { - // console.log(dT(), 'channel diff result', differenceResult) + // this.log(dT(), 'channel diff result', differenceResult) channelState.pts = differenceResult.pts; if (differenceResult._ == 'updates.channelDifferenceEmpty') { - console.log(dT(), 'apply channel empty diff', differenceResult); + this.log(dT(), 'apply channel empty diff', differenceResult); channelState.syncLoading = false; $rootScope.$broadcast('stateSynchronized'); return false; } if(differenceResult._ == 'updates.channelDifferenceTooLong') { - console.log(dT(), 'channel diff too long', differenceResult); + this.log(dT(), 'channel diff too long', differenceResult); channelState.syncLoading = false; delete this.channelStates[channelID]; this.saveUpdate({_: 'updateChannelReload', channel_id: channelID}); @@ -290,12 +293,12 @@ export class ApiUpdatesManager { appChatsManager.saveApiChats(differenceResult.chats); // Should be first because of updateMessageID - console.log(dT(), 'applying', differenceResult.other_updates.length, 'channel other updates') + this.log(dT(), 'applying', differenceResult.other_updates.length, 'channel other updates') differenceResult.other_updates.forEach((update: any) => { this.saveUpdate(update); }); - console.log(dT(), 'applying', differenceResult.new_messages.length, 'channel new messages') + this.log(dT(), 'applying', differenceResult.new_messages.length, 'channel new messages') differenceResult.new_messages.forEach((apiMessage: any) => { this.saveUpdate({ _: 'updateNewChannelMessage', @@ -305,13 +308,13 @@ export class ApiUpdatesManager { }); }); - console.log(dT(), 'apply channel diff', channelState.pts); + this.log(dT(), 'apply channel diff', channelState.pts); if(differenceResult._ == 'updates.channelDifference' && !differenceResult.pFlags['final']) { this.getChannelDifference(channelID); } else { - console.log(dT(), 'finished channel get diff'); + this.log(dT(), 'finished channel get diff'); $rootScope.$broadcast('stateSynchronized'); channelState.syncLoading = false; } diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index eeef4e5d..98a7527a 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -741,6 +741,11 @@ export class AppDialogsManager { public setUnreadMessages(dialog: Dialog) { let dom = this.getDialogDom(dialog.peerID); + if(!dom) { + this.log.error('setUnreadMessages no dom!', dialog); + return; + } + let lastMessage = appMessagesManager.getMessage(dialog.top_message); if(lastMessage._ != 'messageEmpty' && !lastMessage.deleted && lastMessage.from_id == $rootScope.myID && lastMessage.peerID != $rootScope.myID && diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index f915210a..e75ca2af 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -1,6 +1,6 @@ //import apiManager from '../mtproto/apiManager'; import apiManager from '../mtproto/mtprotoworker'; -import { $rootScope, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, findUpTag, langPack, whichChild } from "../utils"; +import { $rootScope, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, findUpTag, langPack, whichChild, cancelEvent } from "../utils"; import appUsersManager from "./appUsersManager"; import appMessagesManager, { Dialog } from "./appMessagesManager"; import appPeersManager from "./appPeersManager"; @@ -36,7 +36,7 @@ console.log('appImManager included33!'); appSidebarLeft; // just to include -const testScroll = true; +const testScroll = false; const IGNOREACTIONS = ['messageActionChannelMigrateFrom']; @@ -237,8 +237,7 @@ export class AppImManager { public updateStatusInterval = 0; public pinnedMsgID = 0; - private pinnedMessageContainer = this.columnEl.querySelector('.pinned-message') as HTMLDivElement; - private pinnedMessageContent = this.pinnedMessageContainer.querySelector('.pinned-message-subtitle') as HTMLDivElement; + private pinnedMessageContainer: HTMLDivElement = null; public lazyLoadQueue = new LazyLoadQueue(); @@ -496,8 +495,17 @@ export class AppImManager { }, {once: true}); }); - (this.columnEl.querySelector('.person') as HTMLDivElement).addEventListener('click', () => { - appSidebarRight.toggleSidebar(true); + this.topbar.addEventListener('click', (e) => { + const pinned = findUpClassName(e.target, 'pinned-message'); + if(pinned) { + e.preventDefault(); + e.cancelBubble = true; + + let mid = +pinned.dataset.mid; + this.setPeer(this.peerID, mid); + } else { + appSidebarRight.toggleSidebar(true); + } }); this.bubblesContainer.addEventListener('click', (e) => { @@ -630,14 +638,6 @@ export class AppImManager { } }); - this.pinnedMessageContainer.addEventListener('click', (e) => { - e.preventDefault(); - e.cancelBubble = true; - - let mid = +this.pinnedMessageContainer.getAttribute('data-mid'); - this.setPeer(this.peerID, mid); - }); - [this.btnMute, this.menuButtons.mute].forEach(el => { el.addEventListener('click', () => this.mutePeer(this.peerID)); }); @@ -757,10 +757,21 @@ export class AppImManager { public setPinnedMessage(message: any) { /////this.log('setting pinned message', message); - return; - this.pinnedMessageContainer.dataset.mid = '' + message.mid; + //return; + const scrollTop = this.scrollable.container.scrollTop; + const newPinned = wrapReply('Pinned Message', message.message, message, true); + newPinned.dataset.mid = '' + message.mid; + + this.topbar.insertBefore(newPinned, this.btnMute); this.topbar.classList.add('is-pinned-shown'); - this.pinnedMessageContent.innerHTML = message.rReply; + + if(this.pinnedMessageContainer) { + this.pinnedMessageContainer.remove(); + } + + this.pinnedMessageContainer = newPinned; + //this.pinnedMessageContent.innerHTML = message.rReply; + this.scrollable.scrollTop = scrollTop + 60; } public updateStatus() { @@ -784,7 +795,7 @@ export class AppImManager { } public loadMoreHistory(top: boolean) { - this.log('loadMoreHistory', top); + //this.log('loadMoreHistory', top); if(!this.peerID || testScroll || this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return; // warning, если иды только отрицательные то вниз не попадёт (хотя мб и так не попадёт) @@ -811,13 +822,13 @@ export class AppImManager { } } - public onScroll() { + public onScroll(e: Event) { if(this.onScrollRAF) window.cancelAnimationFrame(this.onScrollRAF); //if(this.scrollable.scrollLocked) return; this.onScrollRAF = window.requestAnimationFrame(() => { - lottieLoader.checkAnimations(false, 'chat'); + //lottieLoader.checkAnimations(false, 'chat'); if(!touchSupport) { if(this.isScrollingTimeout) { @@ -1076,7 +1087,11 @@ export class AppImManager { if(!cached) { this.scrollable.container.innerHTML = ''; //oldChatInner.remove(); - !samePeer && this.finishPeerChange(); + + if(!samePeer) { + this.finishPeerChange(); + } + this.preloader.attach(this.bubblesContainer); if(mediaSizes.isMobile) { @@ -1093,11 +1108,9 @@ export class AppImManager { if(cached) { this.scrollable.container.innerHTML = ''; //oldChatInner.remove(); - !samePeer && this.finishPeerChange(); - - const pinned = appMessagesManager.getPinnedMessage(peerID); - if(pinned && !pinned.deleted) { - this.setPinnedMessage(pinned); + + if(!samePeer) { + this.finishPeerChange(); } if(mediaSizes.isMobile) { @@ -1206,6 +1219,14 @@ export class AppImManager { this.btnMute.style.display = appPeersManager.isBroadcast(peerID) ? '' : 'none'; this.menuButtons.mute.style.display = this.myID == this.peerID ? 'none' : ''; + const pinned = appMessagesManager.getPinnedMessage(peerID); + if(pinned && !pinned.deleted) { + this.setPinnedMessage(pinned); + } else if(this.pinnedMessageContainer) { + this.pinnedMessageContainer.remove(); + this.pinnedMessageContainer = null; + } + window.requestAnimationFrame(() => { let title = ''; if(this.peerID == this.myID) title = 'Saved Messages'; @@ -1258,7 +1279,7 @@ export class AppImManager { //bubble.remove(); }); - lottieLoader.checkAnimations(); + lottieLoader.checkAnimations(false, 'chat'); this.deleteEmptyDateGroups(); } @@ -1366,7 +1387,7 @@ export class AppImManager { if(el instanceof HTMLVideoElement) { let source = el.firstElementChild as HTMLSourceElement; if(!source || !source.src) { - this.log.warn('no source', el, source, 'src', source.src); + //this.log.warn('no source', el, source, 'src', source.src); return; } else if(el.readyState >= 4) return; } else if(el.complete || !el.src) return; @@ -1381,8 +1402,8 @@ export class AppImManager { }; if(el instanceof HTMLVideoElement) { - el.addEventListener('loadeddata', onLoad); - r = () => el.readyState >= 4; + el.addEventListener('canplay', onLoad); + r = () => el.readyState >= 1; } else { el.addEventListener('load', onLoad); r = () => el.complete; @@ -1393,8 +1414,9 @@ export class AppImManager { window.requestAnimationFrame(c); let timeout = setTimeout(() => { - console.log('did not called', el, el.parentElement, el.complete, src); - reject(); + // @ts-ignore + this.log.error('did not called', el, el.parentElement, el.complete, el.readyState, src); + resolve(); }, 1500); }); @@ -2242,6 +2264,8 @@ export class AppImManager { let scrollTop = this.scrollable.scrollTop; previousScrollHeightMinusTop = this.scrollable.scrollHeight - scrollTop; + //this.chatInner.style.height = '100%'; + //previousScrollHeightMinusTop = 0; /* if(reverse) { previousScrollHeightMinusTop = this.scrollable.scrollHeight - scrollTop; } else { @@ -2262,7 +2286,11 @@ export class AppImManager { if(previousScrollHeightMinusTop !== undefined) { const newScrollTop = reverse ? this.scrollable.scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; this.log('performHistoryResult: will set scrollTop', this.scrollable.scrollHeight, newScrollTop, this.scrollable.container.clientHeight); + + // touchSupport for safari iOS + touchSupport && (this.scrollable.container.style.overflow = 'hidden'); this.scrollable.scrollTop = newScrollTop; + touchSupport && (this.scrollable.container.style.overflow = ''); } resolve(true); diff --git a/src/lib/appManagers/appMediaViewer.ts b/src/lib/appManagers/appMediaViewer.ts index 007ffbbb..59231969 100644 --- a/src/lib/appManagers/appMediaViewer.ts +++ b/src/lib/appManagers/appMediaViewer.ts @@ -777,7 +777,9 @@ export class AppMediaViewer { let video = mover.querySelector('video') || document.createElement('video'); let source = video.firstElementChild as HTMLSourceElement || document.createElement('source'); + video.setAttribute('playsinline', ''); if(media.type == 'gif') { + video.muted = true; video.autoplay = true; video.loop = true; } diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 6f06e32b..a08ed62e 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -23,7 +23,7 @@ import appPollsManager from "./appPollsManager"; import searchIndexManager from '../searchIndexManager'; import { MTDocument, MTPhotoSize } from "../../types"; -console.trace('include'); +//console.trace('include'); const APITIMEOUT = 0; @@ -238,10 +238,10 @@ export class AppMessagesManager { apiUpdatesManager.attach(updates ?? null); resolve(); - }).catch(resolve); + }).catch(resolve).finally(() => { + setInterval(() => this.saveState(), 10000); + }); }); - - setInterval(() => this.saveState(), 10000); } public saveState() { @@ -1986,7 +1986,7 @@ export class AppMessagesManager { str = langPack[_] + suffix; } - console.log('message action:', action); + //console.log('message action:', action); messageText = '' + str + ''; } diff --git a/src/lib/appManagers/appWebpManager.ts b/src/lib/appManagers/appWebpManager.ts index 715b06b5..8554b0bb 100644 --- a/src/lib/appManagers/appWebpManager.ts +++ b/src/lib/appManagers/appWebpManager.ts @@ -19,7 +19,7 @@ class AppWebpManager { if(!res) { (window as any).webpLoaded = () => { - console.log('webpHero loaded'); + //console.log('webpHero loaded'); this.webpMachine = new (window as any).WebpMachine(); resolve(); }; @@ -56,7 +56,7 @@ class AppWebpManager { this.busyPromise = this.convert(bytes); let res = await this.busyPromise; - console.log('converted webp', res); + //console.log('converted webp', res); callback(res as Uint8Array); @@ -85,7 +85,7 @@ class AppWebpManager { } public convertToPng(bytes: Uint8Array) { - console.warn('convertToPng!'); + //console.warn('convertToPng!'); return new Promise((resolve, reject) => { // @ts-ignore this.queue.push({bytes, callback: resolve}); diff --git a/src/lib/cacheStorage.ts b/src/lib/cacheStorage.ts index 5b13dcb6..0710d5a5 100644 --- a/src/lib/cacheStorage.ts +++ b/src/lib/cacheStorage.ts @@ -1,12 +1,12 @@ -import {blobConstruct, bytesToBase64, blobSafeMimeType, dataUrlToBlob} from './bin_utils'; +import {blobConstruct} from './bin_utils'; import FileManager from './filemanager'; -import { logger } from './polyfill'; +//import { logger } from './polyfill'; class CacheStorageController { public dbName = 'cachedFiles'; public openDbPromise: Promise; - private log: ReturnType = logger('CS'); + //private log: ReturnType = logger('CS'); constructor() { this.openDatabase(); diff --git a/src/lib/config.ts b/src/lib/config.ts index ad2cb6bf..59e2e5a8 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -92,13 +92,16 @@ export const mediaSizes = new MediaSizes(); // @ts-ignore export const touchSupport = ('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch); +export const isApple = navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i) != -1; + const Config = { Emoji, LatinizeMap, TLD, Countries, MediaSizes: mediaSizes, - touchSupport + touchSupport, + isApple }; (window as any).Config = Config; export default Config; \ No newline at end of file diff --git a/src/lib/lottieLoader.ts b/src/lib/lottieLoader.ts index 383bb998..4dd81767 100644 --- a/src/lib/lottieLoader.ts +++ b/src/lib/lottieLoader.ts @@ -1,10 +1,19 @@ import { isInDOM } from "./utils"; +import { isApple } from "./config"; let convert = (value: number) => { return Math.round(Math.min(Math.max(value, 0), 1) * 255); }; type RLottiePlayerListeners = 'enterFrame' | 'ready'; +type RLottieOptions = { + container: HTMLElement, + autoplay?: boolean, + animationData: any, + loop?: boolean, + width?: number, + height?: number +}; export class RLottiePlayer { public static reqId = 0; @@ -16,8 +25,8 @@ export class RLottiePlayer { public worker: QueryableWorker; - public width: number; - public height: number; + private width = 0; + private height = 0; public listeners: Partial<{ [k in RLottiePlayerListeners]: (res: any) => void @@ -40,21 +49,38 @@ export class RLottiePlayer { private frThen: number; private rafId: number; - private playedTimes = 0; + //private playedTimes = 0; - constructor({el, width, height, worker}: { + constructor({el, worker, options}: { el: HTMLElement, - width: number, - height: number, - worker: QueryableWorker + worker: QueryableWorker, + options: RLottieOptions }) { this.reqId = ++RLottiePlayer['reqId']; this.el = el; - this.width = width; - this.height = height; this.worker = worker; + for(let i in options) { + if(this.hasOwnProperty(i)) { + // @ts-ignore + this[i] = options[i]; + } + } + + //console.log("RLottiePlayer width:", this.width, this.height, options); + + if(window.devicePixelRatio > 1) { + if(isApple) { + this.width *= window.devicePixelRatio; + this.height *= window.devicePixelRatio; + } else { + this.width *= (window.devicePixelRatio - 1.5); + this.height *= (window.devicePixelRatio - 1.5); + } + } + this.canvas = document.createElement('canvas'); + this.canvas.classList.add('rlottie'); this.canvas.width = this.width; this.canvas.height = this.height; this.context = this.canvas.getContext('2d'); @@ -134,11 +160,11 @@ export class RLottiePlayer { public renderFrame(frame: Uint8ClampedArray, frameNo: number) { try { - this.context.putImageData(new ImageData(frame, this.width, this.height), 0, 0); + this.context.putImageData(new ImageData(frame, this.width, this.height), 0, 0/* , 0, 0, this.canvas.width, this.canvas.height */); } catch(err) { console.error('RLottiePlayer renderFrame error:', err, frame, this.width, this.height); this.autoplay = false; - this.stop(); + this.pause(); } this.setListenerResult('enterFrame', frameNo); @@ -170,7 +196,7 @@ export class RLottiePlayer { private mainLoopForwards() { this.sendQuery('renderFrame', this.curFrame++); if(this.curFrame >= this.frameCount) { - this.playedTimes++; + //this.playedTimes++; if(!this.loop) return false; @@ -183,7 +209,7 @@ export class RLottiePlayer { private mainLoopBackwards() { this.sendQuery('renderFrame', this.curFrame--); if(this.curFrame < 0) { - this.playedTimes++; + //this.playedTimes++; if(!this.loop) return false; @@ -385,14 +411,7 @@ class LottieLoader { } } - public async loadAnimationWorker(params: { - container: HTMLElement, - autoplay?: boolean, - animationData: any, - loop?: boolean, - width?: number, - height?: number - }, group = '', toneIndex = -1) { + public async loadAnimationWorker(params: RLottieOptions, group = '', toneIndex = -1) { params.autoplay = true; if(toneIndex >= 1 && toneIndex <= 5) { @@ -403,21 +422,16 @@ class LottieLoader { await this.loadLottieWorkers(); } - const width = params.width || parseInt(params.container.style.width); - const height = params.height || parseInt(params.container.style.height); + if(!params.width || !params.height) { + params.width = parseInt(params.container.style.width); + params.height = parseInt(params.container.style.height); + } - if(!width || !height) { + if(!params.width || !params.height) { throw new Error('No size for sticker!'); } - const player = this.initPlayer(params.container, params.animationData, width, height); - for(let i in params) { - // @ts-ignore - if(player.hasOwnProperty(i)) { - // @ts-ignore - player[i] = params[i]; - } - } + const player = this.initPlayer(params.container, params); (this.byGroups[group] ?? (this.byGroups[group] = [])).push(player); @@ -425,7 +439,7 @@ class LottieLoader { } public checkAnimations(blurred?: boolean, group?: string, destroy = false) { - const groups = group && false ? [group] : Object.keys(this.byGroups); + const groups = group /* && false */ ? [group] : Object.keys(this.byGroups); if(group && !this.byGroups[group]) { console.warn('no animation group:', group); @@ -438,20 +452,6 @@ class LottieLoader { animations.forEach(player => { this.checkAnimation(player, blurred, destroy); - - //if(!autoplay) continue; - - /* if(blurred || !isElementInViewport(container)) { - if(!paused) { - this.debug && console.log('pause animation', isElementInViewport(container), container); - animation.pause(); - animations[i].paused = true; - } - } else if(paused) { - this.debug && console.log('play animation', container); - animation.play(); - animations[i].paused = false; - } */ }); } } @@ -523,12 +523,11 @@ class LottieLoader { this.workers.length = 0; } - private initPlayer(el: HTMLElement, json: any, width: number, height: number) { + private initPlayer(el: HTMLElement, options: RLottieOptions) { const rlPlayer = new RLottiePlayer({ el, - width, - height, - worker: this.workers[this.curWorkerNum++] + worker: this.workers[this.curWorkerNum++], + options }); this.players[rlPlayer.reqId] = rlPlayer; @@ -536,7 +535,7 @@ class LottieLoader { this.curWorkerNum = 0; } - rlPlayer.loadFromData(json); + rlPlayer.loadFromData(options.animationData); return rlPlayer; } diff --git a/src/lib/polyfill.ts b/src/lib/polyfill.ts index 42b48fd2..1bb796f4 100644 --- a/src/lib/polyfill.ts +++ b/src/lib/polyfill.ts @@ -4,25 +4,30 @@ import {SecureRandom} from 'jsbn'; export const secureRandom = new SecureRandom(); -export function logger(prefix: string) { +export enum LogLevels { + log = 1, + warn = 2, + error = 4 +}; +export function logger(prefix: string, level = LogLevels.log | LogLevels.warn | LogLevels.error) { function Log(...args: any[]) { - return console.log(dT(), '[' + prefix + ']:', ...args); + return level & LogLevels.log && console.log(dT(), '[' + prefix + ']:', ...args); } Log.warn = function(...args: any[]) { - return console.warn(dT(), '[' + prefix + ']:', ...args); + return level & LogLevels.warn && console.warn(dT(), '[' + prefix + ']:', ...args); }; Log.info = function(...args: any[]) { - return console.info(dT(), '[' + prefix + ']:', ...args); + return level & LogLevels.log && console.info(dT(), '[' + prefix + ']:', ...args); }; Log.error = function(...args: any[]) { - return console.error(dT(), '[' + prefix + ']:', ...args); + return level & LogLevels.error && console.error(dT(), '[' + prefix + ']:', ...args); }; Log.trace = function(...args: any[]) { - return console.trace(dT(), '[' + prefix + ']:', ...args); + return level & LogLevels.log && console.trace(dT(), '[' + prefix + ']:', ...args); } return Log; diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index ba1feaad..c55e55a4 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -437,7 +437,7 @@ $time-background: rgba(0, 0, 0, .35); position: relative; /* padding: .25rem; */ - &.is-reply-media { + &.is-media { .pinned-message-content, .reply-content { padding-left: 40px; } diff --git a/src/scss/style.scss b/src/scss/style.scss index 62e11ec2..fd401a39 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -1354,6 +1354,11 @@ img.emoji { pointer-events: none; } +.rlottie { + max-width: 100%; + max-height: 100%; +} + /* #chats-container { display: -webkit-box; display: -webkit-flex;