2020-11-11 10:46:20 +01:00
|
|
|
/*
|
|
|
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {encryptAttachment} from "../e2ee/attachment.js";
|
2020-11-13 17:19:19 +01:00
|
|
|
import {createEnum} from "../../utils/enum.js";
|
|
|
|
import {ObservableValue} from "../../observable/ObservableValue.js";
|
|
|
|
import {AbortError} from "../../utils/error.js";
|
|
|
|
|
|
|
|
export const UploadStatus = createEnum("Waiting", "Encrypting", "Uploading", "Uploaded", "Error");
|
2020-11-11 10:46:20 +01:00
|
|
|
|
|
|
|
export class AttachmentUpload {
|
|
|
|
constructor({filename, blob, hsApi, platform, isEncrypted}) {
|
|
|
|
this._filename = filename;
|
|
|
|
this._unencryptedBlob = blob;
|
|
|
|
this._isEncrypted = isEncrypted;
|
|
|
|
this._platform = platform;
|
|
|
|
this._hsApi = hsApi;
|
|
|
|
this._mxcUrl = null;
|
|
|
|
this._transferredBlob = null;
|
|
|
|
this._encryptionInfo = null;
|
|
|
|
this._uploadRequest = null;
|
|
|
|
this._aborted = false;
|
2020-11-11 11:51:11 +01:00
|
|
|
this._error = null;
|
2020-11-13 17:19:19 +01:00
|
|
|
this._status = new ObservableValue(UploadStatus.Waiting);
|
2020-11-16 10:45:46 +01:00
|
|
|
this._progress = new ObservableValue(0);
|
2020-11-13 17:19:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
get status() {
|
|
|
|
return this._status;
|
2020-11-11 10:46:20 +01:00
|
|
|
}
|
|
|
|
|
2020-11-16 10:45:46 +01:00
|
|
|
get uploadProgress() {
|
|
|
|
return this._progress;
|
|
|
|
}
|
|
|
|
|
2020-11-13 17:19:19 +01:00
|
|
|
async upload() {
|
|
|
|
if (this._status.get() === UploadStatus.Waiting) {
|
|
|
|
this._upload();
|
|
|
|
}
|
2020-11-13 19:10:18 +01:00
|
|
|
await this._status.waitFor(s => {
|
|
|
|
return s === UploadStatus.Error || s === UploadStatus.Uploaded;
|
|
|
|
}).promise;
|
2020-11-13 17:19:19 +01:00
|
|
|
if (this._status.get() === UploadStatus.Error) {
|
|
|
|
throw this._error;
|
2020-11-11 10:46:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-13 17:19:19 +01:00
|
|
|
/** @package */
|
2020-11-11 10:46:20 +01:00
|
|
|
async _upload() {
|
2020-11-11 11:51:11 +01:00
|
|
|
try {
|
|
|
|
let transferredBlob = this._unencryptedBlob;
|
|
|
|
if (this._isEncrypted) {
|
2020-11-13 17:19:19 +01:00
|
|
|
this._status.set(UploadStatus.Encrypting);
|
2020-11-11 11:51:11 +01:00
|
|
|
const {info, blob} = await encryptAttachment(this._platform, this._unencryptedBlob);
|
|
|
|
transferredBlob = blob;
|
|
|
|
this._encryptionInfo = info;
|
|
|
|
}
|
|
|
|
if (this._aborted) {
|
2020-11-13 17:19:19 +01:00
|
|
|
throw new AbortError("upload aborted during encryption");
|
2020-11-11 11:51:11 +01:00
|
|
|
}
|
2020-11-16 10:45:46 +01:00
|
|
|
this._progress.set(0);
|
2020-11-13 17:19:19 +01:00
|
|
|
this._status.set(UploadStatus.Uploading);
|
2020-11-16 10:45:46 +01:00
|
|
|
this._uploadRequest = this._hsApi.uploadAttachment(transferredBlob, this._filename, {
|
|
|
|
uploadProgress: sentBytes => this._progress.set(sentBytes / transferredBlob.size)
|
|
|
|
});
|
2020-11-11 11:51:11 +01:00
|
|
|
const {content_uri} = await this._uploadRequest.response();
|
2020-11-16 10:45:46 +01:00
|
|
|
this._progress.set(1);
|
2020-11-11 11:51:11 +01:00
|
|
|
this._mxcUrl = content_uri;
|
|
|
|
this._transferredBlob = transferredBlob;
|
2020-11-13 17:19:19 +01:00
|
|
|
this._status.set(UploadStatus.Uploaded);
|
2020-11-11 11:51:11 +01:00
|
|
|
} catch (err) {
|
|
|
|
this._error = err;
|
2020-11-13 17:19:19 +01:00
|
|
|
this._status.set(UploadStatus.Error);
|
2020-11-11 10:46:20 +01:00
|
|
|
}
|
2020-11-11 11:51:11 +01:00
|
|
|
}
|
|
|
|
|
2020-11-11 10:46:20 +01:00
|
|
|
/** @public */
|
|
|
|
abort() {
|
|
|
|
this._aborted = true;
|
|
|
|
this._uploadRequest?.abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
get localPreview() {
|
|
|
|
return this._unencryptedBlob;
|
|
|
|
}
|
|
|
|
|
2020-11-11 11:51:11 +01:00
|
|
|
get error() {
|
|
|
|
return this._error;
|
|
|
|
}
|
|
|
|
|
2020-11-11 10:46:20 +01:00
|
|
|
/** @package */
|
2020-11-13 17:19:19 +01:00
|
|
|
applyToContent(urlPath, content) {
|
2020-11-11 10:46:20 +01:00
|
|
|
if (!this._mxcUrl) {
|
|
|
|
throw new Error("upload has not finished");
|
|
|
|
}
|
2020-11-13 17:19:19 +01:00
|
|
|
let prefix = urlPath.substr(0, urlPath.lastIndexOf("url"));
|
|
|
|
setPath(`${prefix}info.size`, content, this._transferredBlob.size);
|
2020-11-13 19:22:06 +01:00
|
|
|
setPath(`${prefix}info.mimetype`, content, this._unencryptedBlob.mimeType);
|
2020-11-11 10:46:20 +01:00
|
|
|
if (this._isEncrypted) {
|
2020-11-13 17:19:19 +01:00
|
|
|
setPath(`${prefix}file`, content, Object.assign(this._encryptionInfo, {
|
2020-11-13 19:22:06 +01:00
|
|
|
mimetype: this._unencryptedBlob.mimeType,
|
2020-11-11 10:46:20 +01:00
|
|
|
url: this._mxcUrl
|
2020-11-13 17:19:19 +01:00
|
|
|
}));
|
2020-11-11 10:46:20 +01:00
|
|
|
} else {
|
2020-11-13 17:19:19 +01:00
|
|
|
setPath(`${prefix}url`, content, this._mxcUrl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function setPath(path, content, value) {
|
|
|
|
const parts = path.split(".");
|
|
|
|
let obj = content;
|
|
|
|
for (let i = 0; i < (parts.length - 1); i += 1) {
|
|
|
|
const key = parts[i];
|
|
|
|
if (!obj[key]) {
|
|
|
|
obj[key] = {};
|
2020-11-11 10:46:20 +01:00
|
|
|
}
|
2020-11-13 17:19:19 +01:00
|
|
|
obj = obj[key];
|
2020-11-11 10:46:20 +01:00
|
|
|
}
|
2020-11-13 17:19:19 +01:00
|
|
|
const propKey = parts[parts.length - 1];
|
|
|
|
obj[propKey] = value;
|
2020-11-11 10:46:20 +01:00
|
|
|
}
|