Browse Source

Performance fix

master
Eduard Kuzmenko 3 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. 2
      src/components/chat/replies.ts
  5. 8
      src/components/lazyLoadQueue.ts
  6. 117
      src/components/poll.ts
  7. 21
      src/components/ripple.ts
  8. 1
      src/components/scrollable.ts
  9. 2
      src/components/visibilityIntersector.ts
  10. 9
      src/components/wrappers.ts
  11. 2
      src/config/app.ts
  12. 5
      src/helpers/blur.ts
  13. 89
      src/helpers/dom.ts
  14. 17
      src/helpers/dom/getElementByPoint.ts
  15. 68
      src/helpers/files.ts
  16. 6
      src/helpers/schedulers.ts
  17. 7
      src/index.ts
  18. 3
      src/lib/appManagers/appImManager.ts
  19. 21
      src/lib/appManagers/appMessagesManager.ts
  20. 10
      src/lib/appManagers/appPhotosManager.ts
  21. 18
      src/lib/appManagers/appProfileManager.ts
  22. 4
      src/lib/appManagers/appStateManager.ts
  23. 4
      src/scss/partials/_chatBubble.scss
  24. 63
      src/scss/partials/_poll.scss
  25. 6
      src/scss/style.scss

2
src/components/appMediaViewer.ts

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

12
src/components/appSearchSuper..ts

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

51
src/components/chat/bubbles.ts

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

2
src/components/chat/replies.ts

@ -148,4 +148,4 @@ export default class RepliesElement extends HTMLElement { @@ -148,4 +148,4 @@ export default class RepliesElement extends HTMLElement {
}
}
customElements.define(TAG_NAME, RepliesElement);
customElements.define(TAG_NAME, RepliesElement);

8
src/components/lazyLoadQueue.ts

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

117
src/components/poll.ts

@ -21,7 +21,7 @@ import SetTransition from "./singleTransition"; @@ -21,7 +21,7 @@ import SetTransition from "./singleTransition";
import findUpClassName from "../helpers/dom/findUpClassName";
let lineTotalLength = 0;
const tailLength = 9;
//const tailLength = 9;
const times = 10;
const fullTime = 340;
const oneTime = fullTime / times;
@ -163,8 +163,9 @@ export default class PollElement extends HTMLElement { @@ -163,8 +163,9 @@ export default class PollElement extends HTMLElement {
private votersCountDiv: HTMLDivElement;
private maxOffset = -46.5;
private maxLength: number;
private maxLengths: number[];
//private maxLength: number;
//private maxLengths: number[];
private maxPercents: number[];
public isClosed = false;
private isQuiz = false;
@ -393,21 +394,17 @@ export default class PollElement extends HTMLElement { @@ -393,21 +394,17 @@ export default class PollElement extends HTMLElement {
footerDiv.append(this.sendVoteBtn);
}
const width = this.getBoundingClientRect().width;
this.maxLength = width + tailLength + this.maxOffset + -13.7; // 13 - position left
//const width = this.getBoundingClientRect().width;
//this.maxLength = width + tailLength + this.maxOffset + -13.7; // 13 - position left
if(poll.chosenIndexes.length || this.isClosed) {
this.performResults(results, poll.chosenIndexes);
this.performResults(results, poll.chosenIndexes, false);
} else if(!this.isClosed) {
this.setVotersCount(results);
attachClickEvent(this, this.clickHandler);
}
}
connectedCallback() {
this.render();
}
initQuizHint(results: PollResults) {
if(results.solution && results.solution_entities) {
const toggleHint = document.createElement('div');
@ -486,7 +483,11 @@ export default class PollElement extends HTMLElement { @@ -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)) {
this.answerDivs.forEach((el, idx) => {
el.classList.toggle('is-correct', !!results.results[idx].pFlags.correct);
@ -533,9 +534,13 @@ export default class PollElement extends HTMLElement { @@ -533,9 +534,13 @@ export default class PollElement extends HTMLElement {
if(this.chosenIndexes.length || this.isRetracted || this.isClosed) {
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(() => {
this.setResults(this.isRetracted ? this.percents : percents, this.chosenIndexes);
this.setResults(this.isRetracted ? this.percents : percents, this.chosenIndexes, animate);
this.percents = percents;
this.isRetracted = false;
});
@ -576,7 +581,7 @@ export default class PollElement extends HTMLElement { @@ -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.answerDivs.forEach((el, idx) => {
@ -584,7 +589,8 @@ export default class PollElement extends HTMLElement { @@ -584,7 +589,8 @@ export default class PollElement extends HTMLElement {
});
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
if(this.isRetracted) {
@ -592,42 +598,70 @@ export default class PollElement extends HTMLElement { @@ -592,42 +598,70 @@ export default class PollElement extends HTMLElement {
this.setLineProgress(idx, -1);
});
} else {
this.svgLines.forEach((svg, idx) => {
void svg.getBoundingClientRect(); // reflow
this.setLineProgress(idx, 1);
});
const cb = () => {
this.svgLines.forEach((svg, idx) => {
//void svg.getBoundingClientRect(); // reflow
this.setLineProgress(idx, 1);
});
};
animate ? fastRaf(cb) : cb();
}
percents = percents.slice();
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
if(this.isRetracted) {
for(let i = (times - 1), k = 0; i >= 0; --i, ++k) {
setTimeout(() => {
percents.forEach((percents, idx) => {
const value = Math.round(percents / times * i);
this.numberDivs[idx].innerText = value + '%';
});
}, oneTime * k);
getPercentValue = (percents, index) => Math.round(percents / times * index);
if(animate) {
for(let i = (times - 1), k = 0; i >= 0; --i, ++k) {
setTimeout(() => {
iterate(i);
}, oneTime * k);
}
} else {
iterate(0);
}
} else {
for(let i = 0; i < times; ++i) {
setTimeout(() => {
percents.forEach((percents, idx) => {
const value = Math.round(percents / times * (i + 1));
this.numberDivs[idx].innerText = value + '%';
});
}, oneTime * i);
getPercentValue = (percents, index) => Math.round(percents / times * (index + 1));
if(animate) {
for(let i = 0; i < times; ++i) {
setTimeout(() => {
iterate(i);
}, oneTime * i);
}
} else {
iterate(times - 1);
}
}
if(this.isRetracted) {
this.classList.add('is-retracting');
if(animate) {
this.classList.add('is-retracting');
}
this.classList.remove('is-voted');
setTimeout(() => {
this.classList.remove('is-retracting');
const cb = () => {
this.svgLines.forEach(svg => svg.style.display = 'none');
}, fullTime);
};
if(animate) {
setTimeout(() => {
this.classList.remove('is-retracting');
cb();
}, fullTime);
} else {
cb();
}
} else {
this.classList.add('is-voted');
}
@ -647,15 +681,16 @@ export default class PollElement extends HTMLElement { @@ -647,15 +681,16 @@ export default class PollElement extends HTMLElement {
replaceContent(this.votersCountDiv, i18n(key, args));
}
setLineProgress(index: number, percents: number) {
setLineProgress(index: number, multiplier: number) {
const svg = this.svgLines[index];
if(percents === -1) {
if(multiplier === -1) {
svg.style.strokeDasharray = '';
svg.style.strokeDashoffset = '';
} else {
svg.style.strokeDasharray = (percents * this.maxLengths[index]) + ', 485.9';
svg.style.strokeDashoffset = '' + percents * this.maxOffset;
//svg.style.strokeDasharray = (percents * this.maxLengths[index]) + ', 485.9';
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 @@ @@ -5,6 +5,7 @@
*/
import findUpClassName from "../helpers/dom/findUpClassName";
import sequentialDom from "../helpers/sequentialDom";
import {isTouchSupported} from "../helpers/touchSupport";
import rootScope from "../lib/rootScope";
@ -44,22 +45,22 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool @@ -44,22 +45,22 @@ export function ripple(elem: HTMLElement, callback: (id: number) => Promise<bool
//const duration = isSquare || mediaSizes.isMobile ? 200 : 700;
//return;
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) {
let delay = Math.max(duration - elapsedTime, duration / 2);
setTimeout(() => elem.classList.add('hiding'), Math.max(delay - duration / 2, 0));
setTimeout(() => {
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
elem.remove();
if(onEnd) onEnd(clickId);
}, delay);
setTimeout(cb, delay);
} else {
elem.classList.add('hiding');
setTimeout(() => {
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
elem.remove();
if(onEnd) onEnd(clickId);
}, duration / 2);
setTimeout(cb, duration / 2);
}
if(!isTouchSupported) {

1
src/components/scrollable.ts

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

2
src/components/visibilityIntersector.ts

@ -120,4 +120,4 @@ export default class VisibilityIntersector { @@ -120,4 +120,4 @@ export default class VisibilityIntersector {
public lock() {
this.locked = true;
}
}
}

9
src/components/wrappers.ts

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

2
src/config/app.ts

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

5
src/helpers/blur.ts

@ -46,8 +46,11 @@ function processBlur(dataUri: string, radius: number, iterations: number) { @@ -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) {
return new Promise<string>((resolve) => {
if(blurPromises[dataUri]) return blurPromises[dataUri];
return blurPromises[dataUri] = new Promise<string>((resolve) => {
//return resolve(dataUri);
pushHeavyTask({
items: [[dataUri, radius, iterations]],

89
src/helpers/dom.ts

@ -529,7 +529,7 @@ export const getSelectedNodes = () => { @@ -529,7 +529,7 @@ export const getSelectedNodes = () => {
return nodes.filter(node => !!node);
};
export const isSelectionSingle = (input: Element = document.activeElement) => {
/* export const isSelectionSingle = (input: Element = document.activeElement) => {
const nodes = getSelectedNodes();
const parents = [...new Set(nodes.map(node => node.parentNode))];
const differentParents = parents.length > 1;
@ -545,7 +545,7 @@ export const isSelectionSingle = (input: Element = document.activeElement) => { @@ -545,7 +545,7 @@ export const isSelectionSingle = (input: Element = document.activeElement) => {
}
return single;
};
}; */
export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom', callback: () => void, listenerSetter: ListenerSetter) => {
if(isTouchSupported) {
@ -587,81 +587,6 @@ export const handleScrollSideEvent = (elem: HTMLElement, side: 'top' | 'bottom', @@ -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) {
inputs.forEach(input => {
input.addEventListener('change', () => {
@ -756,11 +681,13 @@ export function htmlToSpan(html: string) { @@ -756,11 +681,13 @@ export function htmlToSpan(html: string) {
}
export function replaceContent(elem: HTMLElement, node: string | Node) {
if(elem.children.length === 1) {
elem.firstChild.remove();
// * children.length doesn't count text nodes
if(elem.children.length) {
elem.firstChild.replaceWith(node);
} else if(!elem.firstChild) {
elem.append(node);
} else {
elem.textContent = '';
elem.append(node);
}
elem.append(node);
}

17
src/helpers/dom/getElementByPoint.ts

@ -0,0 +1,17 @@ @@ -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;

68
src/helpers/files.ts

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

7
src/index.ts

@ -58,9 +58,16 @@ console.timeEnd('get storage1'); */ @@ -58,9 +58,16 @@ console.timeEnd('get storage1'); */
// @ts-ignore
const w = window.visualViewport || window; // * handle iOS keyboard
let setViewportVH = false;
let lastVH: number;
const setVH = () => {
// @ts-ignore
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;
document.documentElement.style.setProperty('--vh', `${vh}px`);

3
src/lib/appManagers/appImManager.ts

@ -24,7 +24,7 @@ import appPhotosManager from './appPhotosManager'; @@ -24,7 +24,7 @@ import appPhotosManager from './appPhotosManager';
import appProfileManager from './appProfileManager';
import appStickersManager from './appStickersManager';
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 MarkupTooltip from '../../components/chat/markupTooltip';
import { isTouchSupported } from '../../helpers/touchSupport';
@ -47,6 +47,7 @@ import { i18n } from '../langPack'; @@ -47,6 +47,7 @@ import { i18n } from '../langPack';
import { SendMessageAction } from '../../layer';
import { highlightningColor } from '../../helpers/color';
import { getObjectKeysAndSort } from '../../helpers/object';
import { getFilesFromEvent } from '../../helpers/files';
//console.log('appImManager included33!');

21
src/lib/appManagers/appMessagesManager.ts

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

10
src/lib/appManagers/appPhotosManager.ts

@ -133,7 +133,7 @@ export class AppPhotosManager { @@ -133,7 +133,7 @@ export class AppPhotosManager {
public getUserPhotos(userId: number, maxId: string = '0', limit: number = 20) {
const inputUser = appUsersManager.getUserInput(userId);
return apiManager.invokeApi('photos.getUserPhotos', {
return apiManager.invokeApiCacheable('photos.getUserPhotos', {
user_id: inputUser,
offset: 0,
limit,
@ -204,13 +204,13 @@ export class AppPhotosManager { @@ -204,13 +204,13 @@ export class AppPhotosManager {
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 image = new Image();
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) => {
renderImageFromUrl(image, url, resolve);
});
@ -252,7 +252,7 @@ export class AppPhotosManager { @@ -252,7 +252,7 @@ export class AppPhotosManager {
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._ === 'document') {
const cacheContext = this.getCacheContext(photo);
@ -264,7 +264,7 @@ export class AppPhotosManager { @@ -264,7 +264,7 @@ export class AppPhotosManager {
const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
const thumb = sizes?.length ? sizes.find(size => size._ === 'photoStrippedSize') : null;
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 @@ @@ -11,7 +11,9 @@
import { MOUNT_CLASS_TO } from "../../config/debug";
import { tsNow } from "../../helpers/date";
import { replaceContent } from "../../helpers/dom";
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 apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
@ -504,8 +506,7 @@ export class AppProfileManager { @@ -504,8 +506,7 @@ export class AppProfileManager {
if(!needFadeIn) {
// смотри в misc.ts: renderImageFromUrl
callback = () => {
div.innerHTML = '';
div.append(img);
replaceContent(div, img);
div.dataset.color = '';
};
} else {
@ -515,15 +516,16 @@ export class AppProfileManager { @@ -515,15 +516,16 @@ export class AppProfileManager {
}
callback = () => {
div.innerHTML = '';
div.append(img);
replaceContent(div, img);
setTimeout(() => {
if(div.childElementCount) {
div.dataset.color = '';
if(animate) {
img.classList.remove('fade-in');
sequentialDom.mutateElement(img, () => {
img.classList.remove('fade-in');
});
}
}
}, animate ? 200 : 0);
@ -554,7 +556,7 @@ export class AppProfileManager { @@ -554,7 +556,7 @@ export class AppProfileManager {
//console.log('loadDialogPhoto location:', location, inputPeer);
if(peerId === myId && isDialog) {
div.innerHTML = '';
div.innerText = '';
div.dataset.color = '';
div.classList.add('tgico-saved');
div.classList.remove('tgico-deletedaccount');
@ -564,7 +566,7 @@ export class AppProfileManager { @@ -564,7 +566,7 @@ export class AppProfileManager {
if(peerId > 0) {
const user = appUsersManager.getUser(peerId);
if(user && user.pFlags && user.pFlags.deleted) {
div.innerHTML = '';
div.innerText = '';
div.dataset.color = appPeersManager.getPeerColorById(peerId);
div.classList.add('tgico-deletedaccount');
div.classList.remove('tgico-saved');
@ -578,7 +580,7 @@ export class AppProfileManager { @@ -578,7 +580,7 @@ export class AppProfileManager {
color = appPeersManager.getPeerColorById(peerId);
}
div.innerHTML = '';
div.innerText = '';
div.classList.remove('tgico-saved', 'tgico-deletedaccount');
div.dataset.color = color;

4
src/lib/appManagers/appStateManager.ts

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

4
src/scss/partials/_chatBubble.scss

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

63
src/scss/partials/_poll.scss

@ -55,10 +55,9 @@ poll-element { @@ -55,10 +55,9 @@ poll-element {
color: var(--primary-color);
cursor: pointer;
transform: scale(1);
transition: transform .2s ease;
body.animation-level-0 & {
transition: none;
@include animation-level(2) {
transition: transform .2s ease;
}
// @include respond-to(handhelds) {
@ -100,7 +99,6 @@ poll-element { @@ -100,7 +99,6 @@ poll-element {
font-weight: 500;
margin-top: 7px;
font-size: 14px;
transition: .34s opacity;
margin-left: -9px;
text-align: right;
width: 40px;
@ -117,10 +115,7 @@ poll-element { @@ -117,10 +115,7 @@ poll-element {
width: 16px;
font-weight: bold;
font-size: .75rem;
opacity: 0;
animation: fade-in-opacity .1s ease forwards;
animation-direction: reverse;
animation-delay: .24s;
opacity: 1;
display: flex;
align-items: center;
justify-content: center;
@ -148,14 +143,16 @@ poll-element { @@ -148,14 +143,16 @@ poll-element {
&:not(.is-correct):not(.is-chosen) {
.poll-answer-selected {
display: none;
opacity: 0;
}
}
// Multiple answers
&.is-chosing {
.poll-answer-selected {
opacity: 1;
.circle-hover {
.poll-answer-selected {
opacity: 1;
}
}
& ~ .poll-footer {
@ -238,11 +235,11 @@ poll-element { @@ -238,11 +235,11 @@ poll-element {
&-answer {
&.is-chosen:not(.is-correct) {
use {
stroke: #DF3F40;
stroke: var(--danger-color);
}
.poll-answer-selected {
background: #DF3F40;
background: var(--danger-color);
//line-height: 16px;
&:before {
@ -278,7 +275,6 @@ poll-element { @@ -278,7 +275,6 @@ poll-element {
left: -1px;
top: -1px;
transform: scale(1);
transition: .1s transform;
.poll-answer-selected {
display: flex!important;
@ -300,10 +296,13 @@ poll-element { @@ -300,10 +296,13 @@ poll-element {
border-radius: 50%;
height: 34px;
width: 34px;
transition: transform .12s;
background-color: #f4f4f4;
background-color: var(--light-secondary-text-color);
transform: scale(.1);
visibility: hidden;
@include animation-level(2) {
transition: transform .12s ease;
}
}
.progress-ring {
@ -315,12 +314,11 @@ poll-element { @@ -315,12 +314,11 @@ poll-element {
&__circle {
transform-origin: center;
transform: rotate(-90deg);
transition: stroke-dashoffset .15s;
stroke-dasharray: 56.5487, 56.5487;
stroke-dashoffset: 0;
stroke-opacity: 1;
stroke-width: 2;
stroke: var(--border-color);
stroke: var(--poll-circle-color);
fill: transparent;
}
}
@ -333,10 +331,6 @@ poll-element { @@ -333,10 +331,6 @@ poll-element {
.poll-answer-percents {
opacity: 1;
}
.poll-answer-selected {
animation-direction: normal;
}
}
&.is-retracting {
@ -353,6 +347,29 @@ poll-element { @@ -353,6 +347,29 @@ poll-element {
.poll-line {
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; @@ -42,8 +42,8 @@ $chat-padding-handhelds: .5rem;
--hover-alpha: #{$hover-alpha};
--transition-standard-easing: cubic-bezier(.4, .0, .2, 1);
--transition-standard-in-time: .25s;
--transition-standard-out-time: .2s;
--transition-standard-in-time: .3s;
--transition-standard-out-time: .25s;
--transition-standard-in: var(--transition-standard-in-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; @@ -149,6 +149,7 @@ $chat-padding-handhelds: .5rem;
--badge-text-color: #fff;
--link-color: #00488f;
--ripple-color: rgba(0, 0, 0, .08);
--poll-circle-color: var(--border-color);
--message-background-color: var(--surface-color);
--message-checkbox-color: #61c642;
@ -192,6 +193,7 @@ html.night { @@ -192,6 +193,7 @@ html.night {
--badge-text-color: #fff;
--link-color: var(--primary-color);
--ripple-color: rgba(255, 255, 255, .08);
--poll-circle-color: #fff;
--message-background-color: var(--surface-color);
--message-checkbox-color: var(--primary-color);

Loading…
Cancel
Save