From 9c5c9741b2a6e9f2f13a85fc90b20c557ec3607d Mon Sep 17 00:00:00 2001 From: morethanwords Date: Fri, 30 Oct 2020 02:23:10 +0200 Subject: [PATCH] Render first message and load rest with animation Fix video unload in chrome --- src/components/animationIntersector.ts | 9 +- src/components/chat/contextMenu.ts | 2 + src/lib/appManagers/appImManager.ts | 118 +++++++++++++++++----- src/lib/appManagers/appMessagesManager.ts | 4 +- src/scss/partials/_chatBubble.scss | 28 +++++ 5 files changed, 132 insertions(+), 29 deletions(-) diff --git a/src/components/animationIntersector.ts b/src/components/animationIntersector.ts index 2b43bd44..f50b27e3 100644 --- a/src/components/animationIntersector.ts +++ b/src/components/animationIntersector.ts @@ -2,6 +2,7 @@ import { isInDOM } from "../lib/utils"; import { RLottiePlayer } from "../lib/lottieLoader"; import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config"; import $rootScope from "../lib/rootScope"; +import { isSafari } from "../helpers/userAgent"; export interface AnimationItem { el: HTMLElement, @@ -71,9 +72,11 @@ export class AnimationIntersector { const {el, animation} = player; animation.remove(); - if(animation instanceof HTMLVideoElement) { - animation.src = ''; - animation.load(); + if(animation instanceof HTMLVideoElement && isSafari) { + setTimeout(() => { // TODO: очистка по очереди, а не все вместе с этим таймаутом + animation.src = ''; + animation.load(); + }, 1e3); } for(const group in this.byGroups) { diff --git a/src/components/chat/contextMenu.ts b/src/components/chat/contextMenu.ts index 2151b8ca..98a2c775 100644 --- a/src/components/chat/contextMenu.ts +++ b/src/components/chat/contextMenu.ts @@ -118,6 +118,8 @@ export default class ChatContextMenu { onClick: this.onPinClick, verify: () => { const message = appMessagesManager.getMessage(this.msgID); + // for new layer + // return this.msgID > 0 && message._ != 'messageService' && appImManager.pinnedMsgID != this.msgID && (this.peerID > 0 || appChatsManager.hasRights(-this.peerID, 'pin')); return this.msgID > 0 && message._ != 'messageService' && appImManager.pinnedMsgID != this.msgID && (this.peerID == $rootScope.myID || (this.peerID < 0 && appChatsManager.hasRights(-this.peerID, 'pin'))); } }, { diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index bb7510c3..927d1a81 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -44,7 +44,7 @@ import appChatsManager, { Channel, Chat } from "./appChatsManager"; import appDialogsManager from "./appDialogsManager"; import appDocsManager from './appDocsManager'; import appInlineBotsManager from './AppInlineBotsManager'; -import appMessagesManager, { Dialog } from "./appMessagesManager"; +import appMessagesManager, { AppMessagesManager, Dialog, HistoryResult } from "./appMessagesManager"; import appPeersManager from "./appPeersManager"; import appPhotosManager from "./appPhotosManager"; import appPollsManager from './appPollsManager'; @@ -57,7 +57,8 @@ import appUsersManager from "./appUsersManager"; appSidebarLeft; // just to include -const TEST_SCROLL = false; +const TEST_SCROLL_TIMES: number = undefined; +let TEST_SCROLL = TEST_SCROLL_TIMES; const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown'; @@ -843,7 +844,7 @@ export class AppImManager { public loadMoreHistory(top: boolean, justLoad = false) { //this.log('loadMoreHistory', top); - if(!this.peerID || TEST_SCROLL || this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return; + if(!this.peerID || /* TEST_SCROLL || */ this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return; // warning, если иды только отрицательные то вниз не попадёт (хотя мб и так не попадёт) let history = Object.keys(this.bubbles).map(id => +id).filter(id => id > 0).sort((a, b) => a - b); @@ -1027,6 +1028,10 @@ export class AppImManager { this.scrolledAll = false; this.scrolledAllDown = false; + if(TEST_SCROLL !== undefined) { + TEST_SCROLL = TEST_SCROLL_TIMES; + } + this.bubbles = {}; this.dateMessages = {}; this.bubbleGroups.cleanup(); @@ -2338,7 +2343,7 @@ export class AppImManager { return bubble; } - public performHistoryResult(history: number[], reverse: boolean, isBackLimit: boolean, additionMsgID: number) { + public performHistoryResult(history: number[], reverse: boolean, isBackLimit: boolean, additionMsgID?: number) { // commented bot getProfile in getHistory! if(!history/* .filter((id: number) => id > 0) */.length) { if(!isBackLimit) { @@ -2382,13 +2387,17 @@ export class AppImManager { const method = (reverse ? history.shift : history.pop).bind(history); + //const padding = 99999; const realLength = this.scrollable.length; - let previousScrollHeightMinusTop: number; + let previousScrollHeightMinusTop: number/* , previousScrollHeight: number */; if(realLength > 0 && (reverse || isSafari)) { // for safari need set when scrolling bottom too this.messagesQueueOnRender = () => { const {scrollTop, scrollHeight} = this.scrollable; + //previousScrollHeight = scrollHeight + padding; previousScrollHeightMinusTop = reverse ? scrollHeight - scrollTop : scrollTop; + + //this.chatInner.style.paddingTop = padding + 'px'; /* if(reverse) { previousScrollHeightMinusTop = this.scrollable.scrollHeight - scrollTop; } else { @@ -2409,8 +2418,18 @@ export class AppImManager { //.then(() => new Promise(resolve => setTimeout(resolve, 100))) .then(() => { if(previousScrollHeightMinusTop !== undefined) { + /* const scrollHeight = this.scrollable.scrollHeight; + const addedHeight = scrollHeight - previousScrollHeight; + + this.chatInner.style.paddingTop = (padding - addedHeight) + 'px'; + + //const newScrollTop = reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; + const newScrollTop = reverse ? scrollHeight - addedHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; + this.log('performHistoryResult: will set scrollTop', + previousScrollHeightMinusTop, this.scrollable.scrollHeight, + newScrollTop, this.scrollable.container.clientHeight); */ + //const newScrollTop = reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; 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 isTouchSupported && isApple && (this.scrollable.container.style.overflow = 'hidden'); @@ -2444,13 +2463,22 @@ export class AppImManager { //console.time('appImManager call getHistory'); const pageCount = appPhotosManager.windowH / 38/* * 1.25 */ | 0; //const loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount; - const realLoadCount = Object.keys(this.bubbles).length > 0 ? Math.max(40, pageCount) : pageCount;//const realLoadCount = 50; + const realLoadCount = Object.keys(this.bubbles).length > 0 || additionMsgID ? Math.max(40, pageCount) : pageCount;//const realLoadCount = 50; let loadCount = realLoadCount; - if(TEST_SCROLL) { + /* if(TEST_SCROLL) { //loadCount = 1; if(Object.keys(this.bubbles).length > 0) return {cached: false, promise: Promise.resolve(true)}; + } */ + if(TEST_SCROLL !== undefined) { + if(TEST_SCROLL) { + if(Object.keys(this.bubbles).length > 0) { + --TEST_SCROLL; + } + } else { + return {cached: false, promise: Promise.resolve(true)}; + } } ////console.time('render history total'); @@ -2465,12 +2493,21 @@ export class AppImManager { } } - const result = appMessagesManager.getHistory(this.peerID, maxID, loadCount, backLimit); + /* const result = additionMsgID ? + {history: [additionMsgID]} : + appMessagesManager.getHistory(this.peerID, maxID, loadCount, backLimit); */ + let result: ReturnType | {history: number[]} = appMessagesManager.getHistory(this.peerID, maxID, loadCount, backLimit); + let resultPromise: Promise; + + const isFirstMessageRender = !!additionMsgID && result instanceof Promise && !appMessagesManager.getMessage(additionMsgID).grouped_id; + if(isFirstMessageRender) { + resultPromise = result as Promise; + result = {history: [additionMsgID]}; + //additionMsgID = 0; + } - let promise: Promise, cached: boolean; - if(result instanceof Promise) { - cached = false; - promise = result.then((result) => { + const processPromise = (result: Promise) => { + const promise = result.then((result) => { this.log('getHistory not cached result by maxID:', maxID, reverse, isBackLimit, result, peerID, justLoad); if(justLoad) { @@ -2487,24 +2524,55 @@ export class AppImManager { ////console.timeEnd('render history total'); - return this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID); + return this.performHistoryResult(result.history || [], reverse, isBackLimit, !isFirstMessageRender && additionMsgID); }, (err) => { this.log.error('getHistory error:', err); return false; }); + + return promise; + }; + + let promise: Promise, cached: boolean; + if(result instanceof Promise) { + cached = false; + promise = processPromise(result); } else if(justLoad) { return null; } else { cached = true; this.log('getHistory cached result by maxID:', maxID, reverse, isBackLimit, result, peerID, justLoad); - promise = this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID); + promise = this.performHistoryResult(result.history || [], reverse, isBackLimit, !isFirstMessageRender && additionMsgID); //return (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise); //return this.performHistoryResult(result.history || [], reverse, isBackLimit, additionMsgID, true); } - (reverse ? this.getHistoryTopPromise = promise : this.getHistoryBottomPromise = promise); + const waitPromise = isFirstMessageRender ? processPromise(resultPromise) : promise; - promise.finally(() => { + if(isFirstMessageRender) { + waitPromise.then(() => { + const mids = getObjectKeysAndSort(this.bubbles, 'desc'); + mids.findAndSplice(mid => mid == additionMsgID); + mids.forEach((mid, idx) => { + const bubble = this.bubbles[mid]; + + //if(idx || isSafari) { + // ! 0.1 = 1ms задержка для Safari, без этого первое сообщение над самым нижним может появиться позже другого с animation-delay, LOL ! + bubble.style.animationDelay = ((idx || 0.1) * 10) + 'ms'; + //} + + bubble.classList.add('zoom-fade'); + bubble.addEventListener('animationend', () => { + bubble.style.animationDelay = ''; + bubble.classList.remove('zoom-fade'); + }, {once: true}); + //this.log('supa', bubble); + }); + }); + } + + (reverse ? this.getHistoryTopPromise = waitPromise : this.getHistoryBottomPromise = waitPromise); + waitPromise.finally(() => { (reverse ? this.getHistoryTopPromise = undefined : this.getHistoryBottomPromise = undefined); }); @@ -2512,7 +2580,7 @@ export class AppImManager { return null; } - /* false && */promise.then(() => { + /* false && */!isFirstMessageRender && promise.then(() => { if(reverse) { this.loadedTopTimes++; this.loadedBottomTimes = Math.max(0, --this.loadedBottomTimes); @@ -2537,7 +2605,7 @@ export class AppImManager { this.scrolledAllDown = false; this.log('getHistory: slice bottom messages:', ids.length, loadCount); - this.getHistoryBottomPromise = undefined; // !WARNING, это нужно для обратной загрузки истории, если запрос словил флуд + //this.getHistoryBottomPromise = undefined; // !WARNING, это нужно для обратной загрузки истории, если запрос словил флуд } else { //ids = ids.slice(0, removeCount); //ids = ids.slice(0, ids.length - (removeCount * 2)); @@ -2545,7 +2613,7 @@ export class AppImManager { this.scrolledAll = false; this.log('getHistory: slice up messages:', ids.length, loadCount); - this.getHistoryTopPromise = undefined; // !WARNING, это нужно для обратной загрузки истории, если запрос словил флуд + //this.getHistoryTopPromise = undefined; // !WARNING, это нужно для обратной загрузки истории, если запрос словил флуд } this.log('getHistory: will slice ids:', ids, reverse); @@ -2556,10 +2624,12 @@ export class AppImManager { this.setUnreadDelimiter(); // не нашёл места лучше // preload more - setTimeout(() => { - this.loadMoreHistory(true, true); - this.loadMoreHistory(false, true); - }, 0); + //if(!isFirstMessageRender) { + setTimeout(() => { + this.loadMoreHistory(true, true); + this.loadMoreHistory(false, true); + }, 0); + //} }); return {cached, promise}; diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 9e5a6e67..6edf38a8 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -4186,7 +4186,7 @@ export class AppMessagesManager { limit += backLimit; } - return this.requestHistory(reqPeerID, maxID, limit, offset).then((historyResult: any) => { + return this.requestHistory(reqPeerID, maxID, limit, offset).then((historyResult) => { historyStorage.count = historyResult.count || historyResult.messages.length; if(isMigrated) { historyStorage.count++; @@ -4331,7 +4331,7 @@ export class AppMessagesManager { //timeout: APITIMEOUT, noErrorBox: true }).then((historyResult) => { - this.log('requestHistory result:', historyResult, maxID, limit, offset); + this.log('requestHistory result:', peerID, historyResult, maxID, limit, offset); if(historyResult._ == 'messages.messagesNotModified') { return historyResult; diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 29abd65d..86c27873 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -14,6 +14,24 @@ $bubble-margin: .25rem; } } +/* + * zoom-fade-opacity + */ + @keyframes zoom-opacity-fade-in { + 0% { + //transform: scale(.8) translateZ(0); + transform: scale3d(.8, .8, 1); + //transform: scale(.8); + opacity: 0; + } + 100% { + //transform: scale(1) translateZ(0); + transform: scale3d(1, 1, 1); + //transform: scale(1); + opacity: 1; + } +} + .bubbles-date-group { position: relative; @@ -156,6 +174,16 @@ $bubble-margin: .25rem; } } + &.zoom-fade /* .bubble__container */ { + //transform: scale(.8) translateZ(0); + transform: scale3d(.8, .8, 1); + //transform: scale(.8); + opacity: 0; + transform-origin: center; + animation: zoom-opacity-fade-in .2s linear forwards; + animation-delay: 0; + } + &-select-checkbox { z-index: 2; position: absolute;