Browse Source

Performance fix

master
Eduard Kuzmenko 4 years ago
parent
commit
3763ef768e
  1. 2
      src/components/appMediaViewer.ts
  2. 12
      src/components/appSearchSuper..ts
  3. 51
      src/components/chat/bubbles.ts
  4. 8
      src/components/lazyLoadQueue.ts
  5. 117
      src/components/poll.ts
  6. 21
      src/components/ripple.ts
  7. 1
      src/components/scrollable.ts
  8. 9
      src/components/wrappers.ts
  9. 2
      src/config/app.ts
  10. 5
      src/helpers/blur.ts
  11. 89
      src/helpers/dom.ts
  12. 17
      src/helpers/dom/getElementByPoint.ts
  13. 66
      src/helpers/files.ts
  14. 6
      src/helpers/schedulers.ts
  15. 7
      src/index.ts
  16. 3
      src/lib/appManagers/appImManager.ts
  17. 21
      src/lib/appManagers/appMessagesManager.ts
  18. 10
      src/lib/appManagers/appPhotosManager.ts
  19. 18
      src/lib/appManagers/appProfileManager.ts
  20. 4
      src/lib/appManagers/appStateManager.ts
  21. 4
      src/scss/partials/_chatBubble.scss
  22. 61
      src/scss/partials/_poll.scss
  23. 6
      src/scss/style.scss

2
src/components/appMediaViewer.ts

@ -959,7 +959,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
img = new Image(); img = new Image();
img.src = cacheContext.url; img.src = cacheContext.url;
} else { } else {
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(media); const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(media, true);
if(gotThumb) { if(gotThumb) {
thumbPromise = gotThumb.loadPromise; thumbPromise = gotThumb.loadPromise;
img = gotThumb.image; img = gotThumb.image;

12
src/components/appSearchSuper..ts

@ -291,9 +291,9 @@ export default class AppSearchSuper {
// Jolly Cobra's // Workaround for scrollable content flickering during animation. // Jolly Cobra's // Workaround for scrollable content flickering during animation.
const container = this.scrollable.container; const container = this.scrollable.container;
if(container.style.overflowY !== 'hidden') { if(container.style.overflowY !== 'hidden') {
const scrollBarWidth = container.offsetWidth - container.clientWidth; // const scrollBarWidth = container.offsetWidth - container.clientWidth;
container.style.overflowY = 'hidden'; container.style.overflowY = 'hidden';
container.style.paddingRight = `${scrollBarWidth}px`; // container.style.paddingRight = `${scrollBarWidth}px`;
this.container.classList.add('sliding'); this.container.classList.add('sliding');
} }
}; };
@ -313,7 +313,7 @@ export default class AppSearchSuper {
container.style.display = ''; container.style.display = '';
} }
container.style.paddingRight = '0'; // container.style.paddingRight = '0';
this.container.classList.remove('sliding'); this.container.classList.remove('sliding');
}; };
@ -478,7 +478,8 @@ export default class AppSearchSuper {
boxHeight: 0, boxHeight: 0,
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
middleware, middleware,
withoutPreloader: true withoutPreloader: true,
noBlur: true
}); });
} }
@ -586,7 +587,8 @@ export default class AppSearchSuper {
lazyLoadQueue: this.lazyLoadQueue, lazyLoadQueue: this.lazyLoadQueue,
middleware, middleware,
size: appPhotosManager.choosePhotoSize(webpage.photo, 60, 60, false), size: appPhotosManager.choosePhotoSize(webpage.photo, 60, 60, false),
loadPromises: promises loadPromises: promises,
noBlur: true
}); });
} else { } else {
previewDiv.classList.add('empty'); previewDiv.classList.add('empty');

51
src/components/chat/bubbles.ts

@ -15,7 +15,7 @@ import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
import type sessionStorage from '../../lib/sessionStorage'; import type sessionStorage from '../../lib/sessionStorage';
import type Chat from "./chat"; import type Chat from "./chat";
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager"; import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import { cancelEvent, whichChild, getElementByPoint, attachClickEvent, positionElementByIndex, reflowScrollableElement } from "../../helpers/dom"; import { cancelEvent, whichChild, attachClickEvent, positionElementByIndex, reflowScrollableElement, replaceContent } from "../../helpers/dom";
import { getObjectKeysAndSort } from "../../helpers/object"; import { getObjectKeysAndSort } from "../../helpers/object";
import { isTouchSupported } from "../../helpers/touchSupport"; import { isTouchSupported } from "../../helpers/touchSupport";
import { logger } from "../../lib/logger"; import { logger } from "../../lib/logger";
@ -58,6 +58,7 @@ import { forEachReverse } from "../../helpers/array";
import findUpClassName from "../../helpers/dom/findUpClassName"; import findUpClassName from "../../helpers/dom/findUpClassName";
import findUpTag from "../../helpers/dom/findUpTag"; import findUpTag from "../../helpers/dom/findUpTag";
import { toast } from "../toast"; import { toast } from "../toast";
import { getElementByPoint } from "../../helpers/dom/getElementByPoint";
const USE_MEDIA_TAILS = false; const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS: Message.messageService['action']['_'][] = [/* 'messageActionHistoryClear' */]; const IGNORE_ACTIONS: Message.messageService['action']['_'][] = [/* 'messageActionHistoryClear' */];
@ -71,7 +72,6 @@ export default class ChatBubbles {
bubblesContainer: HTMLDivElement; bubblesContainer: HTMLDivElement;
chatInner: HTMLDivElement; chatInner: HTMLDivElement;
scrollable: Scrollable; scrollable: Scrollable;
scroll: HTMLElement;
private getHistoryTopPromise: Promise<boolean>; private getHistoryTopPromise: Promise<boolean>;
private getHistoryBottomPromise: Promise<boolean>; private getHistoryBottomPromise: Promise<boolean>;
@ -140,10 +140,10 @@ export default class ChatBubbles {
this.chatInner = document.createElement('div'); this.chatInner = document.createElement('div');
this.chatInner.classList.add('bubbles-inner'); this.chatInner.classList.add('bubbles-inner');
this.bubblesContainer.append(this.chatInner);
this.setScroll(); this.setScroll();
this.bubblesContainer.append(this.scrollable.container);
// * constructor end // * constructor end
this.log = this.chat.log; this.log = this.chat.log;
@ -1067,10 +1067,12 @@ export default class ChatBubbles {
}; };
public setScroll() { public setScroll() {
this.scrollable = new Scrollable(this.bubblesContainer/* .firstElementChild */ as HTMLElement, 'IM', /* 10300 */300); this.scrollable = new Scrollable(null, 'IM', /* 10300 */300);
this.scrollable.loadedAll.top = false; this.scrollable.loadedAll.top = false;
this.scrollable.loadedAll.bottom = false; this.scrollable.loadedAll.bottom = false;
this.scrollable.container.append(this.chatInner);
/* const getScrollOffset = () => { /* const getScrollOffset = () => {
//return Math.round(Math.max(300, appPhotosManager.windowH / 1.5)); //return Math.round(Math.max(300, appPhotosManager.windowH / 1.5));
return 300; return 300;
@ -1081,17 +1083,14 @@ export default class ChatBubbles {
}); });
this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner, getScrollOffset()); */ this.scrollable = new Scrollable(this.bubblesContainer, 'y', 'IM', this.chatInner, getScrollOffset()); */
this.scroll = this.scrollable.container;
this.scrollable.onAdditionalScroll = this.onScroll; this.scrollable.onAdditionalScroll = this.onScroll;
this.scrollable.onScrolledTop = () => this.loadMoreHistory(true); this.scrollable.onScrolledTop = () => this.loadMoreHistory(true);
this.scrollable.onScrolledBottom = () => this.loadMoreHistory(false); this.scrollable.onScrolledBottom = () => this.loadMoreHistory(false);
//this.scrollable.attachSentinels(undefined, 300); //this.scrollable.attachSentinels(undefined, 300);
this.bubblesContainer.classList.add('scrolled-down');
if(isTouchSupported) { if(isTouchSupported) {
this.scroll.addEventListener('touchmove', () => { this.scrollable.container.addEventListener('touchmove', () => {
if(this.isScrollingTimeout) { if(this.isScrollingTimeout) {
clearTimeout(this.isScrollingTimeout); clearTimeout(this.isScrollingTimeout);
} else if(!this.chatInner.classList.contains('is-scrolling')) { } else if(!this.chatInner.classList.contains('is-scrolling')) {
@ -1099,7 +1098,7 @@ export default class ChatBubbles {
} }
}, {passive: true}); }, {passive: true});
this.scroll.addEventListener('touchend', () => { this.scrollable.container.addEventListener('touchend', () => {
if(!this.chatInner.classList.contains('is-scrolling')) { if(!this.chatInner.classList.contains('is-scrolling')) {
return; return;
} }
@ -1447,7 +1446,7 @@ export default class ChatBubbles {
this.chat.dispatchEvent('setPeer', lastMsgId, false); this.chat.dispatchEvent('setPeer', lastMsgId, false);
} else if(topMessage && !isJump) { } else if(topMessage && !isJump) {
//this.log('will scroll down', this.scroll.scrollTop, this.scroll.scrollHeight); //this.log('will scroll down', this.scroll.scrollTop, this.scroll.scrollHeight);
this.scroll.scrollTop = this.scroll.scrollHeight; this.scrollable.scrollTop = this.scrollable.scrollHeight;
this.chat.dispatchEvent('setPeer', lastMsgId, true); this.chat.dispatchEvent('setPeer', lastMsgId, true);
} }
@ -1493,9 +1492,8 @@ export default class ChatBubbles {
const oldChatInner = this.chatInner; const oldChatInner = this.chatInner;
this.cleanup(); this.cleanup();
this.chatInner = document.createElement('div'); this.chatInner = oldChatInner.cloneNode() as HTMLDivElement;
this.chatInner.className = oldChatInner.className; this.chatInner.classList.remove('disable-hover', 'is-scrolling');
this.chatInner.classList.add('disable-hover', 'is-scrolling');
this.lazyLoadQueue.lock(); this.lazyLoadQueue.lock();
@ -1516,7 +1514,7 @@ export default class ChatBubbles {
// clear // clear
if(!cached) { if(!cached) {
if(!samePeer) { if(!samePeer) {
this.scrollable.container.innerHTML = ''; this.scrollable.container.textContent = '';
//oldChatInner.remove(); //oldChatInner.remove();
this.chat.finishPeerChange(isTarget, isJump, lastMsgId); this.chat.finishPeerChange(isTarget, isJump, lastMsgId);
this.preloader.attach(this.bubblesContainer); this.preloader.attach(this.bubblesContainer);
@ -1529,9 +1527,6 @@ export default class ChatBubbles {
const setPeerPromise = promise.then(() => { const setPeerPromise = promise.then(() => {
////this.log('setPeer removing preloader'); ////this.log('setPeer removing preloader');
this.scrollable.container.innerHTML = '';
//oldChatInner.remove();
if(cached) { if(cached) {
if(!samePeer) { if(!samePeer) {
this.chat.finishPeerChange(isTarget, isJump, lastMsgId); // * костыль this.chat.finishPeerChange(isTarget, isJump, lastMsgId); // * костыль
@ -1540,11 +1535,14 @@ export default class ChatBubbles {
this.preloader.detach(); this.preloader.detach();
} }
this.scrollable.container.append(this.chatInner); replaceContent(this.scrollable.container, this.chatInner);
animationIntersector.unlockGroup(CHAT_ANIMATION_GROUP); animationIntersector.unlockGroup(CHAT_ANIMATION_GROUP);
animationIntersector.checkAnimations(false, CHAT_ANIMATION_GROUP/* , true */); animationIntersector.checkAnimations(false, CHAT_ANIMATION_GROUP/* , true */);
this.lazyLoadQueue.unlock(); fastRaf(() => {
this.lazyLoadQueue.unlock();
});
//if(dialog && lastMsgID && lastMsgID !== topMessage && (this.bubbles[lastMsgID] || this.firstUnreadBubble)) { //if(dialog && lastMsgID && lastMsgID !== topMessage && (this.bubbles[lastMsgID] || this.firstUnreadBubble)) {
if(savedPosition) { if(savedPosition) {
@ -1564,7 +1562,7 @@ export default class ChatBubbles {
const fromUp = maxBubbleId > 0 && (maxBubbleId < lastMsgId || lastMsgId < 0); const fromUp = maxBubbleId > 0 && (maxBubbleId < lastMsgId || lastMsgId < 0);
const followingUnread = readMaxId === lastMsgId && !isTarget; const followingUnread = readMaxId === lastMsgId && !isTarget;
if(!fromUp && samePeer) { if(!fromUp && samePeer) {
this.scrollable.scrollTop = this.scrollable.scrollHeight; this.scrollable.scrollTop = 99999;
} else if(fromUp/* && (samePeer || forwardingUnread) */) { } else if(fromUp/* && (samePeer || forwardingUnread) */) {
this.scrollable.scrollTop = 0; this.scrollable.scrollTop = 0;
} }
@ -1583,7 +1581,7 @@ export default class ChatBubbles {
} }
} }
} else { } else {
this.scrollable.scrollTop = this.scrollable.scrollHeight; this.scrollable.scrollTop = 99999;
} }
this.chat.dispatchEvent('setPeer', lastMsgId, !isJump); this.chat.dispatchEvent('setPeer', lastMsgId, !isJump);
@ -1613,8 +1611,11 @@ export default class ChatBubbles {
} }
} }
this.chatInner.classList.remove('disable-hover', 'is-scrolling'); // warning, performance! //this.chatInner.classList.remove('disable-hover', 'is-scrolling'); // warning, performance!
/* if(!document.body.classList.contains(RIGHT_COLUMN_ACTIVE_CLASSNAME)) {
return new Promise<void>((resolve) => fastRaf(resolve));
} */
//console.timeEnd('appImManager setPeer'); //console.timeEnd('appImManager setPeer');
}).catch(err => { }).catch(err => {
this.log.error('getHistory promise error:', err); this.log.error('getHistory promise error:', err);
@ -2729,9 +2730,9 @@ export default class ChatBubbles {
const peerId = this.peerId; const peerId = this.peerId;
//console.time('appImManager call getHistory'); //console.time('appImManager call getHistory');
const pageCount = this.appPhotosManager.windowH / 38/* * 1.25 */ | 0; const pageCount = Math.min(30, this.appPhotosManager.windowH / 38/* * 1.25 */ | 0);
//const loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount; //const loadCount = Object.keys(this.bubbles).length > 0 ? 50 : pageCount;
const realLoadCount = Object.keys(this.bubbles).length > 0 || additionMsgId ? 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;
//const realLoadCount = pageCount;//const realLoadCount = 50; //const realLoadCount = pageCount;//const realLoadCount = 50;
let loadCount = realLoadCount; let loadCount = realLoadCount;

8
src/components/lazyLoadQueue.ts

@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import { debounce } from "../helpers/schedulers"; import { throttle } from "../helpers/schedulers";
import { logger, LogLevels } from "../lib/logger"; import { logger, LogLevels } from "../lib/logger";
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector"; import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
import { findAndSpliceAll } from "../helpers/array"; import { findAndSpliceAll } from "../helpers/array";
@ -33,7 +33,7 @@ export class LazyLoadQueueBase {
protected processQueue: () => void; protected processQueue: () => void;
constructor(protected parallelLimit = PARALLEL_LIMIT) { constructor(protected parallelLimit = PARALLEL_LIMIT) {
this.processQueue = debounce(() => this._processQueue(), 20, false, true); this.processQueue = throttle(() => this._processQueue(), 20, false);
} }
public clear() { public clear() {
@ -117,6 +117,8 @@ export class LazyLoadQueueBase {
protected _processQueue(item?: LazyLoadElementBase) { protected _processQueue(item?: LazyLoadElementBase) {
if(!this.queue.length || this.lockPromise || (this.parallelLimit > 0 && this.inProcess.size >= this.parallelLimit)) return; if(!this.queue.length || this.lockPromise || (this.parallelLimit > 0 && this.inProcess.size >= this.parallelLimit)) return;
//console.log('_processQueue start');
let added = 0;
do { do {
if(item) { if(item) {
this.queue.findAndSplice(i => i === item); this.queue.findAndSplice(i => i === item);
@ -131,7 +133,9 @@ export class LazyLoadQueueBase {
} }
item = null; item = null;
++added;
} while(this.inProcess.size < this.parallelLimit && this.queue.length); } while(this.inProcess.size < this.parallelLimit && this.queue.length);
//console.log('_processQueue end, added', added, this.queue.length);
} }
public push(el: LazyLoadElementBase) { public push(el: LazyLoadElementBase) {

117
src/components/poll.ts

@ -21,7 +21,7 @@ import SetTransition from "./singleTransition";
import findUpClassName from "../helpers/dom/findUpClassName"; import findUpClassName from "../helpers/dom/findUpClassName";
let lineTotalLength = 0; let lineTotalLength = 0;
const tailLength = 9; //const tailLength = 9;
const times = 10; const times = 10;
const fullTime = 340; const fullTime = 340;
const oneTime = fullTime / times; const oneTime = fullTime / times;
@ -163,8 +163,9 @@ export default class PollElement extends HTMLElement {
private votersCountDiv: HTMLDivElement; private votersCountDiv: HTMLDivElement;
private maxOffset = -46.5; private maxOffset = -46.5;
private maxLength: number; //private maxLength: number;
private maxLengths: number[]; //private maxLengths: number[];
private maxPercents: number[];
public isClosed = false; public isClosed = false;
private isQuiz = false; private isQuiz = false;
@ -393,21 +394,17 @@ export default class PollElement extends HTMLElement {
footerDiv.append(this.sendVoteBtn); footerDiv.append(this.sendVoteBtn);
} }
const width = this.getBoundingClientRect().width; //const width = this.getBoundingClientRect().width;
this.maxLength = width + tailLength + this.maxOffset + -13.7; // 13 - position left //this.maxLength = width + tailLength + this.maxOffset + -13.7; // 13 - position left
if(poll.chosenIndexes.length || this.isClosed) { if(poll.chosenIndexes.length || this.isClosed) {
this.performResults(results, poll.chosenIndexes); this.performResults(results, poll.chosenIndexes, false);
} else if(!this.isClosed) { } else if(!this.isClosed) {
this.setVotersCount(results); this.setVotersCount(results);
attachClickEvent(this, this.clickHandler); attachClickEvent(this, this.clickHandler);
} }
} }
connectedCallback() {
this.render();
}
initQuizHint(results: PollResults) { initQuizHint(results: PollResults) {
if(results.solution && results.solution_entities) { if(results.solution && results.solution_entities) {
const toggleHint = document.createElement('div'); const toggleHint = document.createElement('div');
@ -486,7 +483,11 @@ export default class PollElement extends HTMLElement {
}); });
} }
performResults(results: PollResults, chosenIndexes: number[]) { performResults(results: PollResults, chosenIndexes: number[], animate = true) {
if(!rootScope.settings.animationsEnabled) {
animate = false;
}
if(this.isQuiz && (results.results?.length || this.isClosed)) { if(this.isQuiz && (results.results?.length || this.isClosed)) {
this.answerDivs.forEach((el, idx) => { this.answerDivs.forEach((el, idx) => {
el.classList.toggle('is-correct', !!results.results[idx].pFlags.correct); el.classList.toggle('is-correct', !!results.results[idx].pFlags.correct);
@ -533,9 +534,13 @@ export default class PollElement extends HTMLElement {
if(this.chosenIndexes.length || this.isRetracted || this.isClosed) { if(this.chosenIndexes.length || this.isRetracted || this.isClosed) {
const percents = results.results.map(v => results.total_voters ? v.voters / results.total_voters * 100 : 0); const percents = results.results.map(v => results.total_voters ? v.voters / results.total_voters * 100 : 0);
SetTransition(this, '', !this.isRetracted, 340); this.classList.toggle('no-transition', !animate);
if(animate) {
SetTransition(this, '', !this.isRetracted, 340);
}
fastRaf(() => { fastRaf(() => {
this.setResults(this.isRetracted ? this.percents : percents, this.chosenIndexes); this.setResults(this.isRetracted ? this.percents : percents, this.chosenIndexes, animate);
this.percents = percents; this.percents = percents;
this.isRetracted = false; this.isRetracted = false;
}); });
@ -576,7 +581,7 @@ export default class PollElement extends HTMLElement {
} }
} }
setResults(percents: number[], chosenIndexes: number[]) { setResults(percents: number[], chosenIndexes: number[], animate: boolean) {
this.svgLines.forEach(svg => svg.style.display = ''); this.svgLines.forEach(svg => svg.style.display = '');
this.answerDivs.forEach((el, idx) => { this.answerDivs.forEach((el, idx) => {
@ -584,7 +589,8 @@ export default class PollElement extends HTMLElement {
}); });
const maxValue = Math.max(...percents); const maxValue = Math.max(...percents);
this.maxLengths = percents.map(p => p / maxValue * this.maxLength); //this.maxLengths = percents.map(p => p / maxValue * this.maxLength);
this.maxPercents = percents.map(p => p / maxValue);
// line // line
if(this.isRetracted) { if(this.isRetracted) {
@ -592,42 +598,70 @@ export default class PollElement extends HTMLElement {
this.setLineProgress(idx, -1); this.setLineProgress(idx, -1);
}); });
} else { } else {
this.svgLines.forEach((svg, idx) => { const cb = () => {
void svg.getBoundingClientRect(); // reflow this.svgLines.forEach((svg, idx) => {
this.setLineProgress(idx, 1); //void svg.getBoundingClientRect(); // reflow
}); this.setLineProgress(idx, 1);
});
};
animate ? fastRaf(cb) : cb();
} }
percents = percents.slice(); percents = percents.slice();
roundPercents(percents); roundPercents(percents);
let getPercentValue: (percents: number, index: number) => number;
const iterate = (i: number) => {
percents.forEach((percents, idx) => {
const value = getPercentValue(percents, i);
this.numberDivs[idx].innerText = value + '%';
});
};
// numbers // numbers
if(this.isRetracted) { if(this.isRetracted) {
for(let i = (times - 1), k = 0; i >= 0; --i, ++k) { getPercentValue = (percents, index) => Math.round(percents / times * index);
setTimeout(() => {
percents.forEach((percents, idx) => { if(animate) {
const value = Math.round(percents / times * i); for(let i = (times - 1), k = 0; i >= 0; --i, ++k) {
this.numberDivs[idx].innerText = value + '%'; setTimeout(() => {
}); iterate(i);
}, oneTime * k); }, oneTime * k);
}
} else {
iterate(0);
} }
} else { } else {
for(let i = 0; i < times; ++i) { getPercentValue = (percents, index) => Math.round(percents / times * (index + 1));
setTimeout(() => {
percents.forEach((percents, idx) => { if(animate) {
const value = Math.round(percents / times * (i + 1)); for(let i = 0; i < times; ++i) {
this.numberDivs[idx].innerText = value + '%'; setTimeout(() => {
}); iterate(i);
}, oneTime * i); }, oneTime * i);
}
} else {
iterate(times - 1);
} }
} }
if(this.isRetracted) { if(this.isRetracted) {
this.classList.add('is-retracting'); if(animate) {
this.classList.add('is-retracting');
}
this.classList.remove('is-voted'); this.classList.remove('is-voted');
setTimeout(() => { const cb = () => {
this.classList.remove('is-retracting');
this.svgLines.forEach(svg => svg.style.display = 'none'); this.svgLines.forEach(svg => svg.style.display = 'none');
}, fullTime); };
if(animate) {
setTimeout(() => {
this.classList.remove('is-retracting');
cb();
}, fullTime);
} else {
cb();
}
} else { } else {
this.classList.add('is-voted'); this.classList.add('is-voted');
} }
@ -647,15 +681,16 @@ export default class PollElement extends HTMLElement {
replaceContent(this.votersCountDiv, i18n(key, args)); replaceContent(this.votersCountDiv, i18n(key, args));
} }
setLineProgress(index: number, percents: number) { setLineProgress(index: number, multiplier: number) {
const svg = this.svgLines[index]; const svg = this.svgLines[index];
if(percents === -1) { if(multiplier === -1) {
svg.style.strokeDasharray = ''; svg.style.strokeDasharray = '';
svg.style.strokeDashoffset = ''; svg.style.strokeDashoffset = '';
} else { } else {
svg.style.strokeDasharray = (percents * this.maxLengths[index]) + ', 485.9'; //svg.style.strokeDasharray = (percents * this.maxLengths[index]) + ', 485.9';
svg.style.strokeDashoffset = '' + percents * this.maxOffset; svg.style.strokeDasharray = (multiplier * this.maxPercents[index] * 100) + '%, 485.9';
svg.style.strokeDashoffset = '' + multiplier * this.maxOffset;
} }
} }

21
src/components/ripple.ts

@ -5,6 +5,7 @@
*/ */
import findUpClassName from "../helpers/dom/findUpClassName"; import findUpClassName from "../helpers/dom/findUpClassName";
import sequentialDom from "../helpers/sequentialDom";
import {isTouchSupported} from "../helpers/touchSupport"; import {isTouchSupported} from "../helpers/touchSupport";
import rootScope from "../lib/rootScope"; import rootScope from "../lib/rootScope";
@ -44,22 +45,22 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
//const duration = isSquare || mediaSizes.isMobile ? 200 : 700; //const duration = isSquare || mediaSizes.isMobile ? 200 : 700;
//return; //return;
let elapsedTime = Date.now() - startTime; let elapsedTime = Date.now() - startTime;
const cb = () => {
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
sequentialDom.mutate(() => {
elem.remove();
});
if(onEnd) onEnd(clickId);
};
if(elapsedTime < duration) { if(elapsedTime < duration) {
let delay = Math.max(duration - elapsedTime, duration / 2); let delay = Math.max(duration - elapsedTime, duration / 2);
setTimeout(() => elem.classList.add('hiding'), Math.max(delay - duration / 2, 0)); setTimeout(() => elem.classList.add('hiding'), Math.max(delay - duration / 2, 0));
setTimeout(() => { setTimeout(cb, delay);
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
elem.remove();
if(onEnd) onEnd(clickId);
}, delay);
} else { } else {
elem.classList.add('hiding'); elem.classList.add('hiding');
setTimeout(() => { setTimeout(cb, duration / 2);
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
elem.remove();
if(onEnd) onEnd(clickId);
}, duration / 2);
} }
if(!isTouchSupported) { if(!isTouchSupported) {

1
src/components/scrollable.ts

@ -108,6 +108,7 @@ export class ScrollableBase {
forceDuration?: number, forceDuration?: number,
axis?: 'x' | 'y' axis?: 'x' | 'y'
) { ) {
//return Promise.resolve();
return fastSmoothScroll(this.container, element, position, margin, maxDistance, forceDirection, forceDuration, axis); return fastSmoothScroll(this.container, element, position, margin, maxDistance, forceDirection, forceDuration, axis);
} }
} }

9
src/components/wrappers.ts

@ -579,7 +579,7 @@ function wrapMediaWithTail(photo: MyPhoto | MyDocument, message: {mid: number, m
const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", 'foreignObject'); const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", 'foreignObject');
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(photo); const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(photo, true);
if(gotThumb) { if(gotThumb) {
foreignObject.append(gotThumb.image); foreignObject.append(gotThumb.image);
} }
@ -632,7 +632,7 @@ function wrapMediaWithTail(photo: MyPhoto | MyDocument, message: {mid: number, m
return img; return img;
} }
export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size, withoutPreloader, loadPromises, noAutoDownload}: { export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size, withoutPreloader, loadPromises, noAutoDownload, noBlur}: {
photo: MyPhoto | MyDocument, photo: MyPhoto | MyDocument,
message: any, message: any,
container: HTMLElement, container: HTMLElement,
@ -646,6 +646,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
withoutPreloader?: boolean, withoutPreloader?: boolean,
loadPromises?: Promise<any>[], loadPromises?: Promise<any>[],
noAutoDownload?: boolean, noAutoDownload?: boolean,
noBlur?: boolean,
}) { }) {
if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) { if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) {
if(boxWidth && boxHeight && photo._ === 'document') { if(boxWidth && boxHeight && photo._ === 'document') {
@ -680,7 +681,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message && message.message); size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message && message.message);
} }
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(photo); const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(photo, !noBlur);
if(gotThumb) { if(gotThumb) {
loadThumbPromise = gotThumb.loadPromise; loadThumbPromise = gotThumb.loadPromise;
thumbImage = gotThumb.image; thumbImage = gotThumb.image;
@ -1307,6 +1308,6 @@ export function wrapPoll(message: any) {
elem.setAttribute('peer-id', '' + message.peerId); elem.setAttribute('peer-id', '' + message.peerId);
elem.setAttribute('poll-id', message.media.poll.id); elem.setAttribute('poll-id', message.media.poll.id);
elem.setAttribute('message-id', '' + message.mid); elem.setAttribute('message-id', '' + message.mid);
//elem.render(); elem.render();
return elem; return elem;
} }

2
src/config/app.ts

@ -12,7 +12,7 @@
const App = { const App = {
id: 1025907, id: 1025907,
hash: '452b0359b988148995f22ff0f4229750', hash: '452b0359b988148995f22ff0f4229750',
version: '0.4.0', version: '0.4.1',
langPackVersion: '0.1.3', langPackVersion: '0.1.3',
langPack: 'macos', langPack: 'macos',
langPackCode: 'en', langPackCode: 'en',

5
src/helpers/blur.ts

@ -46,8 +46,11 @@ function processBlur(dataUri: string, radius: number, iterations: number) {
}); });
} }
const blurPromises: {[dataUri: string]: Promise<string>} = {};
export default function blur(dataUri: string, radius: number = RADIUS, iterations: number = ITERATIONS) { export default function blur(dataUri: string, radius: number = RADIUS, iterations: number = ITERATIONS) {
return new Promise<string>((resolve) => { if(blurPromises[dataUri]) return blurPromises[dataUri];
return blurPromises[dataUri] = new Promise<string>((resolve) => {
//return resolve(dataUri); //return resolve(dataUri);
pushHeavyTask({ pushHeavyTask({
items: [[dataUri, radius, iterations]], items: [[dataUri, radius, iterations]],

89
src/helpers/dom.ts

@ -529,7 +529,7 @@ export const getSelectedNodes = () => {
return nodes.filter(node => !!node); return nodes.filter(node => !!node);
}; };
export const isSelectionSingle = (input: Element = document.activeElement) => { /* export const isSelectionSingle = (input: Element = document.activeElement) => {
const nodes = getSelectedNodes(); const nodes = getSelectedNodes();
const parents = [...new Set(nodes.map(node => node.parentNode))]; const parents = [...new Set(nodes.map(node => node.parentNode))];
const differentParents = parents.length > 1; const differentParents = parents.length > 1;
@ -545,7 +545,7 @@ export const isSelectionSingle = (input: Element = document.activeElement) => {
} }
return single; return single;
}; }; */
export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom', callback: () => void, listenerSetter: ListenerSetter) => { export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom', callback: () => void, listenerSetter: ListenerSetter) => {
if(isTouchSupported) { if(isTouchSupported) {
@ -587,81 +587,6 @@ export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom',
} }
}; };
export const getElementByPoint = (container: HTMLElement, verticalSide: 'top' | 'bottom', horizontalSide: 'center' | 'left'): HTMLElement => {
const rect = container.getBoundingClientRect();
const x = horizontalSide === 'center' ? Math.ceil(rect.left + ((rect.right - rect.left) / 2) + 1) : Math.ceil(rect.left + 1);
const y = verticalSide === 'bottom' ? Math.floor(rect.top + rect.height - 1) : Math.ceil(rect.top + 1);
return document.elementFromPoint(x, y) as any;
};
MOUNT_CLASS_TO.getElementByPoint = getElementByPoint;
export async function getFilesFromEvent(e: ClipboardEvent | DragEvent, onlyTypes = false): Promise<any[]> {
const files: any[] = [];
const scanFiles = async(entry: any, item: DataTransferItem) => {
if(entry.isDirectory) {
const directoryReader = entry.createReader();
await new Promise<void>((resolve, reject) => {
directoryReader.readEntries(async(entries: any) => {
for(const entry of entries) {
await scanFiles(entry, item);
}
resolve();
});
});
} else if(entry) {
if(onlyTypes) {
files.push(entry.type);
} else {
const itemFile = item.getAsFile(); // * Safari can't handle entry.file with pasting
const file = entry instanceof File ?
entry :
(
entry instanceof DataTransferItem ?
entry.getAsFile() :
await new Promise((resolve, reject) => entry.file(resolve, (err: any) => resolve(itemFile)))
);
/* if(!onlyTypes) {
console.log('getFilesFromEvent: got file', item, file);
} */
if(!file) return;
files.push(file);
}
}
};
if(e instanceof DragEvent && e.dataTransfer.files && !e.dataTransfer.items) {
for(let i = 0; i < e.dataTransfer.files.length; i++) {
const file = e.dataTransfer.files[i];
files.push(onlyTypes ? file.type : file);
}
} else {
// @ts-ignore
const items = (e.dataTransfer || e.clipboardData || e.originalEvent.clipboardData).items;
const promises: Promise<any>[] = [];
for(let i = 0; i < items.length; ++i) {
const item: DataTransferItem = items[i];
if(item.kind === 'file') {
const entry = (onlyTypes ? item : item.webkitGetAsEntry()) || item.getAsFile();
promises.push(scanFiles(entry, item));
}
}
await Promise.all(promises);
}
/* if(!onlyTypes) {
console.log('getFilesFromEvent: got files:', e, files);
} */
return files;
}
/* export function radiosHandleChange(inputs: HTMLInputElement[], onChange: (value: string) => void) { /* export function radiosHandleChange(inputs: HTMLInputElement[], onChange: (value: string) => void) {
inputs.forEach(input => { inputs.forEach(input => {
input.addEventListener('change', () => { input.addEventListener('change', () => {
@ -756,11 +681,13 @@ export function htmlToSpan(html: string) {
} }
export function replaceContent(elem: HTMLElement, node: string | Node) { export function replaceContent(elem: HTMLElement, node: string | Node) {
if(elem.children.length === 1) { // * children.length doesn't count text nodes
elem.firstChild.remove(); if(elem.children.length) {
elem.firstChild.replaceWith(node);
} else if(!elem.firstChild) {
elem.append(node);
} else { } else {
elem.textContent = ''; elem.textContent = '';
elem.append(node);
} }
elem.append(node);
} }

17
src/helpers/dom/getElementByPoint.ts

@ -0,0 +1,17 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { MOUNT_CLASS_TO } from "../../config/debug";
export function getElementByPoint(container: HTMLElement, verticalSide: 'top' | 'bottom', horizontalSide: 'center' | 'left'): HTMLElement {
//return null;
const rect = container.getBoundingClientRect();
const x = horizontalSide === 'center' ? Math.ceil(rect.left + ((rect.right - rect.left) / 2) + 1) : Math.ceil(rect.left + 1);
const y = verticalSide === 'bottom' ? Math.floor(rect.top + rect.height - 1) : Math.ceil(rect.top + 1);
return document.elementFromPoint(x, y) as any;
};
MOUNT_CLASS_TO.getElementByPoint = getElementByPoint;

66
src/helpers/files.ts

@ -54,3 +54,69 @@ export function onVideoLoad(video: HTMLVideoElement) {
video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => resolve(), {once: true}); video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => resolve(), {once: true});
}); });
} }
export async function getFilesFromEvent(e: ClipboardEvent | DragEvent, onlyTypes = false): Promise<any[]> {
const files: any[] = [];
const scanFiles = async(entry: any, item: DataTransferItem) => {
if(entry.isDirectory) {
const directoryReader = entry.createReader();
await new Promise<void>((resolve, reject) => {
directoryReader.readEntries(async(entries: any) => {
for(const entry of entries) {
await scanFiles(entry, item);
}
resolve();
});
});
} else if(entry) {
if(onlyTypes) {
files.push(entry.type);
} else {
const itemFile = item.getAsFile(); // * Safari can't handle entry.file with pasting
const file = entry instanceof File ?
entry :
(
entry instanceof DataTransferItem ?
entry.getAsFile() :
await new Promise((resolve, reject) => entry.file(resolve, (err: any) => resolve(itemFile)))
);
/* if(!onlyTypes) {
console.log('getFilesFromEvent: got file', item, file);
} */
if(!file) return;
files.push(file);
}
}
};
if(e instanceof DragEvent && e.dataTransfer.files && !e.dataTransfer.items) {
for(let i = 0; i < e.dataTransfer.files.length; i++) {
const file = e.dataTransfer.files[i];
files.push(onlyTypes ? file.type : file);
}
} else {
// @ts-ignore
const items = (e.dataTransfer || e.clipboardData || e.originalEvent.clipboardData).items;
const promises: Promise<any>[] = [];
for(let i = 0; i < items.length; ++i) {
const item: DataTransferItem = items[i];
if(item.kind === 'file') {
const entry = (onlyTypes ? item : item.webkitGetAsEntry()) || item.getAsFile();
promises.push(scanFiles(entry, item));
}
}
await Promise.all(promises);
}
/* if(!onlyTypes) {
console.log('getFilesFromEvent: got files:', e, files);
} */
return files;
}

6
src/helpers/schedulers.ts

@ -37,7 +37,7 @@ export function debounce<F extends AnyToVoidFunction>(
}; };
} }
/* export function throttle<F extends AnyToVoidFunction>( export function throttle<F extends AnyToVoidFunction>(
fn: F, fn: F,
ms: number, ms: number,
shouldRunFirst = true, shouldRunFirst = true,
@ -70,7 +70,7 @@ export function debounce<F extends AnyToVoidFunction>(
}, ms); }, ms);
} }
}; };
} */ }
/* export function throttleWithRaf<F extends AnyToVoidFunction>(fn: F) { /* export function throttleWithRaf<F extends AnyToVoidFunction>(fn: F) {
return throttleWith(fastRaf, fn); return throttleWith(fastRaf, fn);
@ -131,7 +131,7 @@ export function fastRaf(callback: NoneToVoidFunction) {
} }
export function doubleRaf() { export function doubleRaf() {
return new Promise((resolve) => { return new Promise<void>((resolve) => {
fastRaf(() => { fastRaf(() => {
fastRaf(resolve); fastRaf(resolve);
}); });

7
src/index.ts

@ -58,9 +58,16 @@ console.timeEnd('get storage1'); */
// @ts-ignore // @ts-ignore
const w = window.visualViewport || window; // * handle iOS keyboard const w = window.visualViewport || window; // * handle iOS keyboard
let setViewportVH = false; let setViewportVH = false;
let lastVH: number;
const setVH = () => { const setVH = () => {
// @ts-ignore // @ts-ignore
const vh = (setViewportVH && !rootScope.default.overlayIsActive ? w.height || w.innerHeight : window.innerHeight) * 0.01; const vh = (setViewportVH && !rootScope.default.overlayIsActive ? w.height || w.innerHeight : window.innerHeight) * 0.01;
if(lastVH === vh) {
return;
}
lastVH = vh;
//const vh = document.documentElement.scrollHeight * 0.01; //const vh = document.documentElement.scrollHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`); document.documentElement.style.setProperty('--vh', `${vh}px`);

3
src/lib/appManagers/appImManager.ts

@ -24,7 +24,7 @@ import appPhotosManager from './appPhotosManager';
import appProfileManager from './appProfileManager'; import appProfileManager from './appProfileManager';
import appStickersManager from './appStickersManager'; import appStickersManager from './appStickersManager';
import appWebPagesManager from './appWebPagesManager'; import appWebPagesManager from './appWebPagesManager';
import { blurActiveElement, cancelEvent, disableTransition, getFilesFromEvent, placeCaretAtEnd, whichChild } from '../../helpers/dom'; import { blurActiveElement, cancelEvent, disableTransition, placeCaretAtEnd, whichChild } from '../../helpers/dom';
import PopupNewMedia from '../../components/popups/newMedia'; import PopupNewMedia from '../../components/popups/newMedia';
import MarkupTooltip from '../../components/chat/markupTooltip'; import MarkupTooltip from '../../components/chat/markupTooltip';
import { isTouchSupported } from '../../helpers/touchSupport'; import { isTouchSupported } from '../../helpers/touchSupport';
@ -47,6 +47,7 @@ import { i18n } from '../langPack';
import { SendMessageAction } from '../../layer'; import { SendMessageAction } from '../../layer';
import { highlightningColor } from '../../helpers/color'; import { highlightningColor } from '../../helpers/color';
import { getObjectKeysAndSort } from '../../helpers/object'; import { getObjectKeysAndSort } from '../../helpers/object';
import { getFilesFromEvent } from '../../helpers/files';
//console.log('appImManager included33!'); //console.log('appImManager included33!');

21
src/lib/appManagers/appMessagesManager.ts

@ -67,6 +67,7 @@ export type HistoryStorage = {
readPromise?: Promise<void>, readPromise?: Promise<void>,
readMaxId?: number, readMaxId?: number,
readOutboxMaxId?: number, readOutboxMaxId?: number,
triedToReadMaxId?: number,
maxOutId?: number, maxOutId?: number,
reply_markup?: any reply_markup?: any
@ -3433,7 +3434,7 @@ export class AppMessagesManager {
if((message.totalEntities as MessageEntity[]).find(e => goodEntities.includes(e._)) || RichTextProcessor.matchUrl(message.message)) { if((message.totalEntities as MessageEntity[]).find(e => goodEntities.includes(e._)) || RichTextProcessor.matchUrl(message.message)) {
found = true; found = true;
} }
} else if(neededContents['avatar'] && message.action && ['messageActionChannelEditPhoto', 'messageActionChatEditPhoto'].includes(message.action._)) { } else if(neededContents['avatar'] && message.action && ['messageActionChannelEditPhoto', 'messageActionChatEditPhoto', 'messageActionChannelEditVideo', 'messageActionChatEditVideo'].includes(message.action._)) {
found = true; found = true;
}/* else if(neededFlags.find(flag => message.pFlags[flag])) { }/* else if(neededFlags.find(flag => message.pFlags[flag])) {
found = true; found = true;
@ -3471,9 +3472,12 @@ export class AppMessagesManager {
}); });
} }
const canCache = (['inputMessagesFilterChatPhotos', 'inputMessagesFilterPinned'] as MyInputMessagesFilter[]).includes(inputFilter._);
const method = (canCache ? apiManager.invokeApiCacheable : apiManager.invokeApi).bind(apiManager);
let apiPromise: Promise<any>; let apiPromise: Promise<any>;
if(peerId && !nextRate && folderId === undefined/* || !query */) { if(peerId && !nextRate && folderId === undefined/* || !query */) {
apiPromise = apiManager.invokeApi('messages.search', { apiPromise = method('messages.search', {
peer: appPeersManager.getInputPeerById(peerId), peer: appPeersManager.getInputPeerById(peerId),
q: query || '', q: query || '',
filter: inputFilter as any as MessagesFilter, filter: inputFilter as any as MessagesFilter,
@ -3502,7 +3506,7 @@ export class AppMessagesManager {
offsetPeerId = this.getMessagePeer(offsetMessage); offsetPeerId = this.getMessagePeer(offsetMessage);
} }
apiPromise = apiManager.invokeApi('messages.searchGlobal', { apiPromise = method('messages.searchGlobal', {
q: query, q: query,
filter: inputFilter as any as MessagesFilter, filter: inputFilter as any as MessagesFilter,
min_date: minDate, min_date: minDate,
@ -3721,7 +3725,7 @@ export class AppMessagesManager {
} }
public readHistory(peerId: number, maxId = 0, threadId?: number, force = false) { public readHistory(peerId: number, maxId = 0, threadId?: number, force = false) {
// return Promise.resolve(); //return Promise.resolve();
// console.trace('start read') // console.trace('start read')
this.log('readHistory:', peerId, maxId, threadId); this.log('readHistory:', peerId, maxId, threadId);
if(!this.getReadMaxIdIfUnread(peerId, threadId) && !force) { if(!this.getReadMaxIdIfUnread(peerId, threadId) && !force) {
@ -3729,9 +3733,12 @@ export class AppMessagesManager {
return Promise.resolve(); return Promise.resolve();
} }
const isChannel = appPeersManager.isChannel(peerId);
const historyStorage = this.getHistoryStorage(peerId, threadId); const historyStorage = this.getHistoryStorage(peerId, threadId);
if(historyStorage.triedToReadMaxId >= maxId) {
return Promise.resolve();
}
let apiPromise: Promise<any>; let apiPromise: Promise<any>;
if(threadId) { if(threadId) {
if(!historyStorage.readPromise) { if(!historyStorage.readPromise) {
@ -3751,7 +3758,7 @@ export class AppMessagesManager {
read_max_id: maxId read_max_id: maxId
} as Update.updateReadChannelDiscussionInbox } as Update.updateReadChannelDiscussionInbox
}); });
} else if(isChannel) { } else if(appPeersManager.isChannel(peerId)) {
if(!historyStorage.readPromise) { if(!historyStorage.readPromise) {
apiPromise = apiManager.invokeApi('channels.readHistory', { apiPromise = apiManager.invokeApi('channels.readHistory', {
channel: appChatsManager.getChannelInput(-peerId), channel: appChatsManager.getChannelInput(-peerId),
@ -3811,6 +3818,8 @@ export class AppMessagesManager {
return historyStorage.readPromise; return historyStorage.readPromise;
} }
historyStorage.triedToReadMaxId = maxId;
apiPromise.finally(() => { apiPromise.finally(() => {
delete historyStorage.readPromise; delete historyStorage.readPromise;

10
src/lib/appManagers/appPhotosManager.ts

@ -133,7 +133,7 @@ export class AppPhotosManager {
public getUserPhotos(userId: number, maxId: string = '0', limit: number = 20) { public getUserPhotos(userId: number, maxId: string = '0', limit: number = 20) {
const inputUser = appUsersManager.getUserInput(userId); const inputUser = appUsersManager.getUserInput(userId);
return apiManager.invokeApi('photos.getUserPhotos', { return apiManager.invokeApiCacheable('photos.getUserPhotos', {
user_id: inputUser, user_id: inputUser,
offset: 0, offset: 0,
limit, limit,
@ -204,13 +204,13 @@ export class AppPhotosManager {
return thumb.url ?? (defineNotNumerableProperties(thumb, ['url']), thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker)); return thumb.url ?? (defineNotNumerableProperties(thumb, ['url']), thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
} }
public getImageFromStrippedThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize) { public getImageFromStrippedThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, useBlur: boolean) {
const url = this.getPreviewURLFromThumb(thumb, false); const url = this.getPreviewURLFromThumb(thumb, false);
const image = new Image(); const image = new Image();
image.classList.add('thumbnail'); image.classList.add('thumbnail');
const loadPromise = blur(url).then(url => { const loadPromise = (useBlur ? blur(url) : Promise.resolve(url)).then(url => {
return new Promise<any>((resolve) => { return new Promise<any>((resolve) => {
renderImageFromUrl(image, url, resolve); renderImageFromUrl(image, url, resolve);
}); });
@ -252,7 +252,7 @@ export class AppPhotosManager {
return photoSize; return photoSize;
} }
public getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument): ReturnType<AppPhotosManager['getImageFromStrippedThumb']> { public getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument, useBlur: boolean): ReturnType<AppPhotosManager['getImageFromStrippedThumb']> {
if(!photo.downloaded || (photo as MyDocument).type === 'video' || (photo as MyDocument).type === 'gif') { if(!photo.downloaded || (photo as MyDocument).type === 'video' || (photo as MyDocument).type === 'gif') {
if(photo._ === 'document') { if(photo._ === 'document') {
const cacheContext = this.getCacheContext(photo); const cacheContext = this.getCacheContext(photo);
@ -264,7 +264,7 @@ export class AppPhotosManager {
const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs; const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
const thumb = sizes?.length ? sizes.find(size => size._ === 'photoStrippedSize') : null; const thumb = sizes?.length ? sizes.find(size => size._ === 'photoStrippedSize') : null;
if(thumb && ('bytes' in thumb)) { if(thumb && ('bytes' in thumb)) {
return appPhotosManager.getImageFromStrippedThumb(thumb as any); return appPhotosManager.getImageFromStrippedThumb(thumb as any, useBlur);
} }
} }

18
src/lib/appManagers/appProfileManager.ts

@ -11,7 +11,9 @@
import { MOUNT_CLASS_TO } from "../../config/debug"; import { MOUNT_CLASS_TO } from "../../config/debug";
import { tsNow } from "../../helpers/date"; import { tsNow } from "../../helpers/date";
import { replaceContent } from "../../helpers/dom";
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl"; import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
import sequentialDom from "../../helpers/sequentialDom";
import { ChannelParticipantsFilter, ChannelsChannelParticipants, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, UserFull, UserProfilePhoto } from "../../layer"; import { ChannelParticipantsFilter, ChannelsChannelParticipants, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, UserFull, UserProfilePhoto } from "../../layer";
//import apiManager from '../mtproto/apiManager'; //import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker'; import apiManager from '../mtproto/mtprotoworker';
@ -504,8 +506,7 @@ export class AppProfileManager {
if(!needFadeIn) { if(!needFadeIn) {
// смотри в misc.ts: renderImageFromUrl // смотри в misc.ts: renderImageFromUrl
callback = () => { callback = () => {
div.innerHTML = ''; replaceContent(div, img);
div.append(img);
div.dataset.color = ''; div.dataset.color = '';
}; };
} else { } else {
@ -515,15 +516,16 @@ export class AppProfileManager {
} }
callback = () => { callback = () => {
div.innerHTML = ''; replaceContent(div, img);
div.append(img);
setTimeout(() => { setTimeout(() => {
if(div.childElementCount) { if(div.childElementCount) {
div.dataset.color = ''; div.dataset.color = '';
if(animate) { if(animate) {
img.classList.remove('fade-in'); sequentialDom.mutateElement(img, () => {
img.classList.remove('fade-in');
});
} }
} }
}, animate ? 200 : 0); }, animate ? 200 : 0);
@ -554,7 +556,7 @@ export class AppProfileManager {
//console.log('loadDialogPhoto location:', location, inputPeer); //console.log('loadDialogPhoto location:', location, inputPeer);
if(peerId === myId && isDialog) { if(peerId === myId && isDialog) {
div.innerHTML = ''; div.innerText = '';
div.dataset.color = ''; div.dataset.color = '';
div.classList.add('tgico-saved'); div.classList.add('tgico-saved');
div.classList.remove('tgico-deletedaccount'); div.classList.remove('tgico-deletedaccount');
@ -564,7 +566,7 @@ export class AppProfileManager {
if(peerId > 0) { if(peerId > 0) {
const user = appUsersManager.getUser(peerId); const user = appUsersManager.getUser(peerId);
if(user && user.pFlags && user.pFlags.deleted) { if(user && user.pFlags && user.pFlags.deleted) {
div.innerHTML = ''; div.innerText = '';
div.dataset.color = appPeersManager.getPeerColorById(peerId); div.dataset.color = appPeersManager.getPeerColorById(peerId);
div.classList.add('tgico-deletedaccount'); div.classList.add('tgico-deletedaccount');
div.classList.remove('tgico-saved'); div.classList.remove('tgico-saved');
@ -578,7 +580,7 @@ export class AppProfileManager {
color = appPeersManager.getPeerColorById(peerId); color = appPeersManager.getPeerColorById(peerId);
} }
div.innerHTML = ''; div.innerText = '';
div.classList.remove('tgico-saved', 'tgico-deletedaccount'); div.classList.remove('tgico-saved', 'tgico-deletedaccount');
div.dataset.color = color; div.dataset.color = color;

4
src/lib/appManagers/appStateManager.ts

@ -170,9 +170,9 @@ export class AppStateManager extends EventListenerBase<{
const time = Date.now(); const time = Date.now();
if(state) { if(state) {
if(state.version !== STATE_VERSION) { /* if(state.version !== STATE_VERSION) {
state = copy(STATE_INIT); state = copy(STATE_INIT);
} else if((state.stateCreatedTime + REFRESH_EVERY) < time/* || true *//* && false */) { } else */if((state.stateCreatedTime + REFRESH_EVERY) < time/* || true *//* && false */) {
if(DEBUG) { if(DEBUG) {
this.log('will refresh state', state.stateCreatedTime, time); this.log('will refresh state', state.stateCreatedTime, time);
} }

4
src/scss/partials/_chatBubble.scss

@ -2017,11 +2017,13 @@ $bubble-margin: .25rem;
&-answer-selected { &-answer-selected {
background-color: var(--message-out-primary-color); background-color: var(--message-out-primary-color);
color: var(--message-out-background-color);
} }
html.no-touch &-answer:hover { html.no-touch &-answer:hover {
.animation-ring { .animation-ring {
background-color: rgba(79, 174, 78, .08); background-color: var(--message-out-primary-color);
opacity: .08;
} }
} }

61
src/scss/partials/_poll.scss

@ -55,10 +55,9 @@ poll-element {
color: var(--primary-color); color: var(--primary-color);
cursor: pointer; cursor: pointer;
transform: scale(1); transform: scale(1);
transition: transform .2s ease;
body.animation-level-0 & { @include animation-level(2) {
transition: none; transition: transform .2s ease;
} }
// @include respond-to(handhelds) { // @include respond-to(handhelds) {
@ -100,7 +99,6 @@ poll-element {
font-weight: 500; font-weight: 500;
margin-top: 7px; margin-top: 7px;
font-size: 14px; font-size: 14px;
transition: .34s opacity;
margin-left: -9px; margin-left: -9px;
text-align: right; text-align: right;
width: 40px; width: 40px;
@ -117,10 +115,7 @@ poll-element {
width: 16px; width: 16px;
font-weight: bold; font-weight: bold;
font-size: .75rem; font-size: .75rem;
opacity: 0; opacity: 1;
animation: fade-in-opacity .1s ease forwards;
animation-direction: reverse;
animation-delay: .24s;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -148,14 +143,16 @@ poll-element {
&:not(.is-correct):not(.is-chosen) { &:not(.is-correct):not(.is-chosen) {
.poll-answer-selected { .poll-answer-selected {
display: none; opacity: 0;
} }
} }
// Multiple answers // Multiple answers
&.is-chosing { &.is-chosing {
.poll-answer-selected { .circle-hover {
opacity: 1; .poll-answer-selected {
opacity: 1;
}
} }
& ~ .poll-footer { & ~ .poll-footer {
@ -238,11 +235,11 @@ poll-element {
&-answer { &-answer {
&.is-chosen:not(.is-correct) { &.is-chosen:not(.is-correct) {
use { use {
stroke: #DF3F40; stroke: var(--danger-color);
} }
.poll-answer-selected { .poll-answer-selected {
background: #DF3F40; background: var(--danger-color);
//line-height: 16px; //line-height: 16px;
&:before { &:before {
@ -278,7 +275,6 @@ poll-element {
left: -1px; left: -1px;
top: -1px; top: -1px;
transform: scale(1); transform: scale(1);
transition: .1s transform;
.poll-answer-selected { .poll-answer-selected {
display: flex!important; display: flex!important;
@ -300,10 +296,13 @@ poll-element {
border-radius: 50%; border-radius: 50%;
height: 34px; height: 34px;
width: 34px; width: 34px;
transition: transform .12s; background-color: var(--light-secondary-text-color);
background-color: #f4f4f4;
transform: scale(.1); transform: scale(.1);
visibility: hidden; visibility: hidden;
@include animation-level(2) {
transition: transform .12s ease;
}
} }
.progress-ring { .progress-ring {
@ -315,12 +314,11 @@ poll-element {
&__circle { &__circle {
transform-origin: center; transform-origin: center;
transform: rotate(-90deg); transform: rotate(-90deg);
transition: stroke-dashoffset .15s;
stroke-dasharray: 56.5487, 56.5487; stroke-dasharray: 56.5487, 56.5487;
stroke-dashoffset: 0; stroke-dashoffset: 0;
stroke-opacity: 1; stroke-opacity: 1;
stroke-width: 2; stroke-width: 2;
stroke: var(--border-color); stroke: var(--poll-circle-color);
fill: transparent; fill: transparent;
} }
} }
@ -333,10 +331,6 @@ poll-element {
.poll-answer-percents { .poll-answer-percents {
opacity: 1; opacity: 1;
} }
.poll-answer-selected {
animation-direction: normal;
}
} }
&.is-retracting { &.is-retracting {
@ -353,6 +347,29 @@ poll-element {
.poll-line { .poll-line {
transition: stroke-dashoffset .34s linear, stroke-dasharray .34s linear; transition: stroke-dashoffset .34s linear, stroke-dasharray .34s linear;
} }
.poll-answer-selected {
transition-delay: .24s;
transition: opacity .1s ease forwards;
}
&.is-retracting {
.poll-answer-selected {
transition-delay: 0s;
}
}
.poll-answer-percents {
transition: .34s opacity;
}
.progress-ring__circle {
transition: stroke-dashoffset .15s;
}
.circle-hover {
transition: .1s transform;
}
} }
} }

6
src/scss/style.scss

@ -42,8 +42,8 @@ $chat-padding-handhelds: .5rem;
--hover-alpha: #{$hover-alpha}; --hover-alpha: #{$hover-alpha};
--transition-standard-easing: cubic-bezier(.4, .0, .2, 1); --transition-standard-easing: cubic-bezier(.4, .0, .2, 1);
--transition-standard-in-time: .25s; --transition-standard-in-time: .3s;
--transition-standard-out-time: .2s; --transition-standard-out-time: .25s;
--transition-standard-in: var(--transition-standard-in-time) var(--transition-standard-easing); --transition-standard-in: var(--transition-standard-in-time) var(--transition-standard-easing);
--transition-standard-out: var(--transition-standard-out-time) var(--transition-standard-easing); --transition-standard-out: var(--transition-standard-out-time) var(--transition-standard-easing);
@ -149,6 +149,7 @@ $chat-padding-handhelds: .5rem;
--badge-text-color: #fff; --badge-text-color: #fff;
--link-color: #00488f; --link-color: #00488f;
--ripple-color: rgba(0, 0, 0, .08); --ripple-color: rgba(0, 0, 0, .08);
--poll-circle-color: var(--border-color);
--message-background-color: var(--surface-color); --message-background-color: var(--surface-color);
--message-checkbox-color: #61c642; --message-checkbox-color: #61c642;
@ -192,6 +193,7 @@ html.night {
--badge-text-color: #fff; --badge-text-color: #fff;
--link-color: var(--primary-color); --link-color: var(--primary-color);
--ripple-color: rgba(255, 255, 255, .08); --ripple-color: rgba(255, 255, 255, .08);
--poll-circle-color: #fff;
--message-background-color: var(--surface-color); --message-background-color: var(--surface-color);
--message-checkbox-color: var(--primary-color); --message-checkbox-color: var(--primary-color);

Loading…
Cancel
Save