forked from mystiq/hydrogen-web
Merge pull request #767 from vector-im/bwindels/download-media
Menu option to download attached image or video of event
This commit is contained in:
commit
ad8ad22cc1
3 changed files with 55 additions and 10 deletions
|
@ -27,6 +27,29 @@ export class BaseMediaTile extends BaseMessageTile {
|
||||||
this._decryptedFile = null;
|
this._decryptedFile = null;
|
||||||
this._isVisible = false;
|
this._isVisible = false;
|
||||||
this._error = null;
|
this._error = null;
|
||||||
|
this._downloading = false;
|
||||||
|
this._downloadError = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async downloadMedia() {
|
||||||
|
if (this._downloading || this.isPending) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const content = this._getContent();
|
||||||
|
const filename = content.body;
|
||||||
|
this._downloading = true;
|
||||||
|
this.emitChange("status");
|
||||||
|
let blob;
|
||||||
|
try {
|
||||||
|
blob = await this._mediaRepository.downloadAttachment(content);
|
||||||
|
this.platform.saveFileAs(blob, filename);
|
||||||
|
} catch (err) {
|
||||||
|
this._downloadError = err;
|
||||||
|
} finally {
|
||||||
|
blob?.dispose();
|
||||||
|
this._downloading = false;
|
||||||
|
}
|
||||||
|
this.emitChange("status");
|
||||||
}
|
}
|
||||||
|
|
||||||
get isUploading() {
|
get isUploading() {
|
||||||
|
@ -38,7 +61,7 @@ export class BaseMediaTile extends BaseMessageTile {
|
||||||
return pendingEvent && Math.round((pendingEvent.attachmentsSentBytes / pendingEvent.attachmentsTotalBytes) * 100);
|
return pendingEvent && Math.round((pendingEvent.attachmentsSentBytes / pendingEvent.attachmentsTotalBytes) * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
get sendStatus() {
|
get status() {
|
||||||
const {pendingEvent} = this._entry;
|
const {pendingEvent} = this._entry;
|
||||||
switch (pendingEvent?.status) {
|
switch (pendingEvent?.status) {
|
||||||
case SendStatus.Waiting:
|
case SendStatus.Waiting:
|
||||||
|
@ -53,6 +76,12 @@ export class BaseMediaTile extends BaseMessageTile {
|
||||||
case SendStatus.Error:
|
case SendStatus.Error:
|
||||||
return this.i18n`Error: ${pendingEvent.error.message}`;
|
return this.i18n`Error: ${pendingEvent.error.message}`;
|
||||||
default:
|
default:
|
||||||
|
if (this._downloadError) {
|
||||||
|
return `Download failed`;
|
||||||
|
}
|
||||||
|
if (this._downloading) {
|
||||||
|
return this.i18n`Downloading…`;
|
||||||
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,7 +233,7 @@ only loads when the top comes into view*/
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Timeline_messageBody .media > .sendStatus {
|
.Timeline_messageBody .media > .status {
|
||||||
align-self: end;
|
align-self: end;
|
||||||
justify-self: start;
|
justify-self: start;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
|
@ -251,7 +251,7 @@ only loads when the top comes into view*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.Timeline_messageBody .media > time,
|
.Timeline_messageBody .media > time,
|
||||||
.Timeline_messageBody .media > .sendStatus {
|
.Timeline_messageBody .media > .status {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
display: block;
|
display: block;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseMessageView} from "./BaseMessageView.js";
|
import {BaseMessageView} from "./BaseMessageView.js";
|
||||||
|
import {Menu} from "../../../general/Menu.js";
|
||||||
|
|
||||||
export class BaseMediaView extends BaseMessageView {
|
export class BaseMediaView extends BaseMessageView {
|
||||||
renderMessageBody(t, vm) {
|
renderMessageBody(t, vm) {
|
||||||
|
@ -35,24 +36,39 @@ export class BaseMediaView extends BaseMessageView {
|
||||||
this.renderMedia(t, vm),
|
this.renderMedia(t, vm),
|
||||||
t.time(vm.date + " " + vm.time),
|
t.time(vm.date + " " + vm.time),
|
||||||
];
|
];
|
||||||
|
const status = t.div({
|
||||||
|
className: {
|
||||||
|
status: true,
|
||||||
|
hidden: vm => !vm.status
|
||||||
|
},
|
||||||
|
}, vm => vm.status);
|
||||||
|
children.push(status);
|
||||||
if (vm.isPending) {
|
if (vm.isPending) {
|
||||||
const sendStatus = t.div({
|
|
||||||
className: {
|
|
||||||
sendStatus: true,
|
|
||||||
hidden: vm => !vm.sendStatus
|
|
||||||
},
|
|
||||||
}, vm => vm.sendStatus);
|
|
||||||
const progress = t.progress({
|
const progress = t.progress({
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
max: 100,
|
||||||
value: vm => vm.uploadPercentage,
|
value: vm => vm.uploadPercentage,
|
||||||
className: {hidden: vm => !vm.isUploading}
|
className: {hidden: vm => !vm.isUploading}
|
||||||
});
|
});
|
||||||
children.push(sendStatus, progress);
|
children.push(progress);
|
||||||
}
|
}
|
||||||
return t.div({className: "Timeline_messageBody"}, [
|
return t.div({className: "Timeline_messageBody"}, [
|
||||||
t.div({className: "media", style: `max-width: ${vm.width}px`}, children),
|
t.div({className: "media", style: `max-width: ${vm.width}px`}, children),
|
||||||
t.if(vm => vm.error, t => t.p({className: "error"}, vm.error))
|
t.if(vm => vm.error, t => t.p({className: "error"}, vm.error))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createMenuOptions(vm) {
|
||||||
|
const options = super.createMenuOptions(vm);
|
||||||
|
if (!vm.isPending) {
|
||||||
|
let label;
|
||||||
|
switch (vm.shape) {
|
||||||
|
case "image": label = vm.i18n`Download image`; break;
|
||||||
|
case "video": label = vm.i18n`Download video`; break;
|
||||||
|
default: label = vm.i18n`Download media`; break;
|
||||||
|
}
|
||||||
|
options.push(Menu.option(label, () => vm.downloadMedia()));
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue