Browse Source

Fixed bubbles animation

Fix saving state on auth pages
master
morethanwords 4 years ago
parent
commit
a4e6aef6a9
  1. 21
      src/components/appMediaViewer.ts
  2. 2
      src/components/appSearchSuper..ts
  3. 67
      src/components/chat/bubbles.ts
  4. 1
      src/components/chat/chat.ts
  5. 11
      src/components/misc.ts
  6. 2
      src/components/preloader.ts
  7. 2
      src/components/sidebarLeft/tabs/editProfile.ts
  8. 12
      src/components/wrappers.ts
  9. 10
      src/helpers/heavyQueue.ts
  10. 2
      src/index.hbs
  11. 8
      src/scss/partials/_chatBubble.scss
  12. 2
      src/scss/partials/_scrollable.scss
  13. 11
      src/scss/partials/pages/_password.scss

21
src/components/appMediaViewer.ts

@ -447,7 +447,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
let mediaElement: HTMLImageElement | HTMLVideoElement; let mediaElement: HTMLImageElement | HTMLVideoElement;
let src: string; let src: string;
if(target.tagName == 'DIV' || target.tagName == 'AVATAR-ELEMENT') { // useContainerAsTarget if(target.tagName === 'DIV' || target.tagName === 'AVATAR-ELEMENT') { // useContainerAsTarget
if(target.firstElementChild) { if(target.firstElementChild) {
mediaElement = new Image(); mediaElement = new Image();
src = (target.firstElementChild as HTMLImageElement).src; src = (target.firstElementChild as HTMLImageElement).src;
@ -1018,7 +1018,9 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
if(!media.supportsStreaming) { if(!media.supportsStreaming) {
onAnimationEnd.then(() => { onAnimationEnd.then(() => {
if(!media.url) {
preloader.attach(mover, true, promise); preloader.attach(mover, true, promise);
}
}); });
} }
@ -1056,11 +1058,15 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
const load = () => { const load = () => {
const cancellablePromise = appPhotosManager.preloadPhoto(media.id, size); const cancellablePromise = appPhotosManager.preloadPhoto(media.id, size);
onAnimationEnd.then(() => { onAnimationEnd.then(() => {
if(!media.url) {
this.preloader.attach(mover, true, cancellablePromise); this.preloader.attach(mover, true, cancellablePromise);
}
}); });
cancellablePromise.then(() => {
if(this.tempId != tempId) { Promise.all([onAnimationEnd, cancellablePromise]).then(() => {
if(this.tempId !== tempId) {
this.log.warn('media viewer changed photo'); this.log.warn('media viewer changed photo');
return; return;
} }
@ -1082,16 +1088,13 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} }
} else { } else {
const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover; const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
let image = div.firstElementChild as HTMLImageElement; let image = new Image();
if(!image || image.tagName != 'IMG') {
image = new Image();
}
//this.log('will renderImageFromUrl:', image, div, target); //this.log('will renderImageFromUrl:', image, div, target);
renderImageFromUrl(image, url, () => { renderImageFromUrl(image, url, () => {
if(mediaSizes.isMobile) { if(div.firstElementChild?.tagName === 'IMG') {
image.classList.remove('thumbnail'); // может здесь это вообще не нужно div.firstElementChild.remove();
} }
div.append(image); div.append(image);

2
src/components/appSearchSuper..ts

@ -751,6 +751,8 @@ export default class AppSearchSuper {
return; return;
} }
//return;
const peerId = this.searchContext.peerId; const peerId = this.searchContext.peerId;
this.log('load', single, peerId, this.loadPromises); this.log('load', single, peerId, this.loadPromises);

67
src/components/chat/bubbles.ts

@ -43,6 +43,7 @@ import { FocusDirection } from "../../helpers/fastSmoothScroll";
import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent } from "../../hooks/useHeavyAnimationCheck"; import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent } from "../../hooks/useHeavyAnimationCheck";
import { fastRaf } from "../../helpers/schedulers"; import { fastRaf } from "../../helpers/schedulers";
import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise"; import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise";
import EventListenerBase from "../../helpers/eventListenerBase";
const USE_MEDIA_TAILS = false; const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS = ['messageActionHistoryClear']; const IGNORE_ACTIONS = ['messageActionHistoryClear'];
@ -96,6 +97,7 @@ export default class ChatBubbles {
public messagesQueuePromise: Promise<void> = null; public messagesQueuePromise: Promise<void> = null;
private messagesQueue: {message: any, bubble: HTMLDivElement, reverse: boolean, promises: Promise<void>[]}[] = []; private messagesQueue: {message: any, bubble: HTMLDivElement, reverse: boolean, promises: Promise<void>[]}[] = [];
private messagesQueueOnRender: () => void = null; private messagesQueueOnRender: () => void = null;
private messagesQueueOnRenderAdditional: () => void = null;
private firstUnreadBubble: HTMLDivElement = null; private firstUnreadBubble: HTMLDivElement = null;
private attachedUnreadBubble: boolean; private attachedUnreadBubble: boolean;
@ -366,7 +368,7 @@ export default class ChatBubbles {
}); });
} }
/* if(false) */this.stickyIntersector = new StickyIntersector(this.scrollable.container, (stuck, target) => { if(false) this.stickyIntersector = new StickyIntersector(this.scrollable.container, (stuck, target) => {
for(const timestamp in this.dateMessages) { for(const timestamp in this.dateMessages) {
const dateMessage = this.dateMessages[timestamp]; const dateMessage = this.dateMessages[timestamp];
if(dateMessage.container === target) { if(dateMessage.container === target) {
@ -1467,6 +1469,10 @@ export default class ChatBubbles {
this.messagesQueueOnRender(); this.messagesQueueOnRender();
} }
if(this.messagesQueueOnRenderAdditional) {
this.messagesQueueOnRenderAdditional();
}
queue.forEach(({message, bubble, reverse}) => { queue.forEach(({message, bubble, reverse}) => {
this.setBubblePosition(bubble, message, reverse); this.setBubblePosition(bubble, message, reverse);
}); });
@ -1595,9 +1601,9 @@ export default class ChatBubbles {
contentWrapper.innerHTML = ''; contentWrapper.innerHTML = '';
contentWrapper.appendChild(bubbleContainer); contentWrapper.appendChild(bubbleContainer);
//bubbleContainer.style.marginBottom = ''; //bubbleContainer.style.marginBottom = '';
const animationDelay = contentWrapper.style.animationDelay; const transitionDelay = contentWrapper.style.transitionDelay;
contentWrapper.style.cssText = ''; contentWrapper.style.cssText = '';
contentWrapper.style.animationDelay = animationDelay; contentWrapper.style.transitionDelay = transitionDelay;
if(bubble === this.firstUnreadBubble) { if(bubble === this.firstUnreadBubble) {
bubble.classList.add('is-first-unread'); bubble.classList.add('is-first-unread');
@ -2636,9 +2642,9 @@ export default class ChatBubbles {
const waitPromise = isAdditionRender ? processPromise(resultPromise) : promise; const waitPromise = isAdditionRender ? processPromise(resultPromise) : promise;
if(isFirstMessageRender/* && false */) { if(isFirstMessageRender && rootScope.settings.animationsEnabled/* && false */) {
waitPromise.then(() => { this.messagesQueueOnRenderAdditional = () => {
if(rootScope.settings.animationsEnabled && Object.keys(this.bubbles).length) { if(Object.keys(this.bubbles).length > 1) {
let sortedMids = getObjectKeysAndSort(this.bubbles, 'desc'); let sortedMids = getObjectKeysAndSort(this.bubbles, 'desc');
if(isAdditionRender && additionMsgIds.length) { if(isAdditionRender && additionMsgIds.length) {
@ -2664,6 +2670,8 @@ export default class ChatBubbles {
topIds.map(m => this.appMessagesManager.getLocalMessageId(m)), topIds.map(m => this.appMessagesManager.getLocalMessageId(m)),
bottomIds.map(m => this.appMessagesManager.getLocalMessageId(m))); bottomIds.map(m => this.appMessagesManager.getLocalMessageId(m)));
const setBubbles: HTMLElement[] = [];
const delay = isAdditionRender ? 10 : 40; const delay = isAdditionRender ? 10 : 40;
const offsetIndex = isAdditionRender ? 0 : 1; const offsetIndex = isAdditionRender ? 0 : 1;
const animateAsLadder = (mids: number[], offsetIndex = 0) => { const animateAsLadder = (mids: number[], offsetIndex = 0) => {
@ -2678,22 +2686,42 @@ export default class ChatBubbles {
const contentWrapper = this.bubbles[mid].lastElementChild as HTMLElement; const contentWrapper = this.bubbles[mid].lastElementChild as HTMLElement;
lastMsDelay = ((idx + offsetIndex) || 0.1) * delay; lastMsDelay = ((idx + offsetIndex) || 0.1) * delay;
//lastMsDelay = (idx + offsetIndex) * delay;
//lastMsDelay = (idx || 0.1) * 1000; //lastMsDelay = (idx || 0.1) * 1000;
//if(idx || isSafari) { //if(idx || isSafari) {
// ! 0.1 = 1ms задержка для Safari, без этого первое сообщение над самым нижним может появиться позже другого с animation-delay, LOL ! // ! 0.1 = 1ms задержка для Safari, без этого первое сообщение над самым нижним может появиться позже другого с animation-delay, LOL !
contentWrapper.style.animationDelay = lastMsDelay + 'ms'; //contentWrapper.style.animationDelay = lastMsDelay + 'ms';
//} //}
contentWrapper.classList.add('zoom-fade'); contentWrapper.classList.add('zoom-fade');
contentWrapper.addEventListener('animationend', () => { contentWrapper.style.transitionDelay = lastMsDelay + 'ms';
contentWrapper.style.animationDelay = '';
contentWrapper.classList.remove('zoom-fade');
if(idx === (mids.length - 1)) { if(idx === (mids.length - 1)) {
const onTransitionEnd = (e: TransitionEvent) => {
if(e.target !== contentWrapper) {
return;
}
//contentWrapper.style.animationDelay = '';
//contentWrapper.classList.remove('zoom-fade');
//this.log('onTransitionEnd', e);
animationPromise.resolve(); animationPromise.resolve();
contentWrapper.removeEventListener('transitionend', onTransitionEnd);
};
contentWrapper.addEventListener('transitionend', onTransitionEnd);
} }
}, {once: true});
//this.log('supa', bubble); //this.log('supa', bubble);
setBubbles.push(contentWrapper);
fastRaf(() => {
contentWrapper.classList.remove('zoom-fade');
});
}); });
if(!mids.length) { if(!mids.length) {
@ -2712,6 +2740,13 @@ export default class ChatBubbles {
let promise: Promise<any>; let promise: Promise<any>;
if(topIds.length || middleIds.length || bottomIds.length) { if(topIds.length || middleIds.length || bottomIds.length) {
promise = Promise.all(promises); promise = Promise.all(promises);
promise.then(() => {
fastRaf(() => {
setBubbles.forEach(contentWrapper => {
contentWrapper.style.transitionDelay = '';
});
});
});
dispatchHeavyAnimationEvent(promise, Math.max(...delays) + 200); // * 200 - transition time dispatchHeavyAnimationEvent(promise, Math.max(...delays) + 200); // * 200 - transition time
} }
@ -2721,7 +2756,13 @@ export default class ChatBubbles {
}, 0); }, 0);
}); });
} }
});
if(!isAdditionRender) {
this.messagesQueueOnRenderAdditional = undefined;
}
};
} else {
this.messagesQueueOnRenderAdditional = undefined;
} }
(reverse ? this.getHistoryTopPromise = waitPromise : this.getHistoryBottomPromise = waitPromise); (reverse ? this.getHistoryTopPromise = waitPromise : this.getHistoryBottomPromise = waitPromise);
@ -2733,7 +2774,7 @@ export default class ChatBubbles {
return null; return null;
} }
false && !isFirstMessageRender && promise.then(() => { /* false && */!isFirstMessageRender && promise.then(() => {
if(reverse) { if(reverse) {
this.loadedTopTimes++; this.loadedTopTimes++;
this.loadedBottomTimes = Math.max(0, --this.loadedBottomTimes); this.loadedBottomTimes = Math.max(0, --this.loadedBottomTimes);

1
src/components/chat/chat.ts

@ -172,6 +172,7 @@ export default class Chat extends EventListenerBase<{
appSidebarRight.sharedMediaTab.setPeer(peerId, this.threadId); appSidebarRight.sharedMediaTab.setPeer(peerId, this.threadId);
this.input.clearHelper(); // костыль this.input.clearHelper(); // костыль
this.selection.cleanup(); // TODO: REFACTOR !!!!!!
} }
this.peerChanged = samePeer; this.peerChanged = samePeer;

11
src/components/misc.ts

@ -5,6 +5,7 @@ import mediaSizes from "../helpers/mediaSizes";
import { isTouchSupported } from "../helpers/touchSupport"; import { isTouchSupported } from "../helpers/touchSupport";
import { isApple } from "../helpers/userAgent"; import { isApple } from "../helpers/userAgent";
import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config"; import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config";
import { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck";
export const loadedURLs: {[url: string]: boolean} = {}; export const loadedURLs: {[url: string]: boolean} = {};
const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => { const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
@ -32,7 +33,15 @@ export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGIma
loadedURLs[url] = true; loadedURLs[url] = true;
//console.log('onload:', url, performance.now() - perf); //console.log('onload:', url, performance.now() - perf);
callback && callback(); if(callback) {
// TODO: переделать прогрузки аватаров до начала анимации, иначе с этим ожиданием они неприятно появляются
/* getHeavyAnimationPromise().then(() => {
callback();
}); */
callback();
}
//callback && callback();
}); });
if(callback) { if(callback) {

2
src/components/preloader.ts

@ -185,6 +185,8 @@ export default class ProgressivePreloader {
this.attachPromise(promise); this.attachPromise(promise);
} }
//return;
this.detached = false; this.detached = false;
/* window.requestAnimationFrame(() => { /* window.requestAnimationFrame(() => {
if(this.detached) return; if(this.detached) return;

2
src/components/sidebarLeft/tabs/editProfile.ts

@ -52,7 +52,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
this.content.append(this.nextBtn); this.content.append(this.nextBtn);
this.avatarElem = document.createElement('avatar-element') as AvatarElement; this.avatarElem = document.createElement('avatar-element') as AvatarElement;
this.avatarElem.classList.add('avatar-placeholder'); this.avatarElem.classList.add('avatar-placeholder', 'avatar-120');
this.avatarEdit = new AvatarEdit((_upload) => { this.avatarEdit = new AvatarEdit((_upload) => {
this.uploadAvatar = _upload; this.uploadAvatar = _upload;

12
src/components/wrappers.ts

@ -617,13 +617,13 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
//resolve(); //resolve();
if(needFadeIn) { if(needFadeIn) {
setTimeout(() => { image.addEventListener('animationend', () => {
image.classList.remove('fade-in'); image.classList.remove('fade-in');
if(thumbImage) { if(thumbImage) {
thumbImage.remove(); thumbImage.remove();
} }
}, 200); }, {once: true});
} }
}); });
}); });
@ -850,10 +850,10 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
element.classList.add('fade-out'); element.classList.add('fade-out');
} }
setTimeout(() => { animation.canvas.addEventListener('animationend', () => {
animation.canvas.classList.remove('fade-in'); animation.canvas.classList.remove('fade-in');
cb(); cb();
}, 200); }, {once: true});
} }
appDocsManager.saveLottiePreview(doc, animation.canvas, toneIndex); appDocsManager.saveLottiePreview(doc, animation.canvas, toneIndex);
@ -904,12 +904,12 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
}); });
if(needFadeIn) { if(needFadeIn) {
setTimeout(() => { image.addEventListener('animationend', () => {
image.classList.remove('fade-in'); image.classList.remove('fade-in');
if(thumbImage) { if(thumbImage) {
thumbImage.remove(); thumbImage.remove();
} }
}, 200); }, {once: true});
} }
}); });
}; };

10
src/helpers/heavyQueue.ts

@ -12,6 +12,10 @@ const heavyQueue: HeavyQueue<any>[] = [];
let processingQueue = false; let processingQueue = false;
export default function pushHeavyTask<T>(queue: HeavyQueue<T>) { export default function pushHeavyTask<T>(queue: HeavyQueue<T>) {
if(!queue.items.length) {
return Promise.resolve([]);
}
queue.promise = deferredPromise<T[]>(); queue.promise = deferredPromise<T[]>();
heavyQueue.push(queue); heavyQueue.push(queue);
processHeavyQueue(); processHeavyQueue();
@ -32,7 +36,11 @@ function processHeavyQueue() {
} }
function timedChunk<T>(queue: HeavyQueue<T>) { function timedChunk<T>(queue: HeavyQueue<T>) {
if(!queue.items.length) return Promise.resolve([]); if(!queue.items.length) {
queue.promise.resolve([]);
return Promise.resolve([]);
}
const todo = queue.items.slice(); const todo = queue.items.slice();
const results: T[] = []; const results: T[] = [];

2
src/index.hbs

@ -92,7 +92,7 @@
<path id="check" fill="none" d="M 4.7071 12.2929 l 5 5 c 0.3905 0.3905 1.0237 0.3905 1.4142 0 l 11 -11" /> <path id="check" fill="none" d="M 4.7071 12.2929 l 5 5 c 0.3905 0.3905 1.0237 0.3905 1.4142 0 l 11 -11" />
</defs> </defs>
</svg> </svg>
<div id="main-columns" class="tabs-container"> <div id="main-columns" class="tabs-container" data-animation="navigation">
<div class="chatlist-container sidebar sidebar-left main-column" id="column-left"> <div class="chatlist-container sidebar sidebar-left main-column" id="column-left">
<div class="sidebar-slider tabs-container"> <div class="sidebar-slider tabs-container">
<div class="sidebar-slider-item item-main"> <div class="sidebar-slider-item item-main">

8
src/scss/partials/_chatBubble.scss

@ -1504,8 +1504,9 @@ $bubble-margin: .25rem;
} }
.bubble-content-wrapper { .bubble-content-wrapper {
transition: transform var(--layer-transition); transition: transform var(--layer-transition), opacity var(--layer-transition);
transform: scale(1) translateX(0); transform: scale(1) translateX(0);
transform-origin: center;
opacity: 1; opacity: 1;
&.zoom-fade /* .bubble-content */ { &.zoom-fade /* .bubble-content */ {
@ -1513,9 +1514,8 @@ $bubble-margin: .25rem;
transform: scale3d(.8, .8, 1); transform: scale3d(.8, .8, 1);
//transform: scale(.8); //transform: scale(.8);
opacity: 0; opacity: 0;
transform-origin: center; //animation: zoom-opacity-fade-in .2s ease-in-out forwards;
animation: zoom-opacity-fade-in .2s ease-in-out forwards; //animation-delay: 0s;
animation-delay: 0;
} }
@include respond-to(not-handhelds) { @include respond-to(not-handhelds) {

2
src/scss/partials/_scrollable.scss

@ -48,7 +48,7 @@ html:not(.is-safari):not(.is-ios) {
overflow-y: hidden; overflow-y: hidden;
overflow-x: hidden; overflow-x: hidden;
max-height: 100%; max-height: 100%;
//transform: translateZ(0); transform: translateZ(0);
//@include respond-to(not-handhelds) { //@include respond-to(not-handhelds) {
position: absolute; position: absolute;

11
src/scss/partials/pages/_password.scss

@ -1,5 +1,5 @@
.page-password { .page-password {
#password { .input-field-input {
padding-right: 2.5rem; padding-right: 2.5rem;
max-height: 54px; max-height: 54px;
@ -23,13 +23,16 @@
position: absolute; position: absolute;
right: .25rem; right: .25rem;
z-index: 2; z-index: 2;
top: 50%;
transform: translateY(-50%);
font-size: 1.25rem; font-size: 1.25rem;
color: $placeholder-color; color: $placeholder-color;
cursor: pointer; cursor: pointer;
transition: .2s; transition: color .2s;
padding: .5rem; padding: .5rem;
display: flex;
align-items: center;
justify-content: center;
top: 50%;
transform: translateY(-50%);
&:before { &:before {
content: $tgico-eye1; content: $tgico-eye1;

Loading…
Cancel
Save