diff --git a/src/api/gramjs/worker/connector.ts b/src/api/gramjs/worker/connector.ts index 0ba1a9d90..0fc0bab56 100644 --- a/src/api/gramjs/worker/connector.ts +++ b/src/api/gramjs/worker/connector.ts @@ -5,7 +5,7 @@ import type { LocalDb } from '../localDb'; import type { MethodArgs, MethodResponse, Methods } from '../methods/types'; import type { OriginPayload, ThenArg, WorkerMessageEvent } from './types'; -import { DATA_BROADCAST_CHANNEL_NAME, DEBUG } from '../../../config'; +import { DATA_BROADCAST_CHANNEL_NAME, DEBUG, IGNORE_UNHANDLED_ERRORS } from '../../../config'; import { logDebugMessage } from '../../../util/debugConsole'; import Deferred from '../../../util/Deferred'; import { getCurrentTabId, subscribeToMasterChange } from '../../../util/establishMultitabRole'; @@ -295,7 +295,9 @@ function subscribeToWorker(onUpdate: OnApiUpdate) { } else if (payload.type === 'methodCallback') { handleMethodCallback(payload); } else if (payload.type === 'unhandledError') { - throw new Error(payload.error?.message); + const message = payload.error?.message; + if (message && IGNORE_UNHANDLED_ERRORS.has(message)) return; + throw new Error(message); } else if (payload.type === 'sendBeacon') { navigator.sendBeacon(payload.url, payload.data); } else if (payload.type === 'debugLog') { diff --git a/src/components/common/Audio.tsx b/src/components/common/Audio.tsx index f4ca5274c..b8b7a8add 100644 --- a/src/components/common/Audio.tsx +++ b/src/components/common/Audio.tsx @@ -248,7 +248,7 @@ const Audio: FC = ({ if (isDownloading) { cancelMediaDownload({ media }); } else { - downloadMedia({ media }); + downloadMedia({ media, originMessage: message }); } }); diff --git a/src/components/common/Document.tsx b/src/components/common/Document.tsx index ec657e1a1..adef485cf 100644 --- a/src/components/common/Document.tsx +++ b/src/components/common/Document.tsx @@ -47,7 +47,7 @@ type OwnProps = { message: ApiMessage; onDateClick: (arg: ApiMessage) => void; } | { - message?: never; + message?: ApiMessage; onDateClick?: never; }); @@ -123,7 +123,7 @@ const Document = ({ const withMediaViewer = onMediaClick && document.innerMediaType; const handleDownload = useLastCallback(() => { - downloadMedia({ media: document }); + downloadMedia({ media: document, originMessage: message }); }); const handleClick = useLastCallback(() => { diff --git a/src/components/main/DownloadManager.tsx b/src/components/main/DownloadManager.tsx index a250292d9..20e5e9740 100644 --- a/src/components/main/DownloadManager.tsx +++ b/src/components/main/DownloadManager.tsx @@ -1,12 +1,13 @@ import type { FC } from '../../lib/teact/teact'; import { memo, useEffect } from '../../lib/teact/teact'; -import { getActions, withGlobal } from '../../global'; +import { getActions, getGlobal, withGlobal } from '../../global'; import type { TabState } from '../../global/types'; import { ApiMediaFormat } from '../../api/types'; import { selectTabState } from '../../global/selectors'; import download from '../../util/download'; +import generateUniqueId from '../../util/generateUniqueId'; import * as mediaLoader from '../../util/mediaLoader'; import { IS_OPFS_SUPPORTED, IS_SERVICE_WORKER_SUPPORTED, MAX_BUFFER_SIZE } from '../../util/windowEnvironment'; @@ -69,7 +70,14 @@ const DownloadManager: FC = ({ return; } - mediaLoader.fetch(mediaHash, mediaFormat, true).then((result) => { + const handleProgress = () => { + const currentDownloads = selectTabState(getGlobal()).activeDownloads; + if (!currentDownloads[mediaHash]) { + mediaLoader.cancelProgress(handleProgress); + } + }; + + mediaLoader.fetch(mediaHash, mediaFormat, true, handleProgress, generateUniqueId()).then((result) => { if (mediaFormat === ApiMediaFormat.DownloadUrl) { const url = new URL(result, window.document.baseURI); url.searchParams.set('filename', encodeURIComponent(filename)); diff --git a/src/components/mediaViewer/MediaViewerActions.tsx b/src/components/mediaViewer/MediaViewerActions.tsx index f28a1f3b5..973bd2f43 100644 --- a/src/components/mediaViewer/MediaViewerActions.tsx +++ b/src/components/mediaViewer/MediaViewerActions.tsx @@ -110,7 +110,8 @@ const MediaViewerActions: FC = ({ if (isDownloading) { cancelMediaDownload({ media }); } else { - downloadMedia({ media }); + const message = item?.type === 'message' ? item.message : undefined; + downloadMedia({ media, originMessage: message }); } }); diff --git a/src/components/middle/message/ContextMenuContainer.tsx b/src/components/middle/message/ContextMenuContainer.tsx index dcc34320a..2d7c1677f 100644 --- a/src/components/middle/message/ContextMenuContainer.tsx +++ b/src/components/middle/message/ContextMenuContainer.tsx @@ -505,7 +505,7 @@ const ContextMenuContainer: FC = ({ if (isDownloading) { cancelMediaDownload({ media: downloadableMedia }); } else { - downloadMedia({ media: downloadableMedia }); + downloadMedia({ media: downloadableMedia, originMessage: msg }); } }); closeMenu(); diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index f2428fd32..feda1727f 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -1186,6 +1186,7 @@ const Message: FC = ({ {document && ( = ({ {!inPreview && document && ( { - const { media, tabId = getCurrentTabId() } = payload; + const { media, originMessage, tabId = getCurrentTabId() } = payload; const hash = getMediaHash(media, 'download'); if (!hash) return undefined; @@ -637,6 +637,8 @@ addActionHandler('downloadMedia', (global, actions, payload): ActionReturnType = size, format: getMediaFormat(media, 'download'), filename: getMediaFilename(media), + originChatId: originMessage?.chatId, + originMessageId: originMessage?.id, } satisfies ActiveDownloads[string]; return addActiveMediaDownload(global, hash, metadata, tabId); @@ -659,7 +661,7 @@ addActionHandler('downloadSelectedMessages', (global, actions, payload): ActionR messages.forEach((message) => { const media = getMessageDownloadableMedia(message); if (!media) return; - actions.downloadMedia({ media, tabId }); + actions.downloadMedia({ media, originMessage: message, tabId }); }); }); diff --git a/src/global/reducers/messages.ts b/src/global/reducers/messages.ts index a45bc24aa..2ca529b1b 100644 --- a/src/global/reducers/messages.ts +++ b/src/global/reducers/messages.ts @@ -354,6 +354,17 @@ export function deleteChatMessages( } Object.values(global.byTabId).forEach(({ id: tabId }) => { + const tabState = selectTabState(global, tabId); + const activeDownloadsInChat = Object.entries(tabState.activeDownloads).filter( + ([, { originChatId, originMessageId }]) => originChatId === chatId && originMessageId, + ); + + activeDownloadsInChat.forEach(([mediaHash, context]) => { + if (messageIds.includes(context.originMessageId!)) { + global = cancelMessageMediaDownload(global, [mediaHash], tabId); + } + }); + mediaIdsToRemove.forEach((mediaId) => { global = removeIdFromSearchResults(global, chatId, threadId, mediaId, tabId); }); diff --git a/src/global/types.ts b/src/global/types.ts index 6c934e525..991c2d761 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -162,6 +162,8 @@ export type ActiveDownloads = Record; export type IDimensions = { @@ -2632,6 +2634,7 @@ export interface ActionPayloads { downloadSelectedMessages: WithTabId | undefined; downloadMedia: { media: DownloadableMedia; + originMessage?: ApiMessage; } & WithTabId; cancelMediaDownload: { media: DownloadableMedia; diff --git a/src/util/handleError.ts b/src/util/handleError.ts index c1d8c660a..8d99b2dca 100644 --- a/src/util/handleError.ts +++ b/src/util/handleError.ts @@ -50,6 +50,7 @@ function handleErrorEvent(e: ErrorEvent | PromiseRejectionEvent) { if (e instanceof ErrorEvent && e.message === 'ResizeObserver loop limit exceeded') { return; } + e.preventDefault(); handleError(e instanceof ErrorEvent ? (e.error || e.message) : e.reason); }