From 640ff632090e9b6f6728fffb0ce583888cd38cef Mon Sep 17 00:00:00 2001 From: morethanwords Date: Mon, 11 Jan 2021 19:21:03 +0400 Subject: [PATCH] Fix blinking stickers, gifs Fix icons Fix input search layout Fix message input layout --- src/components/chat/bubbles.ts | 5 +- src/components/chat/input.ts | 4 +- src/components/chat/markupTooltip.ts | 2 +- src/components/chat/search.ts | 2 +- src/components/chat/topbar.ts | 2 +- .../emoticonsDropdown/tabs/stickers.ts | 2 + src/components/inputSearch.ts | 4 +- src/components/lazyLoadQueue.ts | 7 +- src/components/preloader.ts | 10 +- src/components/sidebarLeft/tabs/addMembers.ts | 6 +- src/components/sidebarLeft/tabs/newChannel.ts | 2 +- src/components/sidebarLeft/tabs/newGroup.ts | 2 +- src/components/slider.ts | 2 +- src/components/visibilityIntersector.ts | 2 +- src/components/wrappers.ts | 136 ++++---- src/index.hbs | 8 +- src/lib/appManagers/appDocsManager.ts | 45 ++- src/lib/appManagers/appPhotosManager.ts | 20 +- src/lib/cacheStorage.ts | 4 +- src/scss/partials/_chat.scss | 41 ++- src/scss/partials/_chatBubble.scss | 2 +- src/scss/partials/_fonts.scss | 305 +++++++++--------- src/scss/partials/_gifsMasonry.scss | 4 + src/scss/partials/_ico.scss | 277 ++++++++-------- src/scss/partials/_input.scss | 31 +- src/scss/partials/_leftSidebar.scss | 5 +- src/scss/partials/_rightSidebar.scss | 12 +- src/scss/partials/_sidebar.scss | 2 + src/scss/style.scss | 6 +- 29 files changed, 513 insertions(+), 437 deletions(-) diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index ea28411a..cffe7ec8 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -1967,7 +1967,8 @@ export default class ChatBubbles { play: true, loop: true, emoji: bubble.classList.contains('emoji-big') ? messageMessage : undefined, - withThumb: true + withThumb: true, + loadPromises }); break; @@ -2219,7 +2220,7 @@ export default class ChatBubbles { if(savedFrom && this.peerId !== REPLIES_PEER_ID) { const goto = document.createElement('div'); - goto.classList.add('bubble-beside-button', 'goto-original', 'tgico-arrow-next'); + goto.classList.add('bubble-beside-button', 'goto-original', 'tgico-arrow_next'); bubbleContainer.append(goto); bubble.dataset.savedFrom = savedFrom; bubble.classList.add('with-beside-button'); diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index 6d74e3a9..3f6ab014 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -134,7 +134,7 @@ export default class ChatInput { this.inputContainer.append(this.rowsWrapper); this.chatInput.append(this.inputContainer); - this.goDownBtn = Button('bubbles-go-down btn-corner btn-circle z-depth-1 hide', {icon: 'arrow-down'}); + this.goDownBtn = Button('bubbles-go-down btn-corner btn-circle z-depth-1 hide', {icon: 'arrow_down'}); this.goDownUnreadBadge = document.createElement('span'); this.goDownUnreadBadge.classList.add('badge', 'badge-24', 'badge-green'); this.goDownBtn.append(this.goDownUnreadBadge); @@ -172,7 +172,7 @@ export default class ChatInput { this.inputScroll = new Scrollable(this.inputMessageContainer); if(this.chat.type === 'chat') { - this.btnScheduled = ButtonIcon('schedule', {noRipple: true}); + this.btnScheduled = ButtonIcon('scheduled', {noRipple: true}); this.btnScheduled.classList.add('btn-scheduled', 'hide'); attachClickEvent(this.btnScheduled, (e) => { diff --git a/src/components/chat/markupTooltip.ts b/src/components/chat/markupTooltip.ts index 4337d07c..9f98ac1b 100644 --- a/src/components/chat/markupTooltip.ts +++ b/src/components/chat/markupTooltip.ts @@ -61,7 +61,7 @@ export default class MarkupTooltip { } }); - this.linkBackButton = ButtonIcon('back', {noRipple: true}); + this.linkBackButton = ButtonIcon('arrow_back', {noRipple: true}); this.linkInput = document.createElement('input'); this.linkInput.placeholder = 'Enter URL...'; this.linkInput.classList.add('input-clear'); diff --git a/src/components/chat/search.ts b/src/components/chat/search.ts index 178f0fd8..e3714cd9 100644 --- a/src/components/chat/search.ts +++ b/src/components/chat/search.ts @@ -32,7 +32,7 @@ export default class ChatSearch { this.element.classList.add('sidebar-header', 'chat-search', 'chatlist-container'); this.backBtn = document.createElement('button'); - this.backBtn.classList.add('btn-icon', 'tgico-back', 'sidebar-close-button'); + this.backBtn.classList.add('btn-icon', 'tgico-arrow_back', 'sidebar-close-button'); ripple(this.backBtn); this.backBtn.addEventListener('click', () => { diff --git a/src/components/chat/topbar.ts b/src/components/chat/topbar.ts index 1535ef9e..590b8d11 100644 --- a/src/components/chat/topbar.ts +++ b/src/components/chat/topbar.ts @@ -55,7 +55,7 @@ export default class ChatTopbar { this.container = document.createElement('div'); this.container.classList.add('sidebar-header', 'topbar'); - this.btnBack = ButtonIcon('back sidebar-close-button', {noRipple: true}); + this.btnBack = ButtonIcon('arrow_back sidebar-close-button', {noRipple: true}); // * chat info section this.chatInfo = document.createElement('div'); diff --git a/src/components/emoticonsDropdown/tabs/stickers.ts b/src/components/emoticonsDropdown/tabs/stickers.ts index 4adc5715..3f525a85 100644 --- a/src/components/emoticonsDropdown/tabs/stickers.ts +++ b/src/components/emoticonsDropdown/tabs/stickers.ts @@ -72,6 +72,8 @@ export class SuperStickerRenderer { const size = mediaSizes.active.esgSticker.width; + console.log('processVisibleDiv:', div); + const promise = wrapSticker({ doc, div: div as HTMLDivElement, diff --git a/src/components/inputSearch.ts b/src/components/inputSearch.ts index 26ab2d51..514e70f1 100644 --- a/src/components/inputSearch.ts +++ b/src/components/inputSearch.ts @@ -27,10 +27,10 @@ export default class InputSearch { this.input = this.inputField.input; this.input.classList.add('input-search-input'); - const searchIcon = document.createElement('span'); + const searchIcon = document.createElement('i'); searchIcon.classList.add('tgico', 'tgico-search'); - this.clearBtn = document.createElement('span'); + this.clearBtn = document.createElement('i'); this.clearBtn.classList.add('tgico', 'btn-icon', 'tgico-close'); this.input.addEventListener('input', this.onInput); diff --git a/src/components/lazyLoadQueue.ts b/src/components/lazyLoadQueue.ts index c56b8070..47af41b5 100644 --- a/src/components/lazyLoadQueue.ts +++ b/src/components/lazyLoadQueue.ts @@ -224,7 +224,9 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector { private onVisibilityChange = (target: HTMLElement, visible: boolean) => { if(visible) { - this.log('isIntersecting', target); + /* if(DEBUG) { + this.log('isIntersecting', target); + } */ // need for set element first if scrolled findAndSpliceAll(this.queue, (i) => i.div === target).forEach(item => { @@ -271,7 +273,8 @@ export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector { this.intersector = new VisibilityIntersector((target, visible) => { const spliced = findAndSpliceAll(this.queue, (i) => i.div === target); if(visible) { - spliced.forEach(item => { + const items = spliced.length ? spliced : [this._queue.get(target)]; + items.forEach(item => { this.queue.unshift(item || this._queue.get(target)); }); } diff --git a/src/components/preloader.ts b/src/components/preloader.ts index d860ee8e..18ce8a7d 100644 --- a/src/components/preloader.ts +++ b/src/components/preloader.ts @@ -65,17 +65,23 @@ export default class ProgressivePreloader { const tempId = --this.tempId; - const onEnd = () => { + const onEnd = (successfully: boolean) => { promise.notify = null; if(tempId === this.tempId) { + if(successfully) { + this.setProgress(100); + } + this.detach(); this.promise = promise = null; } }; //promise.catch(onEnd); - promise.finally(onEnd); + promise + .then(() => onEnd(true)) + .catch(() => onEnd(false)); if(promise.addNotifyListener) { promise.addNotifyListener((details: {done: number, total: number}) => { diff --git a/src/components/sidebarLeft/tabs/addMembers.ts b/src/components/sidebarLeft/tabs/addMembers.ts index 418728b0..61e9b0d2 100644 --- a/src/components/sidebarLeft/tabs/addMembers.ts +++ b/src/components/sidebarLeft/tabs/addMembers.ts @@ -16,7 +16,7 @@ export default class AppAddMembersTab extends SliderSuperTab { } protected init() { - this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow-next'}); + this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow_next'}); this.content.append(this.nextBtn); this.nextBtn.addEventListener('click', () => { @@ -29,7 +29,7 @@ export default class AppAddMembersTab extends SliderSuperTab { const promise = this.takeOut(peerIds); if(promise instanceof Promise) { - this.nextBtn.classList.remove('tgico-arrow-next'); + this.nextBtn.classList.remove('tgico-arrow_next'); this.nextBtn.disabled = true; putPreloader(this.nextBtn); this.selector.freezed = true; @@ -83,7 +83,7 @@ export default class AppAddMembersTab extends SliderSuperTab { }); } - this.nextBtn.classList.add('tgico-arrow-next'); + this.nextBtn.classList.add('tgico-arrow_next'); this.nextBtn.innerHTML = ''; this.nextBtn.disabled = false; this.nextBtn.classList.toggle('is-visible', this.skippable); diff --git a/src/components/sidebarLeft/tabs/newChannel.ts b/src/components/sidebarLeft/tabs/newChannel.ts index eeedcc34..125483bc 100644 --- a/src/components/sidebarLeft/tabs/newChannel.ts +++ b/src/components/sidebarLeft/tabs/newChannel.ts @@ -54,7 +54,7 @@ export default class AppNewChannelTab extends SliderSuperTab { caption.classList.add('caption'); caption.innerText = 'You can provide an optional description for your channel.'; - this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow-next'}); + this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow_next'}); this.nextBtn.addEventListener('click', () => { const title = this.channelNameInputField.value; diff --git a/src/components/sidebarLeft/tabs/newGroup.ts b/src/components/sidebarLeft/tabs/newGroup.ts index 66ca3dc1..0b19c725 100644 --- a/src/components/sidebarLeft/tabs/newGroup.ts +++ b/src/components/sidebarLeft/tabs/newGroup.ts @@ -44,7 +44,7 @@ export default class AppNewGroupTab extends SliderSuperTab { this.nextBtn.classList.toggle('is-visible', !!value.length && !this.groupNameInputField.input.classList.contains('error')); }); - this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow-next'}); + this.nextBtn = Button('btn-corner btn-circle', {icon: 'arrow_next'}); this.nextBtn.addEventListener('click', () => { const title = this.groupNameInputField.value; diff --git a/src/components/slider.ts b/src/components/slider.ts index c2508a9d..9484cb88 100644 --- a/src/components/slider.ts +++ b/src/components/slider.ts @@ -30,7 +30,7 @@ export class SliderSuperTab implements SliderTab { this.header = document.createElement('div'); this.header.classList.add('sidebar-header'); - this.closeBtn = ButtonIcon('back sidebar-close-button', {noRipple: true}); + this.closeBtn = ButtonIcon('arrow_back sidebar-close-button', {noRipple: true}); this.title = document.createElement('div'); this.title.classList.add('sidebar-header__title'); this.header.append(this.closeBtn, this.title); diff --git a/src/components/visibilityIntersector.ts b/src/components/visibilityIntersector.ts index 452b4ef6..0a75489f 100644 --- a/src/components/visibilityIntersector.ts +++ b/src/components/visibilityIntersector.ts @@ -17,7 +17,7 @@ export default class VisibilityIntersector { entries.forEach(entry => { const target = entry.target as TargetType; - if(this.items.get(target) == entry.isIntersecting) { + if(this.items.get(target) === entry.isIntersecting) { return; } else { this.items.set(target, entry.isIntersecting); diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 459f2935..56dbc417 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -224,7 +224,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai preloader = message.media.preloader as ProgressivePreloader; preloader.attach(container, false); } else if(!doc.downloaded && !doc.supportsStreaming) { - const promise = appDocsManager.downloadDoc(doc, undefined, lazyLoadQueue?.queueId); + const promise = appDocsManager.downloadDoc(doc, /* undefined, */lazyLoadQueue?.queueId); preloader = new ProgressivePreloader(null, true, false, 'prepend'); preloader.attach(container, true, promise); @@ -608,7 +608,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT if(loadPromise) return loadPromise; const promise = photo._ === 'document' && photo.mime_type === 'image/gif' ? - appDocsManager.downloadDoc(photo, undefined, lazyLoadQueue?.queueId) : + appDocsManager.downloadDoc(photo, /* undefined, */lazyLoadQueue?.queueId) : appPhotosManager.preloadPhoto(photo, size, lazyLoadQueue?.queueId); if(preloader) { @@ -667,7 +667,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT }; } -export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: { +export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop, loadPromises}: { doc: MyDocument, div: HTMLElement, middleware?: () => boolean, @@ -679,7 +679,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o width?: number, height?: number, withThumb?: boolean, - loop?: boolean + loop?: boolean, + loadPromises?: Promise[] }) { const stickerType = doc.sticker; @@ -707,29 +708,30 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o const toneIndex = emoji ? getEmojiToneIndex(emoji) : -1; + let loadThumbPromise = deferredPromise(); + let haveThumbCached = false; if((doc.thumbs?.length || doc.stickerCachedThumbs) && !div.firstElementChild && (!doc.downloaded || stickerType == 2 || onlyThumb)/* && doc.thumbs[0]._ != 'photoSizeEmpty' */) { let thumb = doc.stickerCachedThumbs && doc.stickerCachedThumbs[toneIndex] || doc.thumbs[0]; //console.log('wrap sticker', thumb, div); - let img: HTMLImageElement; + let thumbImage: HTMLImageElement; const afterRender = () => { if(!div.childElementCount) { - div.append(img); + thumbImage.classList.add('media-sticker', 'thumbnail'); + div.append(thumbImage); + loadThumbPromise.resolve(); } }; if('url' in thumb) { - img = new Image(); - renderImageFromUrl(img, thumb.url, afterRender); + thumbImage = new Image(); + renderImageFromUrl(thumbImage, thumb.url, afterRender); + haveThumbCached = true; } else if('bytes' in thumb) { - if(thumb._ == 'photoPathSize') { + if(thumb._ === 'photoPathSize') { if(thumb.bytes.length) { - //if(!doc.w) console.error('no w', doc); const d = appPhotosManager.getPathFromPhotoPathSize(thumb); - /* if(d == 'Mz' || d.includes('151,48,349,33z')) { - console.error('no path', doc); - } */ div.innerHTML = ` `; @@ -738,10 +740,12 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o } } - if(thumb && thumb._ != 'photoPathSize' && toneIndex <= 0) { - img = new Image(); + if(thumb && thumb._ !== 'photoPathSize' && toneIndex <= 0) { + thumbImage = new Image(); + if((webpWorkerController.isWebpSupported() || doc.pFlags.stickerThumbConverted || thumb.url)/* && false */) { - renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb as PhotoSize.photoStrippedSize, true), afterRender); + renderImageFromUrl(thumbImage, appPhotosManager.getPreviewURLFromThumb(thumb as PhotoSize.photoStrippedSize, true), afterRender); + haveThumbCached = true; } else { webpWorkerController.convert(doc.id, (thumb as PhotoSize.photoStrippedSize).bytes as Uint8Array).then(bytes => { (thumb as PhotoSize.photoStrippedSize).bytes = bytes; @@ -750,20 +754,20 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o if(middleware && !middleware()) return; if(!div.childElementCount) { - renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb as PhotoSize.photoStrippedSize, true), afterRender); + renderImageFromUrl(thumbImage, appPhotosManager.getPreviewURLFromThumb(thumb as PhotoSize.photoStrippedSize, true), afterRender); } }).catch(() => {}); } } } else if(stickerType == 2 && (withThumb || onlyThumb) && toneIndex <= 0) { - img = new Image(); + thumbImage = new Image(); const load = () => { if(div.childElementCount || (middleware && !middleware())) return; const r = () => { if(div.childElementCount || (middleware && !middleware())) return; - renderImageFromUrl(img, (thumb as PhotoSize.photoStrippedSize).url, afterRender); + renderImageFromUrl(thumbImage, (thumb as PhotoSize.photoStrippedSize).url, afterRender); }; if((thumb as PhotoSize.photoStrippedSize).url) { @@ -779,10 +783,18 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o return Promise.resolve(); } else { load(); + + if((thumb as any).url) { + haveThumbCached = true; + } } } } + if(loadPromises && haveThumbCached) { + loadPromises.push(loadThumbPromise); + } + if(onlyThumb) { // for sticker panel return Promise.resolve(); } @@ -803,7 +815,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o //appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => { //fetch(doc.url).then(res => res.json()).then(async(json) => { - /* return */ await appDocsManager.downloadDoc(doc, undefined, lazyLoadQueue?.queueId) + /* return */ await appDocsManager.downloadDoc(doc, /* undefined, */lazyLoadQueue?.queueId) .then(readBlobAsText) //.then(JSON.parse) .then(async(json) => { @@ -827,7 +839,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o const needFadeIn = !element || element.tagName === 'svg'; const cb = () => { - if(element && element != animation.canvas) { + if(element && element !== animation.canvas) { element.remove(); } }; @@ -866,46 +878,60 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o }); //console.timeEnd('render sticker' + doc.id); - } else if(stickerType == 1) { - let img = new Image(); + } else if(stickerType === 1) { + const image = new Image(); + const thumbImage = div.firstElementChild && div.firstElementChild !== image ? div.firstElementChild : null; + const needFadeIn = !downloaded || thumbImage; - if(!downloaded && (!div.firstElementChild || div.firstElementChild.tagName != 'IMG')) { - img.classList.add('fade-in-transition'); - img.style.opacity = '0'; + image.classList.add('media-sticker'); - /* if(!div.firstElementChild) { - div.append(img); - } */ + if(needFadeIn) { + image.classList.add('fade-in'); + } - img.addEventListener('load', () => { - doc.downloaded = true; - - window.requestAnimationFrame(() => { - img.style.opacity = ''; + return new Promise((resolve, reject) => { + const r = () => { + if(middleware && !middleware()) return resolve(); + + renderImageFromUrl(image, doc.url, () => { + div.append(image); + + window.requestAnimationFrame(() => { + resolve(); + }); + + if(needFadeIn) { + setTimeout(() => { + image.classList.remove('fade-in'); + + if(thumbImage) { + thumbImage.remove(); + } + }, 200); + } }); - }); - } - - const r = () => { - if(middleware && !middleware()) return; - - renderImageFromUrl(img, doc.url, () => { - if(div.firstElementChild && div.firstElementChild != img) { - div.firstElementChild.remove(); - } + }; - div.append(img); - }); - }; - - if(doc.url) r(); - else { - appDocsManager.downloadDoc(doc, undefined, lazyLoadQueue?.queueId).then(r); - } + if(doc.url) r(); + else { + appDocsManager.downloadDoc(doc, /* undefined, */lazyLoadQueue?.queueId).then(r, resolve); + } + }); } - }; - - return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load/* , wasSeen: group == 'chat' && stickerType != 2 */}), Promise.resolve()) : load(); + }; + + const loadPromise: Promise = lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? + (lazyLoadQueue.push({div, load}), Promise.resolve()) : + load(); + + if(doc.downloaded && stickerType === 1) { + loadThumbPromise = loadPromise; + if(loadPromises) { + loadPromises.push(loadThumbPromise); + } + } + + return loadPromise; } export function wrapReply(title: string, subtitle: string, message?: any) { diff --git a/src/index.hbs b/src/index.hbs index ec4b3096..d67e3c94 100644 --- a/src/index.hbs +++ b/src/index.hbs @@ -138,7 +138,7 @@ - +