From 59210564b7a1883d346f063fd4fb24d22b7c88ab Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 8 Apr 2019 17:53:39 +0100 Subject: [PATCH] Fix upload cancel in e2e rooms This is a bit of a mess of passing promises around - we weren't taking the right promise to pass to cancelUpload. Also e2e uploads take time to read into memory & encrypt, so allow cancelling them during those phases too, even though we can't abort those phases before they're done - we do mark the upload as cancelled though so filter the current uploads for cancelled ones. Fixes https://github.com/vector-im/riot-web/issues/4891 --- src/ContentMessages.js | 40 +++++++++++++++++--------- src/components/structures/RoomView.js | 1 + src/components/structures/UploadBar.js | 3 +- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/ContentMessages.js b/src/ContentMessages.js index f4aafbeb31..cefa91767d 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -34,6 +34,7 @@ import "blueimp-canvas-to-blob"; const MAX_WIDTH = 800; const MAX_HEIGHT = 600; +export class UploadCancelledError extends Error {} /** * Create a thumbnail for a image DOM element. @@ -236,28 +237,40 @@ function uploadFile(matrixClient, roomId, file, progressHandler) { if (matrixClient.isRoomEncrypted(roomId)) { // If the room is encrypted then encrypt the file before uploading it. // First read the file into memory. - return readFileAsArrayBuffer(file).then(function(data) { + let cancelled = false; + let uploadPromise; + let encryptInfo; + const prom = readFileAsArrayBuffer(file).then(function(data) { + if (cancelled) throw new UploadCancelledError(); // Then encrypt the file. return encrypt.encryptAttachment(data); }).then(function(encryptResult) { + if (cancelled) throw new UploadCancelledError(); // Record the information needed to decrypt the attachment. - const encryptInfo = encryptResult.info; + encryptInfo = encryptResult.info; // Pass the encrypted data as a Blob to the uploader. const blob = new Blob([encryptResult.data]); - return matrixClient.uploadContent(blob, { + uploadPromise = matrixClient.uploadContent(blob, { progressHandler: progressHandler, includeFilename: false, - }).then(function(url) { - // If the attachment is encrypted then bundle the URL along - // with the information needed to decrypt the attachment and - // add it under a file key. - encryptInfo.url = url; - if (file.type) { - encryptInfo.mimetype = file.type; - } - return {"file": encryptInfo}; }); + + return uploadPromise; + }).then(function(url) { + // If the attachment is encrypted then bundle the URL along + // with the information needed to decrypt the attachment and + // add it under a file key. + encryptInfo.url = url; + if (file.type) { + encryptInfo.mimetype = file.type; + } + return {"file": encryptInfo}; }); + prom.abort = () => { + cancelled = true; + if (uploadPromise) MatrixClientPeg.get().cancelUpload(uploadPromise); + }; + return prom; } else { const basePromise = matrixClient.uploadContent(file, { progressHandler: progressHandler, @@ -513,7 +526,7 @@ export default class ContentMessages { } getCurrentUploads() { - return this.inprogress; + return this.inprogress.filter(u => !u.canceled); } cancelUpload(promise) { @@ -529,6 +542,7 @@ export default class ContentMessages { if (upload) { upload.canceled = true; MatrixClientPeg.get().cancelUpload(upload.promise); + dis.dispatch({action: 'upload_canceled', upload}); } } } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 4f20b354d3..92775f75b3 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -543,6 +543,7 @@ module.exports = React.createClass({ case 'notifier_enabled': case 'upload_started': case 'upload_finished': + case 'upload_canceled': this.forceUpdate(); break; case 'call_state': diff --git a/src/components/structures/UploadBar.js b/src/components/structures/UploadBar.js index a8cb3c0b0e..6f26f0af35 100644 --- a/src/components/structures/UploadBar.js +++ b/src/components/structures/UploadBar.js @@ -16,7 +16,7 @@ limitations under the License. const React = require('react'); import PropTypes from 'prop-types'; -const ContentMessages = require('../../ContentMessages'); +import ContentMessages from '../../ContentMessages'; const dis = require('../../dispatcher'); const filesize = require('filesize'); import { _t } from '../../languageHandler'; @@ -40,6 +40,7 @@ module.exports = React.createClass({displayName: 'UploadBar', switch (payload.action) { case 'upload_progress': case 'upload_finished': + case 'upload_canceled': case 'upload_failed': if (this.mounted) this.forceUpdate(); break;