From c30aa30bd29eba1cc7ca80da06ab1c1a2815f1d8 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Mon, 4 Apr 2022 17:47:06 +0300 Subject: [PATCH] Fix collapsed document file name after animation --- src/components/audio.ts | 1 + src/components/chat/bubbles.ts | 4 +- src/components/middleEllipsis.ts | 78 +++++++++++++++--------------- src/components/wrappers.ts | 16 ++++-- src/helpers/canvas/getTextWidth.ts | 21 ++++++++ src/helpers/mediaSizes.ts | 11 +++-- 6 files changed, 84 insertions(+), 47 deletions(-) create mode 100644 src/helpers/canvas/getTextWidth.ts diff --git a/src/components/audio.ts b/src/components/audio.ts index d0131d66..12dc0423 100644 --- a/src/components/audio.ts +++ b/src/components/audio.ts @@ -295,6 +295,7 @@ function wrapAudio(audioEl: AudioElement) { const middleEllipsisEl = new MiddleEllipsisElement(); middleEllipsisEl.dataset.fontWeight = audioEl.dataset.fontWeight; + middleEllipsisEl.dataset.sizeType = audioEl.dataset.sizeType; if(isVoice) { middleEllipsisEl.append(appMessagesManager.wrapSenderToPeer(message)); } else { diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 64bf5246..ac34b344 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -3369,7 +3369,8 @@ export default class ChatBubbles { message: message as Message.message, autoDownloadSize: this.chat.autoDownload.file, lazyLoadQueue: this.lazyLoadQueue, - loadPromises + loadPromises, + sizeType: 'documentName' }); preview.append(docDiv); preview.classList.add('preview-with-document'); @@ -3578,6 +3579,7 @@ export default class ChatBubbles { useSearch: !(message as Message.message).pFlags.is_scheduled, isScheduled: (message as Message.message).pFlags.is_scheduled } : undefined, + sizeType: 'documentName' }); if(newNameContainer) { diff --git a/src/components/middleEllipsis.ts b/src/components/middleEllipsis.ts index c1616b34..990f32ac 100644 --- a/src/components/middleEllipsis.ts +++ b/src/components/middleEllipsis.ts @@ -4,7 +4,10 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ +import getTextWidth from "../helpers/canvas/getTextWidth"; +import mediaSizes, { MediaSize } from "../helpers/mediaSizes"; import clamp from "../helpers/number/clamp"; +import { fastRaf } from "../helpers/schedulers"; // Thanks to https://stackoverflow.com/a/49349813 @@ -32,17 +35,24 @@ const map: Map = new Set(); export const fontFamily = 'Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif'; const fontSize = '16px'; -let timeoutId: number; +let pendingTest = false; -const setTestQueue = () => { - cancelAnimationFrame(timeoutId); - timeoutId = window.requestAnimationFrame(testQueueElements); -}; +function setTestQueue() { + if(pendingTest) { + return; + } + + pendingTest = true; + fastRaf(() => { + pendingTest = false; + testQueueElements(); + }); +} -const testQueueElements = () => { +function testQueueElements() { testQueue.forEach(testElement); testQueue.clear(); -}; +} window.addEventListener('resize', () => { for(const [key] of map) { @@ -52,7 +62,19 @@ window.addEventListener('resize', () => { setTestQueue(); }, {capture: true, passive: true}); -const testElement = (element: HTMLElement) => { +function getElementWidth(element: HTMLElement) { + const type = element.dataset.sizeType; + if(type) { + const mediaSize = mediaSizes.active; + // @ts-ignore + const size: MediaSize = mediaSize[type]; + return size.width; + } + + return element.getBoundingClientRect().width; +} + +function testElement(element: HTMLElement) { //const perf = performance.now(); // do not recalculate variables a second time let mapped = map.get(element); @@ -75,7 +97,7 @@ const testElement = (element: HTMLElement) => { textWidth = getTextWidth(text, font); //const perf = performance.now(); - elementWidth = element.getBoundingClientRect().width; + elementWidth = getElementWidth(element); //console.log('testMiddleEllipsis get offsetWidth:', performance.now() - perf, font); mapped = {text, textLength, from, multiplier, font, textWidth, elementWidth}; map.set(element, mapped); @@ -83,7 +105,7 @@ const testElement = (element: HTMLElement) => { //console.log('[MEE] testElement map set', element); } - const newElementWidth = element.getBoundingClientRect().width; + const newElementWidth = getElementWidth(element); const widthChanged = firstTime || elementWidth !== newElementWidth; !firstTime && widthChanged && (mapped.elementWidth = elementWidth = newElementWidth); @@ -108,7 +130,7 @@ const testElement = (element: HTMLElement) => { } // * set new width after cutting text - mapped.elementWidth = element.getBoundingClientRect().width; + mapped.elementWidth = getElementWidth(element); //mapped.textWidth = smallerWidth; } else { element.removeAttribute('title'); @@ -116,40 +138,19 @@ const testElement = (element: HTMLElement) => { } //console.log('testMiddleEllipsis for element:', elm, performance.now() - perf); -}; - -let context: CanvasRenderingContext2D; -/** - * Get the text width - * @param {string} text - * @param {string} font - */ -function getTextWidth(text: string, font: string) { - //const perf = performance.now(); - if(!context) { - const canvas = document.createElement('canvas'); - context = canvas.getContext('2d'); - context.font = font; - } - - //context.font = font; - const metrics = context.measureText(text); - //console.log('getTextWidth perf:', performance.now() - perf); - return metrics.width; - //return Math.round(metrics.width); } export class MiddleEllipsisElement extends HTMLElement { - constructor() { - super(); - } - connectedCallback() { //console.log('[MEE]: connectedCallback before', map.has(this), testQueue.has(this), map.size, this.textContent, map); map.set(this, null); - testQueue.add(this); - setTestQueue(); + if(this.dataset.sizeType) { + testElement(this); + } else { + testQueue.add(this); + setTestQueue(); + } //testElement(this); //console.log('[MEE]: connectedCallback after', map.has(this), map.size, testQueue.has(this), testQueue.size); @@ -157,6 +158,7 @@ export class MiddleEllipsisElement extends HTMLElement { disconnectedCallback() { const deleted = map.delete(this); + testQueue.delete(this); //console.log('[MEE]: disconnectedCallback', deleted, map.has(this), map.size, this.textContent, map); } } diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index fb1e92e9..dbb694c4 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -8,7 +8,7 @@ import type Chat from './chat/chat'; import { getEmojiToneIndex } from '../vendor/emoji'; import { deferredPromise } from '../helpers/cancellablePromise'; import { formatFullSentTime } from '../helpers/date'; -import mediaSizes, { ScreenSize } from '../helpers/mediaSizes'; +import mediaSizes, { MediaSizeType, ScreenSize } from '../helpers/mediaSizes'; import { IS_SAFARI } from '../environment/userAgent'; import { Message, MessageMedia, PhotoSize, StickerSet, WebPage } from '../layer'; import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager"; @@ -570,7 +570,7 @@ rootScope.addEventListener('download_start', (docId) => { }); }); -export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises, autoDownloadSize, lazyLoadQueue}: { +export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises, autoDownloadSize, lazyLoadQueue, sizeType}: { message: Message.message, withTime?: boolean, fontWeight?: number, @@ -579,9 +579,11 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS searchContext?: MediaSearchContext, loadPromises?: Promise[], autoDownloadSize?: number, - lazyLoadQueue?: LazyLoadQueue + lazyLoadQueue?: LazyLoadQueue, + sizeType?: MediaSizeType }): HTMLElement { if(!fontWeight) fontWeight = 500; + if(!sizeType) sizeType = '' as any; const noAutoDownload = autoDownloadSize === 0; const doc = ((message.media as MessageMedia.messageMediaDocument).document || ((message.media as MessageMedia.messageMediaWebPage).webpage as WebPage.webPage).document) as MyDocument; @@ -600,6 +602,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS if(uploading) audioElement.preloader = (message.media as any).preloader; audioElement.dataset.fontWeight = '' + fontWeight; + audioElement.dataset.sizeType = sizeType; audioElement.render(); return audioElement; } @@ -671,6 +674,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS const nameDiv = docDiv.querySelector('.document-name') as HTMLElement; const middleEllipsisEl = new MiddleEllipsisElement(); middleEllipsisEl.dataset.fontWeight = '' + fontWeight; + middleEllipsisEl.dataset.sizeType = sizeType; middleEllipsisEl.innerHTML = fileName; nameDiv.append(middleEllipsisEl); @@ -2033,7 +2037,7 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo }); } -export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat, loadPromises, autoDownloadSize, lazyLoadQueue, searchContext, useSearch}: { +export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat, loadPromises, autoDownloadSize, lazyLoadQueue, searchContext, useSearch, sizeType}: { albumMustBeRenderedFull: boolean, message: any, messageDiv: HTMLElement, @@ -2045,6 +2049,7 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, lazyLoadQueue?: LazyLoadQueue, searchContext?: MediaSearchContext, useSearch?: boolean, + sizeType?: MediaSizeType }) { let nameContainer: HTMLElement; const mids = albumMustBeRenderedFull ? chat.getMidsByMid(message.mid) : [message.mid]; @@ -2059,7 +2064,8 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, loadPromises, autoDownloadSize, lazyLoadQueue, - searchContext + searchContext, + sizeType }); const container = document.createElement('div'); diff --git a/src/helpers/canvas/getTextWidth.ts b/src/helpers/canvas/getTextWidth.ts new file mode 100644 index 00000000..b947dcc5 --- /dev/null +++ b/src/helpers/canvas/getTextWidth.ts @@ -0,0 +1,21 @@ + +let context: CanvasRenderingContext2D; +/** + * Get the text width + * @param {string} text + * @param {string} font + */ +export default function getTextWidth(text: string, font: string) { + //const perf = performance.now(); + if(!context) { + const canvas = document.createElement('canvas'); + context = canvas.getContext('2d'); + context.font = font; + } + + //context.font = font; + const metrics = context.measureText(text); + //console.log('getTextWidth perf:', performance.now() - perf); + return metrics.width; + //return Math.round(metrics.width); +} diff --git a/src/helpers/mediaSizes.ts b/src/helpers/mediaSizes.ts index ddcd9500..9449bd16 100644 --- a/src/helpers/mediaSizes.ts +++ b/src/helpers/mediaSizes.ts @@ -39,9 +39,12 @@ type MediaTypeSizes = { staticSticker: MediaSize, emojiSticker: MediaSize, poll: MediaSize, - round: MediaSize + round: MediaSize, + documentName: MediaSize }; +export type MediaSizeType = keyof MediaTypeSizes; + export enum ScreenSize { mobile, medium, @@ -72,7 +75,8 @@ class MediaSizes extends EventListenerBase<{ staticSticker: makeMediaSize(180, 180), emojiSticker: makeMediaSize(112, 112), poll: makeMediaSize(240, 0), - round: makeMediaSize(200, 200) + round: makeMediaSize(200, 200), + documentName: makeMediaSize(200, 0) }, desktop: { regular: makeMediaSize(420, 340), @@ -83,7 +87,8 @@ class MediaSizes extends EventListenerBase<{ staticSticker: makeMediaSize(200, 200), emojiSticker: makeMediaSize(112, 112), poll: makeMediaSize(330, 0), - round: makeMediaSize(280, 280) + round: makeMediaSize(280, 280), + documentName: makeMediaSize(240, 0) } };