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
This commit is contained in:
David Baker 2019-04-08 17:53:39 +01:00
parent 054011f5f8
commit 59210564b7
3 changed files with 30 additions and 14 deletions

View file

@ -34,6 +34,7 @@ import "blueimp-canvas-to-blob";
const MAX_WIDTH = 800; const MAX_WIDTH = 800;
const MAX_HEIGHT = 600; const MAX_HEIGHT = 600;
export class UploadCancelledError extends Error {}
/** /**
* Create a thumbnail for a image DOM element. * Create a thumbnail for a image DOM element.
@ -236,17 +237,25 @@ function uploadFile(matrixClient, roomId, file, progressHandler) {
if (matrixClient.isRoomEncrypted(roomId)) { if (matrixClient.isRoomEncrypted(roomId)) {
// If the room is encrypted then encrypt the file before uploading it. // If the room is encrypted then encrypt the file before uploading it.
// First read the file into memory. // 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. // Then encrypt the file.
return encrypt.encryptAttachment(data); return encrypt.encryptAttachment(data);
}).then(function(encryptResult) { }).then(function(encryptResult) {
if (cancelled) throw new UploadCancelledError();
// Record the information needed to decrypt the attachment. // 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. // Pass the encrypted data as a Blob to the uploader.
const blob = new Blob([encryptResult.data]); const blob = new Blob([encryptResult.data]);
return matrixClient.uploadContent(blob, { uploadPromise = matrixClient.uploadContent(blob, {
progressHandler: progressHandler, progressHandler: progressHandler,
includeFilename: false, includeFilename: false,
});
return uploadPromise;
}).then(function(url) { }).then(function(url) {
// If the attachment is encrypted then bundle the URL along // If the attachment is encrypted then bundle the URL along
// with the information needed to decrypt the attachment and // with the information needed to decrypt the attachment and
@ -257,7 +266,11 @@ function uploadFile(matrixClient, roomId, file, progressHandler) {
} }
return {"file": encryptInfo}; return {"file": encryptInfo};
}); });
}); prom.abort = () => {
cancelled = true;
if (uploadPromise) MatrixClientPeg.get().cancelUpload(uploadPromise);
};
return prom;
} else { } else {
const basePromise = matrixClient.uploadContent(file, { const basePromise = matrixClient.uploadContent(file, {
progressHandler: progressHandler, progressHandler: progressHandler,
@ -513,7 +526,7 @@ export default class ContentMessages {
} }
getCurrentUploads() { getCurrentUploads() {
return this.inprogress; return this.inprogress.filter(u => !u.canceled);
} }
cancelUpload(promise) { cancelUpload(promise) {
@ -529,6 +542,7 @@ export default class ContentMessages {
if (upload) { if (upload) {
upload.canceled = true; upload.canceled = true;
MatrixClientPeg.get().cancelUpload(upload.promise); MatrixClientPeg.get().cancelUpload(upload.promise);
dis.dispatch({action: 'upload_canceled', upload});
} }
} }
} }

View file

@ -543,6 +543,7 @@ module.exports = React.createClass({
case 'notifier_enabled': case 'notifier_enabled':
case 'upload_started': case 'upload_started':
case 'upload_finished': case 'upload_finished':
case 'upload_canceled':
this.forceUpdate(); this.forceUpdate();
break; break;
case 'call_state': case 'call_state':

View file

@ -16,7 +16,7 @@ limitations under the License.
const React = require('react'); const React = require('react');
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const ContentMessages = require('../../ContentMessages'); import ContentMessages from '../../ContentMessages';
const dis = require('../../dispatcher'); const dis = require('../../dispatcher');
const filesize = require('filesize'); const filesize = require('filesize');
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
@ -40,6 +40,7 @@ module.exports = React.createClass({displayName: 'UploadBar',
switch (payload.action) { switch (payload.action) {
case 'upload_progress': case 'upload_progress':
case 'upload_finished': case 'upload_finished':
case 'upload_canceled':
case 'upload_failed': case 'upload_failed':
if (this.mounted) this.forceUpdate(); if (this.mounted) this.forceUpdate();
break; break;