Browse Source

Fixed bubbles animation

Fix saving state on auth pages
master
morethanwords 4 years ago
parent
commit
a4e6aef6a9
  1. 25
      src/components/appMediaViewer.ts
  2. 2
      src/components/appSearchSuper..ts
  3. 75
      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

25
src/components/appMediaViewer.ts

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

2
src/components/appSearchSuper..ts

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

75
src/components/chat/bubbles.ts

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

1
src/components/chat/chat.ts

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

11
src/components/misc.ts

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

2
src/components/preloader.ts

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

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

@ -52,7 +52,7 @@ export default class AppEditProfileTab extends SliderSuperTab { @@ -52,7 +52,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
this.content.append(this.nextBtn);
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.uploadAvatar = _upload;

12
src/components/wrappers.ts

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

10
src/helpers/heavyQueue.ts

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

2
src/index.hbs

@ -92,7 +92,7 @@ @@ -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" />
</defs>
</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="sidebar-slider tabs-container">
<div class="sidebar-slider-item item-main">

8
src/scss/partials/_chatBubble.scss

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

2
src/scss/partials/_scrollable.scss

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

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

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

Loading…
Cancel
Save