From e1de44ebe4bef98ab652e14e8b58b079672ef85a Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sat, 13 Mar 2021 14:12:24 +0400 Subject: [PATCH] Auto-download settings --- src/components/appMediaPlaybackController.ts | 17 +- src/components/audio.ts | 6 +- src/components/chat/bubbles.ts | 28 ++- src/components/chat/chat.ts | 23 ++ src/components/lazyLoadQueue.ts | 4 +- src/components/preloader.ts | 2 +- .../sidebarLeft/tabs/generalSettings.ts | 2 +- src/components/sidebarLeft/tabs/settings.ts | 5 +- src/components/wrappers.ts | 206 +++++++++++------- src/lib/appManagers/appDocsManager.ts | 13 +- src/lib/appManagers/appDownloadManager.ts | 18 +- src/lib/appManagers/appImManager.ts | 10 +- src/lib/appManagers/appMessagesManager.ts | 16 +- src/lib/appManagers/appPhotosManager.ts | 10 +- src/lib/mtproto/apiFileManager.ts | 3 +- 15 files changed, 232 insertions(+), 131 deletions(-) diff --git a/src/components/appMediaPlaybackController.ts b/src/components/appMediaPlaybackController.ts index b124867f..43b510e4 100644 --- a/src/components/appMediaPlaybackController.ts +++ b/src/components/appMediaPlaybackController.ts @@ -3,7 +3,6 @@ import appMessagesManager from "../lib/appManagers/appMessagesManager"; import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager"; import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise"; import { isSafari } from "../helpers/userAgent"; -import { isInDOM } from "../helpers/dom"; import { MOUNT_CLASS_TO } from "../config/debug"; // TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда @@ -111,17 +110,17 @@ class AppMediaPlaybackController { waitingStorage[mid] = deferred; } - // если что - загрузит voice или round заранее, так правильнее - const downloadPromise: Promise = !doc.supportsStreaming ? appDocsManager.downloadDoc(doc) : Promise.resolve(); - Promise.all([deferred, downloadPromise]).then(() => { + deferred.then(() => { //media.autoplay = true; //console.log('will set media url:', media, doc, doc.type, doc.url); - if(doc.type === 'audio' && doc.supportsStreaming && isSafari) { - this.handleSafariStreamable(media); - } - - media.src = doc.url; + ((!doc.supportsStreaming ? appDocsManager.downloadDoc(doc) : Promise.resolve()) as Promise).then(() => { + if(doc.type === 'audio' && doc.supportsStreaming && isSafari) { + this.handleSafariStreamable(media); + } + + media.src = doc.url; + }); }, onError); return storage[mid] = media; diff --git a/src/components/audio.ts b/src/components/audio.ts index b1edfe63..9d7ac288 100644 --- a/src/components/audio.ts +++ b/src/components/audio.ts @@ -341,6 +341,7 @@ export default class AudioElement extends HTMLElement { public voiceAsMusic = false; public searchContext: SearchSuperContext; public showSender = false; + public noAutoDownload: boolean; private attachedHandlers: {[name: string]: any[]} = {}; private onTypeDisconnect: () => void; @@ -464,7 +465,10 @@ export default class AudioElement extends HTMLElement { }; attachClickEvent(this, onClick); - onClick(); + + if(!this.noAutoDownload) { + onClick(); + } } else { if(doc.supportsStreaming) { onLoad(false); diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 01903d49..29b06905 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -30,7 +30,7 @@ import { langPack } from "../../lib/langPack"; import AvatarElement from "../avatar"; import { formatPhoneNumber } from "../misc"; import { ripple } from "../ripple"; -import { wrapAlbum, wrapPhoto, wrapVideo, wrapDocument, wrapSticker, wrapPoll, wrapReply, wrapGroupedDocuments } from "../wrappers"; +import { wrapAlbum, wrapPhoto, wrapVideo, wrapDocument, wrapSticker, wrapPoll, wrapGroupedDocuments } from "../wrappers"; import { MessageRender } from "./messageRender"; import LazyLoadQueue from "../lazyLoadQueue"; import { AppChatsManager } from "../../lib/appManagers/appChatsManager"; @@ -1669,6 +1669,8 @@ export default class ChatBubbles { }).catch(reject); }, 0); }); + + //this.messagesQueuePromise.catch(() => {}); } public setBubblePosition(bubble: HTMLElement, message: any, reverse: boolean) { @@ -2033,7 +2035,8 @@ export default class ChatBubbles { isOut: our, lazyLoadQueue: this.lazyLoadQueue, chat: this.chat, - loadPromises + loadPromises, + noAutoDownload: this.chat.noAutoDownloadMedia, }); break; @@ -2049,7 +2052,8 @@ export default class ChatBubbles { isOut, lazyLoadQueue: this.lazyLoadQueue, middleware: this.getMiddleware(), - loadPromises + loadPromises, + noAutoDownload: this.chat.noAutoDownloadMedia, }); break; @@ -2098,12 +2102,14 @@ export default class ChatBubbles { middleware: this.getMiddleware(), isOut, group: CHAT_ANIMATION_GROUP, - loadPromises + loadPromises, + noAutoDownload: this.chat.noAutoDownloadMedia, }); //} } else { const docDiv = wrapDocument({ - message + message, + noAutoDownload: this.chat.noAutoDownloadMedia, }); preview.append(docDiv); preview.classList.add('preview-with-document'); @@ -2166,7 +2172,8 @@ export default class ChatBubbles { lazyLoadQueue: this.lazyLoadQueue, middleware: this.getMiddleware(), loadPromises, - withoutPreloader: isSquare + withoutPreloader: isSquare, + noAutoDownload: this.chat.noAutoDownloadMedia, }); } @@ -2233,7 +2240,8 @@ export default class ChatBubbles { isOut: our, lazyLoadQueue: this.lazyLoadQueue, chat: this.chat, - loadPromises + loadPromises, + noAutoDownload: this.chat.noAutoDownloadMedia, }); } else { const withTail = !isAndroid && !isApple && doc.type !== 'round' && canHaveTail && !withReplies && USE_MEDIA_TAILS; @@ -2249,7 +2257,8 @@ export default class ChatBubbles { lazyLoadQueue: this.lazyLoadQueue, middleware: this.getMiddleware(), group: CHAT_ANIMATION_GROUP, - loadPromises + loadPromises, + noAutoDownload: this.chat.noAutoDownloadMedia, }); } @@ -2261,7 +2270,8 @@ export default class ChatBubbles { bubble, messageDiv, chat: this.chat, - loadPromises + loadPromises, + noAutoDownload: this.chat.noAutoDownloadMedia }); if(newNameContainer) { diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index ed70b0a7..ba0c0d6d 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -29,6 +29,7 @@ import { renderImageFromUrl } from "../misc"; import SetTransition from "../singleTransition"; import { fastRaf } from "../../helpers/schedulers"; import AppPrivateSearchTab from "../sidebarRight/tabs/search"; +import type { State } from "../../lib/appManagers/appStateManager"; export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion' | 'scheduled'; @@ -54,6 +55,8 @@ export default class Chat extends EventListenerBase<{ public log: ReturnType; public type: ChatType = 'chat'; + + public noAutoDownloadMedia: boolean; constructor(public appImManager: AppImManager, public appChatsManager: AppChatsManager, public appDocsManager: AppDocsManager, public appInlineBotsManager: AppInlineBotsManager, public appMessagesManager: AppMessagesManager, public appPeersManager: AppPeersManager, public appPhotosManager: AppPhotosManager, public appProfileManager: AppProfileManager, public appStickersManager: AppStickersManager, public appUsersManager: AppUsersManager, public appWebPagesManager: AppWebPagesManager, public appPollsManager: AppPollsManager, public apiManager: ApiManagerProxy, public appDraftsManager: AppDraftsManager, public serverTimeManager: ServerTimeManager, public storage: typeof sessionStorage, public appNotificationsManager: AppNotificationsManager) { super(); @@ -210,6 +213,7 @@ export default class Chat extends EventListenerBase<{ appSidebarRight.sharedMediaTab.setPeer(peerId, this.threadId); this.input.clearHelper(); // костыль this.selection.cleanup(); // TODO: REFACTOR !!!!!! + this.setAutoDownloadMedia(); } this.peerChanged = samePeer; @@ -238,6 +242,25 @@ export default class Chat extends EventListenerBase<{ return result; } + public setAutoDownloadMedia() { + let type: keyof State['settings']['autoDownload']; + if(this.peerId < 0) { + if(this.appPeersManager.isBroadcast(this.peerId)) { + type = 'channels'; + } else { + type = 'groups'; + } + } else { + if(this.appUsersManager.isContact(this.peerId)) { + type = 'contacts'; + } else { + type = 'private'; + } + } + + this.noAutoDownloadMedia = !rootScope.settings.autoDownload[type]; + } + public setMessageId(messageId?: number) { return this.setPeer(this.peerId, messageId); } diff --git a/src/components/lazyLoadQueue.ts b/src/components/lazyLoadQueue.ts index a5d51c8a..4cd47418 100644 --- a/src/components/lazyLoadQueue.ts +++ b/src/components/lazyLoadQueue.ts @@ -81,7 +81,9 @@ export class LazyLoadQueueBase { //await item.load(item.div); await this.loadItem(item); } catch(err) { - this.log.error('loadMediaQueue error:', err/* , item */); + if(err !== 'NO_ENTRY_FOUND') { + this.log.error('loadMediaQueue error:', err/* , item */); + } } this.inProcess.delete(item); diff --git a/src/components/preloader.ts b/src/components/preloader.ts index 84cf84ab..24593ffb 100644 --- a/src/components/preloader.ts +++ b/src/components/preloader.ts @@ -22,7 +22,7 @@ export default class ProgressivePreloader { private tryAgainOnFail = true; private attachMethod: 'append' | 'prepend' = 'append'; - private loadFunc: () => {download: CancellablePromise}; + public loadFunc: () => {download: CancellablePromise}; private totalLength: number; diff --git a/src/components/sidebarLeft/tabs/generalSettings.ts b/src/components/sidebarLeft/tabs/generalSettings.ts index ade5d5f3..a0161d69 100644 --- a/src/components/sidebarLeft/tabs/generalSettings.ts +++ b/src/components/sidebarLeft/tabs/generalSettings.ts @@ -103,7 +103,7 @@ export default class AppGeneralSettingsTab extends SliderSuperTab { { const container = section('Auto-Download Media'); - container.classList.add('sidebar-left-section-disabled'); + //container.classList.add('sidebar-left-section-disabled'); const contactsCheckboxField = new CheckboxField({ text: 'Contacts', diff --git a/src/components/sidebarLeft/tabs/settings.ts b/src/components/sidebarLeft/tabs/settings.ts index a647e33d..008a05c0 100644 --- a/src/components/sidebarLeft/tabs/settings.ts +++ b/src/components/sidebarLeft/tabs/settings.ts @@ -9,6 +9,7 @@ import AppGeneralSettingsTab from "./generalSettings"; import AppEditProfileTab from "./editProfile"; import AppChatFoldersTab from "./chatFolders"; import AppNotificationsTab from "./notifications"; +import PeerTitle from "../../peerTitle"; //import AppMediaViewer from "../../appMediaViewerNew"; export default class AppSettingsTab extends SliderSuperTab { @@ -120,7 +121,7 @@ export default class AppSettingsTab extends SliderSuperTab { }); this.buttons.general.addEventListener('click', () => { - new AppGeneralSettingsTab(this.slider as any).open(); + new AppGeneralSettingsTab(this.slider).open(); }); this.buttons.notifications.addEventListener('click', () => { @@ -136,7 +137,7 @@ export default class AppSettingsTab extends SliderSuperTab { let user = appUsersManager.getSelf(); this.avatarElem.setAttribute('peer', '' + user.id); - this.nameDiv.innerHTML = user.rFullName || ''; + this.nameDiv.append(new PeerTitle({peerId: user.id}).element); this.phoneDiv.innerHTML = user.rPhone || ''; } diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index ed006908..ceb19087 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -5,13 +5,12 @@ import { deferredPromise } from '../helpers/cancellablePromise'; import { formatDateAccordingToToday, months } from '../helpers/date'; import mediaSizes from '../helpers/mediaSizes'; import { formatBytes } from '../helpers/number'; -import { isAppleMobile, isSafari } from '../helpers/userAgent'; +import { isSafari } from '../helpers/userAgent'; import { PhotoSize } from '../layer'; import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager"; import appMessagesManager from '../lib/appManagers/appMessagesManager'; import appPhotosManager, { MyPhoto } from '../lib/appManagers/appPhotosManager'; import LottieLoader from '../lib/lottieLoader'; -import VideoPlayer from '../lib/mediaPlayer'; import { attachClickEvent, cancelEvent, isInDOM } from "../helpers/dom"; import webpWorkerController from '../lib/webp/webpWorkerController'; import animationIntersector from './animationIntersector'; @@ -34,7 +33,7 @@ import { animateSingle } from '../helpers/animation'; const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB -export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton}: { +export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises, noPlayButton, noAutoDownload}: { doc: MyDocument, container?: HTMLElement, message?: any, @@ -49,7 +48,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai group?: string, onlyPreview?: boolean, withoutPreloader?: boolean, - loadPromises?: Promise[] + loadPromises?: Promise[], + noAutoDownload?: boolean, }) { const isAlbumItem = !(boxWidth && boxHeight); const canAutoplay = doc.type !== 'video' || (doc.size <= MAX_VIDEO_AUTOPLAY_SIZE && !isAlbumItem); @@ -94,7 +94,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai lazyLoadQueue, middleware, withoutPreloader, - loadPromises + loadPromises, + noAutoDownload }); res.thumb = photoRes; @@ -112,7 +113,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai video.setAttribute('playsinline', 'true'); video.muted = true; if(doc.type === 'round') { - const globalVideo = appMediaPlaybackController.addMedia(message.peerId, doc, message.mid) as HTMLVideoElement; + const globalVideo = appMediaPlaybackController.addMedia(message.peerId, doc, message.mid, !noAutoDownload) as HTMLVideoElement; const divRound = document.createElement('div'); divRound.classList.add('media-round', 'z-depth-1'); @@ -230,7 +231,8 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai lazyLoadQueue, middleware, withoutPreloader: true, - loadPromises + loadPromises, + noAutoDownload }); res.thumb = photoRes; @@ -259,85 +261,97 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai container.append(video); } - const loadVideo = async() => { - if(middleware && !middleware()) { - return; + let preloader: ProgressivePreloader; + if(message?.media?.preloader) { // means upload + preloader = message.media.preloader as ProgressivePreloader; + preloader.attach(container, false); + noAutoDownload = undefined; + } else if(!doc.downloaded && !doc.supportsStreaming) { + preloader = new ProgressivePreloader({ + attachMethod: 'prepend' + }); + } else if(doc.supportsStreaming) { + preloader = new ProgressivePreloader({ + cancelable: false, + attachMethod: 'prepend' + }); + } + + let f = noAutoDownload && photoRes?.preloader?.loadFunc; + const load = () => { + if(preloader && noAutoDownload && !withoutPreloader) { + preloader.construct(); + preloader.setManual(); } let loadPromise: Promise = Promise.resolve(); - let preloader: ProgressivePreloader; - if(message?.media?.preloader) { // means upload - preloader = message.media.preloader as ProgressivePreloader; - preloader.attach(container, false); - } else if(!doc.downloaded && !doc.supportsStreaming) { - const promise = loadPromise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId); - preloader = new ProgressivePreloader({ - attachMethod: 'prepend' - }); - preloader.attach(container, true, promise); - - //if(doc.type !== 'round') { - await promise; - - if(middleware && !middleware()) { - return; + if(preloader) { + if(!doc.downloaded && !doc.supportsStreaming) { + const promise = loadPromise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId, noAutoDownload); + preloader.attach(container, false, promise); + } else if(doc.supportsStreaming) { + if(noAutoDownload) { + loadPromise = Promise.reject(); + } else { + preloader.attach(container, false, null); + video.addEventListener(isSafari ? 'timeupdate' : 'canplay', () => { + preloader.detach(); + }, {once: true}); } - //} - } else if(doc.supportsStreaming) { - preloader = new ProgressivePreloader({ - cancelable: false, - attachMethod: 'prepend' - }); - preloader.attach(container, false, null); - video.addEventListener(isSafari ? 'timeupdate' : 'canplay', () => { - preloader.detach(); - }, {once: true}); + } } - /* if(doc.type === 'round') { - return; - } */ - - //console.log('loaded doc:', doc, doc.url, container); + if(!noAutoDownload && f) { + f(); + f = null; + } + + noAutoDownload = undefined; const deferred = deferredPromise(); + loadPromise.then(() => { + if(middleware && !middleware()) { + deferred.resolve(); + return; + } + + if(doc.type === 'round') { + appMediaPlaybackController.resolveWaitingForLoadMedia(message.peerId, message.mid); + } - //if(doc.type === 'gif'/* || true */) { onVideoLoad(video).then(() => { - /* if(!video.paused) { - video.pause(); - } */ if(group) { animationIntersector.addAnimation(video, group); } - - // test lazyLoadQueue - //setTimeout(() => { - deferred.resolve(); - //}, 5000); + + deferred.resolve(); }); - //} - - if(doc.type === 'video') { - video.addEventListener('timeupdate', () => { - spanTime.innerText = (video.duration - video.currentTime + '').toHHMMSS(false); + + if(doc.type === 'video') { + video.addEventListener('timeupdate', () => { + spanTime.innerText = (video.duration - video.currentTime + '').toHHMMSS(false); + }); + } + + video.addEventListener('error', (e) => { + deferred.resolve(); }); - } + + video.muted = true; + video.loop = true; + //video.play(); + video.autoplay = true; - video.addEventListener('error', (e) => { - deferred.resolve(); - }); + renderImageFromUrl(video, doc.url); + }, () => {}); - video.muted = true; - video.loop = true; - //video.play(); - video.autoplay = true; - - renderImageFromUrl(video, doc.url); - - return Promise.all([loadPromise, deferred]); + return {download: loadPromise, render: deferred}; }; + if(preloader) { + preloader.setDownloadFunction(load); + } + /* if(doc.size >= 20e6 && !doc.downloaded) { let downloadDiv = document.createElement('div'); downloadDiv.classList.add('download'); @@ -356,7 +370,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai return; } */ - res.loadPromise = !lazyLoadQueue ? loadVideo() : (lazyLoadQueue.push({div: container, load: loadVideo}), Promise.resolve()); + res.loadPromise = !lazyLoadQueue ? load().render : (lazyLoadQueue.push({div: container, load: () => load().render}), Promise.resolve()); return res; } @@ -375,14 +389,15 @@ export const formatDate = (timestamp: number, monthShort = false, withYear = tru return str + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2); }; -export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises}: { +export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises, noAutoDownload}: { message: any, withTime?: boolean, fontWeight?: number, voiceAsMusic?: boolean, showSender?: boolean, searchContext?: SearchSuperContext, - loadPromises?: Promise[] + loadPromises?: Promise[], + noAutoDownload?: boolean, }): HTMLElement { if(!fontWeight) fontWeight = 500; @@ -394,6 +409,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS audioElement.setAttribute('peer-id', '' + message.peerId); audioElement.withTime = withTime; audioElement.message = message; + audioElement.noAutoDownload = noAutoDownload; if(voiceAsMusic) audioElement.voiceAsMusic = voiceAsMusic; if(searchContext) audioElement.searchContext = searchContext; @@ -590,7 +606,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}: { +export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size, withoutPreloader, loadPromises, noAutoDownload}: { photo: MyPhoto | MyDocument, message: any, container: HTMLElement, @@ -602,7 +618,8 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT middleware?: () => boolean, size?: PhotoSize, withoutPreloader?: boolean, - loadPromises?: Promise[] + loadPromises?: Promise[], + noAutoDownload?: boolean, }) { if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) { if(boxWidth && boxHeight && photo._ === 'document') { @@ -617,7 +634,8 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT images: { thumb: null, full: null - } + }, + preloader: null }; } @@ -660,6 +678,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT if(message?.media?.preloader) { // means upload preloader = message.media.preloader; preloader.attach(container); + noAutoDownload = undefined; } else { preloader = new ProgressivePreloader({ attachMethod: 'prepend' @@ -669,7 +688,9 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT const getDownloadPromise = () => { const promise = photo._ === 'document' && photo.mime_type === 'image/gif' ? appDocsManager.downloadDoc(photo, /* undefined, */lazyLoadQueue?.queueId) : - appPhotosManager.preloadPhoto(photo, size, lazyLoadQueue?.queueId); + appPhotosManager.preloadPhoto(photo, size, lazyLoadQueue?.queueId, noAutoDownload); + + noAutoDownload = undefined; return promise; }; @@ -701,13 +722,20 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT let loadPromise: Promise; const load = () => { + if(noAutoDownload && !withoutPreloader) { + preloader.construct(); + preloader.setManual(); + } + const promise = getDownloadPromise(); if(!cacheContext.downloaded && !withoutPreloader && (size as PhotoSize.photoSize).w >= 150 && (size as PhotoSize.photoSize).h >= 150) { preloader.attach(container, false, promise); } - return {download: promise, render: promise.then(onLoad)}; + const renderPromise = promise.then(onLoad); + renderPromise.catch(() => {}); + return {download: promise, render: renderPromise}; }; preloader.setDownloadFunction(load); @@ -716,7 +744,11 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT loadThumbPromise = loadPromise = load().render; } else { if(!lazyLoadQueue) loadPromise = load().render; - else lazyLoadQueue.push({div: container, load: () => load().download}); + /* else if(noAutoDownload) { + preloader.construct(); + preloader.setManual(); + preloader.attach(container); + } */ else lazyLoadQueue.push({div: container, load: () => load().download}); } if(loadPromises && loadThumbPromise) { @@ -731,7 +763,8 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT images: { thumb: thumbImage, full: image - } + }, + preloader }; } @@ -1089,7 +1122,7 @@ export function prepareAlbum(options: { } */ } -export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut, chat, loadPromises}: { +export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut, chat, loadPromises, noAutoDownload}: { groupId: string, attachmentDiv: HTMLElement, middleware?: () => boolean, @@ -1097,7 +1130,8 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo uploading?: boolean, isOut: boolean, chat: Chat, - loadPromises?: Promise[] + loadPromises?: Promise[], + noAutoDownload?: boolean, }) { const items: {size: PhotoSize.photoSize, media: any, message: any}[] = []; @@ -1142,7 +1176,8 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo lazyLoadQueue, middleware, size, - loadPromises + loadPromises, + noAutoDownload }); } else { wrapVideo({ @@ -1155,20 +1190,22 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo isOut, lazyLoadQueue, middleware, - loadPromises + loadPromises, + noAutoDownload }); } }); } -export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat, loadPromises}: { +export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat, loadPromises, noAutoDownload}: { albumMustBeRenderedFull: boolean, message: any, messageDiv: HTMLElement, bubble: HTMLElement, uploading?: boolean, chat: Chat, - loadPromises?: Promise[] + loadPromises?: Promise[], + noAutoDownload?: boolean, }) { let nameContainer: HTMLElement; const mids = albumMustBeRenderedFull ? chat.getMidsByMid(message.mid) : [message.mid]; @@ -1180,7 +1217,8 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, const message = chat.getMessage(mid); const div = wrapDocument({ message, - loadPromises + loadPromises, + noAutoDownload }); const container = document.createElement('div'); diff --git a/src/lib/appManagers/appDocsManager.ts b/src/lib/appManagers/appDocsManager.ts index c408c282..c0857ef2 100644 --- a/src/lib/appManagers/appDocsManager.ts +++ b/src/lib/appManagers/appDocsManager.ts @@ -214,7 +214,7 @@ export class AppDocsManager { }; } - public getFileDownloadOptions(doc: MyDocument, thumb?: PhotoSize.photoSize, queueId?: number) { + public getFileDownloadOptions(doc: MyDocument, thumb?: PhotoSize.photoSize, queueId?: number, onlyCache?: boolean) { const inputFileLocation = this.getInput(doc, thumb?.type); let mimeType: string; @@ -230,7 +230,8 @@ export class AppDocsManager { size: thumb ? thumb.size : doc.size, mimeType: mimeType, fileName: doc.file_name, - queueId + queueId, + onlyCache }; } @@ -278,7 +279,7 @@ export class AppDocsManager { return getFileNameByLocation(this.getInput(doc, thumbSize), {fileName: doc.file_name}); } - public downloadDoc(doc: MyDocument, queueId?: number): DownloadBlob { + public downloadDoc(doc: MyDocument, queueId?: number, onlyCache?: boolean): DownloadBlob { const fileName = this.getInputFileName(doc); let download: DownloadBlob = appDownloadManager.getDownload(fileName); @@ -286,15 +287,15 @@ export class AppDocsManager { return download; } - const downloadOptions = this.getFileDownloadOptions(doc, undefined, queueId); + const downloadOptions = this.getFileDownloadOptions(doc, undefined, queueId, onlyCache); download = appDownloadManager.download(downloadOptions); const originalPromise = download; originalPromise.then((blob) => { doc.url = URL.createObjectURL(blob); doc.downloaded = true; - }); - + }, () => {}); + if(doc.type === 'voice' && !opusDecodeController.isPlaySupported()) { download = originalPromise.then(async(blob) => { const reader = new FileReader(); diff --git a/src/lib/appManagers/appDownloadManager.ts b/src/lib/appManagers/appDownloadManager.ts index b45ee91c..72d5e29c 100644 --- a/src/lib/appManagers/appDownloadManager.ts +++ b/src/lib/appManagers/appDownloadManager.ts @@ -56,8 +56,7 @@ export class AppDownloadManager { error.name = 'AbortError'; apiManager.cancelDownload(fileName); - this.clearDownload(fileName); - + deferred.reject(error); deferred.cancel = () => {}; /* } catch(err) { @@ -70,6 +69,10 @@ export class AppDownloadManager { delete this.progressCallbacks[fileName]; }); + deferred.catch(() => { + this.clearDownload(fileName); + }); + return this.downloads[fileName] = deferred; } @@ -121,11 +124,14 @@ export class AppDownloadManager { const tryDownload = (): Promise => { //return Promise.resolve(); - if(!apiManager.worker) { - return this.cacheStorage.getFile(fileName).then((blob) => { + if(!apiManager.worker || options.onlyCache) { + const promise = this.cacheStorage.getFile(fileName).then((blob) => { if(blob.size < options.size) throw 'wrong size'; else deferred.resolve(blob); - }).catch(() => { + }); + + if(options.onlyCache) return promise.catch(onError); + return promise.catch(() => { return apiManager.downloadFile(options).then(deferred.resolve, onError); }); } else { @@ -237,4 +243,4 @@ export class AppDownloadManager { } } -export default new AppDownloadManager(); \ No newline at end of file +export default new AppDownloadManager(); diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 0bec2707..93db8b94 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -161,7 +161,7 @@ export class AppImManager { }); this.setSettings(); - rootScope.on('settings_updated', () => this.setSettings()); + rootScope.on('settings_updated', this.setSettings); useHeavyAnimationCheck(() => { animationIntersector.setOnlyOnePlayableGroup('lock'); @@ -237,7 +237,7 @@ export class AppImManager { return sessionStorage.getFromCache('chatPositions')[key]; } */ - private setSettings() { + private setSettings = () => { document.documentElement.style.setProperty('--messages-text-size', rootScope.settings.messagesTextSize + 'px'); if(rootScope.settings.background.highlightningColor) { @@ -261,7 +261,11 @@ export class AppImManager { lottieLoader.setLoop(rootScope.settings.stickers.loop); animationIntersector.checkAnimations(false); - } + + for(const chat of this.chats) { + chat.setAutoDownloadMedia(); + } + }; // * не могу использовать тут TransitionSlider, так как мне нужен отрисованный блок рядом // * (или под текущим чатом) чтобы правильно отрендерить чат (напр. scrollTop) diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 48970c01..ca9c2f8a 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -6,7 +6,7 @@ import { createPosterForVideo } from "../../helpers/files"; import { copy, defineNotNumerableProperties, getObjectKeysAndSort } from "../../helpers/object"; import { randomLong } from "../../helpers/random"; import { splitStringByLength, limitSymbols } from "../../helpers/string"; -import { ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update } from "../../layer"; +import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update } from "../../layer"; import { InvokeApiOptions } from "../../types"; import { langPack } from "../langPack"; import { logger, LogLevels } from "../logger"; @@ -1759,6 +1759,10 @@ export class AppMessagesManager { // ! нужно передавать folderId, так как по папке !== 0 нет свойства folder_id this.saveConversation(dialog, dialog.folder_id ?? folderId); + if(dialog.peerId === undefined) { + return; + } + /* if(dialog.peerId === -1213511294) { this.log.error('lun bot', folderId, d); } */ @@ -2894,7 +2898,7 @@ export class AppMessagesManager { } /** - * Won't save migrated from peer + * Won't save migrated from peer, forbidden peers, left and kicked */ public saveConversation(dialog: Dialog, folderId = 0) { const peerId = appPeersManager.getPeerId(dialog.peer); @@ -2908,6 +2912,14 @@ export class AppMessagesManager { } const channelId = appPeersManager.isChannel(peerId) ? -peerId : 0; + + if(peerId < 0) { + const chat: Chat = appChatsManager.getChat(-peerId); + if(chat._ === 'channelForbidden' || chat._ === 'chatForbidden' || (chat as Chat.chat).pFlags.left || (chat as Chat.chat).pFlags.kicked) { + return false; + } + } + const peerText = appPeersManager.getPeerSearchText(peerId); searchIndexManager.indexObject(peerId, peerText, this.dialogsIndex); diff --git a/src/lib/appManagers/appPhotosManager.ts b/src/lib/appManagers/appPhotosManager.ts index 741d7515..4a50cfc8 100644 --- a/src/lib/appManagers/appPhotosManager.ts +++ b/src/lib/appManagers/appPhotosManager.ts @@ -256,7 +256,7 @@ export class AppPhotosManager { return null; } - public getPhotoDownloadOptions(photo: MyPhoto | MyDocument, photoSize: PhotoSize, queueId?: number) { + public getPhotoDownloadOptions(photo: MyPhoto | MyDocument, photoSize: PhotoSize, queueId?: number, onlyCache?: boolean) { const isMyDocument = photo._ === 'document'; if(!photoSize || photoSize._ === 'photoSizeEmpty') { @@ -274,7 +274,7 @@ export class AppPhotosManager { thumb_size: photoSize.type } : (photoSize as PhotoSize.photoSize).location; - return {dcId: photo.dc_id, location, size: isPhoto ? (photoSize as PhotoSize.photoSize).size : undefined, queueId}; + return {dcId: photo.dc_id, location, size: isPhoto ? (photoSize as PhotoSize.photoSize).size : undefined, queueId, onlyCache}; } /* public getPhotoURL(photo: MTPhoto | MTMyDocument, photoSize: MTPhotoSize) { @@ -297,7 +297,7 @@ export class AppPhotosManager { return isDownloaded; } - public preloadPhoto(photoId: any, photoSize?: PhotoSize, queueId?: number): CancellablePromise { + public preloadPhoto(photoId: any, photoSize?: PhotoSize, queueId?: number, onlyCache?: boolean): CancellablePromise { const photo = this.getPhoto(photoId); // @ts-ignore @@ -317,7 +317,7 @@ export class AppPhotosManager { return Promise.resolve() as any; } - const downloadOptions = this.getPhotoDownloadOptions(photo, photoSize, queueId); + const downloadOptions = this.getPhotoDownloadOptions(photo, photoSize, queueId, onlyCache); const fileName = getFileNameByLocation(downloadOptions.location); @@ -342,7 +342,7 @@ export class AppPhotosManager { (photoSize as any).url = url; return blob; - }); + }).catch(() => {}); return download; } diff --git a/src/lib/mtproto/apiFileManager.ts b/src/lib/mtproto/apiFileManager.ts index a8bf17c0..55014f6c 100644 --- a/src/lib/mtproto/apiFileManager.ts +++ b/src/lib/mtproto/apiFileManager.ts @@ -25,7 +25,8 @@ export type DownloadOptions = { fileName?: string, mimeType?: string, limitPart?: number, - queueId?: number + queueId?: number, + onlyCache?: boolean, }; type MyUploadFile = UploadFile.uploadFile;