From 83cbe78cd65b0128b5b7c105a196e2012db2f27a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 16 Nov 2020 10:45:46 +0100 Subject: [PATCH] report attachment upload progress --- src/matrix/net/HomeServerApi.js | 1 + src/matrix/room/AttachmentUpload.js | 11 ++++++++++- src/platform/web/dom/request/fetch.js | 8 +++++++- src/platform/web/dom/request/xhr.js | 21 +++++++++++++-------- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/matrix/net/HomeServerApi.js b/src/matrix/net/HomeServerApi.js index bdf35363..935c6889 100644 --- a/src/matrix/net/HomeServerApi.js +++ b/src/matrix/net/HomeServerApi.js @@ -110,6 +110,7 @@ export class HomeServerApi { headers, body: encodedBody, timeout: options?.timeout, + uploadProgress: options?.uploadProgress, format: "json" // response format }); diff --git a/src/matrix/room/AttachmentUpload.js b/src/matrix/room/AttachmentUpload.js index 75a5bfb1..bccbbb67 100644 --- a/src/matrix/room/AttachmentUpload.js +++ b/src/matrix/room/AttachmentUpload.js @@ -35,12 +35,17 @@ export class AttachmentUpload { this._aborted = false; this._error = null; this._status = new ObservableValue(UploadStatus.Waiting); + this._progress = new ObservableValue(0); } get status() { return this._status; } + get uploadProgress() { + return this._progress; + } + async upload() { if (this._status.get() === UploadStatus.Waiting) { this._upload(); @@ -66,9 +71,13 @@ export class AttachmentUpload { if (this._aborted) { throw new AbortError("upload aborted during encryption"); } + this._progress.set(0); this._status.set(UploadStatus.Uploading); - this._uploadRequest = this._hsApi.uploadAttachment(transferredBlob, this._filename); + this._uploadRequest = this._hsApi.uploadAttachment(transferredBlob, this._filename, { + uploadProgress: sentBytes => this._progress.set(sentBytes / transferredBlob.size) + }); const {content_uri} = await this._uploadRequest.response(); + this._progress.set(1); this._mxcUrl = content_uri; this._transferredBlob = transferredBlob; this._status.set(UploadStatus.Uploaded); diff --git a/src/platform/web/dom/request/fetch.js b/src/platform/web/dom/request/fetch.js index 96c9ff9c..dd3b7949 100644 --- a/src/platform/web/dom/request/fetch.js +++ b/src/platform/web/dom/request/fetch.js @@ -21,6 +21,7 @@ import { } from "../../../../matrix/error.js"; import {abortOnTimeout} from "./timeout.js"; import {addCacheBuster} from "./common.js"; +import {xhrRequest} from "./xhr.js"; class RequestResult { constructor(promise, controller) { @@ -51,7 +52,12 @@ class RequestResult { } export function createFetchRequest(createTimeout) { - return function fetchRequest(url, {method, headers, body, timeout, format, cache = false}) { + return function fetchRequest(url, requestOptions) { + // fetch doesn't do upload progress yet, delegate to xhr + if (requestOptions?.uploadProgress) { + return xhrRequest(url, requestOptions); + } + let {method, headers, body, timeout, format, cache = false} = requestOptions; const controller = typeof AbortController === "function" ? new AbortController() : null; // if a BlobHandle, take native blob if (body?.nativeBlob) { diff --git a/src/platform/web/dom/request/xhr.js b/src/platform/web/dom/request/xhr.js index 5ca2d460..e2b9c15b 100644 --- a/src/platform/web/dom/request/xhr.js +++ b/src/platform/web/dom/request/xhr.js @@ -35,7 +35,7 @@ class RequestResult { } } -function send(url, {method, headers, timeout, body, format}) { +function createXhr(url, {method, headers, timeout, format, uploadProgress}) { const xhr = new XMLHttpRequest(); xhr.open(method, url); @@ -52,11 +52,9 @@ function send(url, {method, headers, timeout, body, format}) { xhr.timeout = timeout; } - // if a BlobHandle, take native blob - if (body?.nativeBlob) { - body = body.nativeBlob; + if (uploadProgress) { + xhr.upload.addEventListener("progress", evt => uploadProgress(evt.loaded)); } - xhr.send(body || null); return xhr; } @@ -71,12 +69,12 @@ function xhrAsPromise(xhr, method, url) { } export function xhrRequest(url, options) { - const {cache, format} = options; + let {cache, format, body, method} = options; if (!cache) { url = addCacheBuster(url); } - const xhr = send(url, options); - const promise = xhrAsPromise(xhr, options.method, url).then(xhr => { + const xhr = createXhr(url, options); + const promise = xhrAsPromise(xhr, method, url).then(xhr => { const {status} = xhr; let body = null; if (format === "buffer") { @@ -86,5 +84,12 @@ export function xhrRequest(url, options) { } return {status, body}; }); + + // if a BlobHandle, take native blob + if (body?.nativeBlob) { + body = body.nativeBlob; + } + xhr.send(body || null); + return new RequestResult(promise, xhr); }