diff --git a/src/domain/session/room/timeline/tiles/MissingAttachmentTile.js b/src/domain/session/room/timeline/tiles/MissingAttachmentTile.js new file mode 100644 index 00000000..0a9b5976 --- /dev/null +++ b/src/domain/session/room/timeline/tiles/MissingAttachmentTile.js @@ -0,0 +1,33 @@ +/* +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 {MessageTile} from "./MessageTile.js"; + +export class MissingAttachmentTile extends MessageTile { + get shape() { + return "missing-attachment" + } + + get label() { + const name = this._getContent().body; + const msgtype = this._getContent().msgtype; + if (msgtype === "m.image") { + return this.i18n`The image ${name} wasn't fully sent previously and could not be recovered.`; + } else { + return this.i18n`The file ${name} wasn't fully sent previously and could not be recovered.`; + } + } +} diff --git a/src/domain/session/room/timeline/tilesCreator.js b/src/domain/session/room/timeline/tilesCreator.js index 9ac27a54..1901efe2 100644 --- a/src/domain/session/room/timeline/tilesCreator.js +++ b/src/domain/session/room/timeline/tilesCreator.js @@ -23,12 +23,15 @@ import {RoomNameTile} from "./tiles/RoomNameTile.js"; import {RoomMemberTile} from "./tiles/RoomMemberTile.js"; import {EncryptedEventTile} from "./tiles/EncryptedEventTile.js"; import {EncryptionEnabledTile} from "./tiles/EncryptionEnabledTile.js"; +import {MissingAttachmentTile} from "./tiles/MissingAttachmentTile.js"; export function tilesCreator(baseOptions) { return function tilesCreator(entry, emitUpdate) { const options = Object.assign({entry, emitUpdate}, baseOptions); if (entry.isGap) { return new GapTile(options); + } else if (entry.isPending && entry.pendingEvent.isMissingAttachments) { + return new MissingAttachmentTile(options); } else if (entry.eventType) { switch (entry.eventType) { case "m.room.message": { diff --git a/src/matrix/room/sending/PendingEvent.js b/src/matrix/room/sending/PendingEvent.js index a4fbfc03..554ed06a 100644 --- a/src/matrix/room/sending/PendingEvent.js +++ b/src/matrix/room/sending/PendingEvent.js @@ -64,6 +64,10 @@ export class PendingEvent { return this._data.needsUpload && !this.aborted; } + get isMissingAttachments() { + return this.needsUpload && !this._attachments; + } + setEncrypting() { this._status = SendStatus.Encrypting; this._emitUpdate("status"); @@ -96,6 +100,9 @@ export class PendingEvent { if (!this.needsUpload) { return; } + if (!this._attachments) { + throw new Error("attachments missing"); + } if (this.needsEncryption) { this._status = SendStatus.EncryptingAttachments; this._emitUpdate("status"); diff --git a/src/platform/web/ui/session/room/TimelineList.js b/src/platform/web/ui/session/room/TimelineList.js index 5d0f3fbe..9d946fce 100644 --- a/src/platform/web/ui/session/room/TimelineList.js +++ b/src/platform/web/ui/session/room/TimelineList.js @@ -19,6 +19,7 @@ import {GapView} from "./timeline/GapView.js"; import {TextMessageView} from "./timeline/TextMessageView.js"; import {ImageView} from "./timeline/ImageView.js"; import {FileView} from "./timeline/FileView.js"; +import {MissingAttachmentView} from "./timeline/MissingAttachmentView.js"; import {AnnouncementView} from "./timeline/AnnouncementView.js"; function viewClassForEntry(entry) { @@ -30,6 +31,7 @@ function viewClassForEntry(entry) { return TextMessageView; case "image": return ImageView; case "file": return FileView; + case "missing-attachment": return MissingAttachmentView; } } diff --git a/src/platform/web/ui/session/room/timeline/MissingAttachmentView.js b/src/platform/web/ui/session/room/timeline/MissingAttachmentView.js new file mode 100644 index 00000000..329a82e9 --- /dev/null +++ b/src/platform/web/ui/session/room/timeline/MissingAttachmentView.js @@ -0,0 +1,25 @@ +/* +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 {TemplateView} from "../../../general/TemplateView.js"; +import {renderMessage} from "./common.js"; + +export class MissingAttachmentView extends TemplateView { + render(t, vm) { + const cancel = t.button({className: "link", onClick: () => vm.abortSending()}, vm.i18n`Cancel`); + return renderMessage(t, vm, t.p([vm.label, " ", cancel])); + } +} diff --git a/src/platform/web/ui/session/room/timeline/common.js b/src/platform/web/ui/session/room/timeline/common.js index 50b6a0cd..6f275141 100644 --- a/src/platform/web/ui/session/room/timeline/common.js +++ b/src/platform/web/ui/session/room/timeline/common.js @@ -24,7 +24,7 @@ export function renderMessage(t, vm, children) { pending: vm.isPending, unverified: vm.isUnverified, continuation: vm => vm.isContinuation, - messageStatus: vm => vm.shape === "message-status", + messageStatus: vm => vm.shape === "message-status" || vm.shape === "missing-attachment", }; const profile = t.div({className: "profile"}, [