Handle media download errors better (#12848)

* Handle media download errors better

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add test

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Show error if media download failed

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* More tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2024-07-31 14:07:59 +00:00
committed by GitHub
parent b55653ddf0
commit f3ac6692da
6 changed files with 140 additions and 8 deletions
@@ -24,6 +24,8 @@ import { RovingAccessibleButton } from "../../../accessibility/RovingTabIndex";
import Spinner from "../elements/Spinner";
import { _t, _td, TranslationKey } from "../../../languageHandler";
import { FileDownloader } from "../../../utils/FileDownloader";
import Modal from "../../../Modal";
import ErrorDialog from "../dialogs/ErrorDialog";
interface IProps {
mxEvent: MatrixEvent;
@@ -53,6 +55,23 @@ export default class DownloadActionButton extends React.PureComponent<IProps, IS
}
private onDownloadClick = async (): Promise<void> => {
try {
await this.doDownload();
} catch (e) {
Modal.createDialog(ErrorDialog, {
title: _t("timeline|download_failed"),
description: (
<>
<div>{_t("timeline|download_failed_description")}</div>
<div>{e instanceof Error ? e.toString() : ""}</div>
</>
),
});
this.setState({ loading: false });
}
};
private async doDownload(): Promise<void> {
const mediaEventHelper = this.props.mediaEventHelperGet();
if (this.state.loading || !mediaEventHelper) return;
@@ -64,15 +83,15 @@ export default class DownloadActionButton extends React.PureComponent<IProps, IS
if (this.state.blob) {
// Cheat and trigger a download, again.
return this.doDownload(this.state.blob);
return this.downloadBlob(this.state.blob);
}
const blob = await mediaEventHelper.sourceBlob.value;
this.setState({ blob });
await this.doDownload(blob);
};
await this.downloadBlob(blob);
}
private async doDownload(blob: Blob): Promise<void> {
private async downloadBlob(blob: Blob): Promise<void> {
await this.downloader.download({
blob,
name: this.props.mediaEventHelperGet()!.fileName,
+7 -3
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { MatrixClient, ResizeMethod } from "matrix-js-sdk/src/matrix";
import { MatrixClient, parseErrorResponse, ResizeMethod } from "matrix-js-sdk/src/matrix";
import { MediaEventContent } from "matrix-js-sdk/src/types";
import { Optional } from "matrix-events-sdk";
@@ -144,12 +144,16 @@ export class Media {
* Downloads the source media.
* @returns {Promise<Response>} Resolves to the server's response for chaining.
*/
public downloadSource(): Promise<Response> {
public async downloadSource(): Promise<Response> {
const src = this.srcHttp;
if (!src) {
throw new UserFriendlyError("error|download_media");
}
return fetch(src);
const res = await fetch(src);
if (!res.ok) {
throw parseErrorResponse(res, await res.text());
}
return res;
}
}
+2
View File
@@ -3264,6 +3264,8 @@
"disambiguated_profile": "%(displayName)s (%(matrixId)s)",
"download_action_decrypting": "Decrypting",
"download_action_downloading": "Downloading",
"download_failed": "Download failed",
"download_failed_description": "An error occurred while downloading this file",
"edits": {
"tooltip_label": "Edited at %(date)s. Click to view edits.",
"tooltip_sub": "Click to view edits",