From 801eb068d69b24482c417a995c17cb4febed1881 Mon Sep 17 00:00:00 2001 From: Germain Date: Wed, 3 Nov 2021 08:43:24 +0000 Subject: [PATCH] Make attachments uploadable to a thread (#7064) --- res/css/structures/_UploadBar.scss | 6 ++++ src/ContentMessages.tsx | 36 ++++++++++++++++--- src/components/structures/RoomView.tsx | 4 +-- src/components/structures/ThreadView.tsx | 19 +++++++--- src/components/structures/UploadBar.tsx | 4 ++- .../views/rooms/MessageComposer.tsx | 9 +++-- .../views/rooms/SendMessageComposer.tsx | 2 +- src/models/IUpload.ts | 2 ++ 8 files changed, 66 insertions(+), 16 deletions(-) diff --git a/res/css/structures/_UploadBar.scss b/res/css/structures/_UploadBar.scss index 7c62516b47..4fe6773930 100644 --- a/res/css/structures/_UploadBar.scss +++ b/res/css/structures/_UploadBar.scss @@ -23,6 +23,12 @@ limitations under the License. } } +.mx_ThreadView { + .mx_UploadBar { + padding-left: 0; + } +} + .mx_UploadBar_filename { margin-top: 5px; color: $muted-fg-color; diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx index b85c0462b5..7455d76c59 100644 --- a/src/ContentMessages.tsx +++ b/src/ContentMessages.tsx @@ -46,6 +46,7 @@ import SettingsStore from "./settings/SettingsStore"; import { decorateStartSendingTime, sendRoundTripMetric } from "./sendTimePerformanceMetrics"; import { logger } from "matrix-js-sdk/src/logger"; +import { IEventRelation } from "matrix-js-sdk/src"; const MAX_WIDTH = 800; const MAX_HEIGHT = 600; @@ -436,7 +437,12 @@ export default class ContentMessages { } } - async sendContentListToRoom(files: File[], roomId: string, matrixClient: MatrixClient) { + async sendContentListToRoom( + files: File[], + roomId: string, + relation: IEventRelation | null, + matrixClient: MatrixClient, + ) { if (matrixClient.isGuest()) { dis.dispatch({ action: 'require_registration' }); return; @@ -512,11 +518,20 @@ export default class ContentMessages { uploadAll = true; } } - promBefore = this.sendContentToRoom(file, roomId, matrixClient, promBefore); + promBefore = this.sendContentToRoom(file, roomId, relation, matrixClient, promBefore); } } - getCurrentUploads() { + getCurrentUploads(relation?: IEventRelation) { + return this.inprogress.filter(upload => { + const noRelation = !relation && !upload.relation; + const matchingRelation = relation && upload.relation + && relation.rel_type === upload.relation.rel_type + && relation.event_id === upload.relation.event_id; + + return (noRelation || matchingRelation) && !upload.canceled; + }); + return this.inprogress.filter(u => !u.canceled); } @@ -535,7 +550,13 @@ export default class ContentMessages { } } - private sendContentToRoom(file: File, roomId: string, matrixClient: MatrixClient, promBefore: Promise) { + private sendContentToRoom( + file: File, + roomId: string, + relation: IEventRelation, + matrixClient: MatrixClient, + promBefore: Promise, + ) { const startTime = CountlyAnalytics.getTimestamp(); const content: IContent = { body: file.name || 'Attachment', @@ -545,6 +566,10 @@ export default class ContentMessages { msgtype: "", // set later }; + if (relation) { + content["m.relates_to"] = relation; + } + if (SettingsStore.getValue("Performance.addSendMessageTimingMetadata")) { decorateStartSendingTime(content); } @@ -590,7 +615,8 @@ export default class ContentMessages { const upload: IUpload = { fileName: file.name || 'Attachment', - roomId: roomId, + roomId, + relation, total: file.size, loaded: 0, promise: prom, diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 33fde6e509..03cca683ae 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -786,7 +786,7 @@ export class RoomView extends React.Component { break; case 'picture_snapshot': ContentMessages.sharedInstance().sendContentListToRoom( - [payload.file], this.state.room.roomId, this.context); + [payload.file], this.state.room.roomId, null, this.context); break; case 'notifier_enabled': case Action.UploadStarted: @@ -1292,7 +1292,7 @@ export class RoomView extends React.Component { ev.stopPropagation(); ev.preventDefault(); ContentMessages.sharedInstance().sendContentListToRoom( - ev.dataTransfer.files, this.state.room.roomId, this.context, + ev.dataTransfer.files, this.state.room.roomId, null, this.context, ); dis.fire(Action.FocusSendMessageComposer); diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 17895be9d1..8cdc560df8 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from 'react'; -import { MatrixEvent, Room } from 'matrix-js-sdk/src'; +import { IEventRelation, MatrixEvent, Room } from 'matrix-js-sdk/src'; import { Thread, ThreadEvent } from 'matrix-js-sdk/src/models/thread'; import { RelationType } from 'matrix-js-sdk/src/@types/event'; @@ -37,6 +37,8 @@ import { MatrixClientPeg } from '../../MatrixClientPeg'; import { E2EStatus } from '../../utils/ShieldUtils'; import EditorStateTransfer from '../../utils/EditorStateTransfer'; import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext'; +import ContentMessages from '../../ContentMessages'; +import UploadBar from './UploadBar'; import { ChevronFace, ContextMenuTooltipButton } from './ContextMenu'; import { _t } from '../../languageHandler'; import IconizedContextMenu, { @@ -304,6 +306,12 @@ export default class ThreadView extends React.Component { const highlightedEventId = this.props.initialEventHighlighted ? this.props.initialEvent?.getId() : null; + + const threadRelation: IEventRelation = { + rel_type: RelationType.Thread, + event_id: this.state.thread?.id, + }; + return ( { /> ) } + { ContentMessages.sharedInstance().getCurrentUploads(threadRelation).length > 0 && ( + + ) } + { this.state?.thread?.timelineSet && ( { } private getUploadsInRoom(): IUpload[] { - const uploads = ContentMessages.sharedInstance().getCurrentUploads(); + const uploads = ContentMessages.sharedInstance().getCurrentUploads(this.props.relation); return uploads.filter(u => u.roomId === this.props.room.roomId); } diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 8075082dbf..a9bf62f887 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -128,6 +128,7 @@ const EmojiButton: React.FC = ({ addEmoji, menuPosition, narr interface IUploadButtonProps { roomId: string; + relation?: IEventRelation | null; } class UploadButton extends React.Component { @@ -169,7 +170,7 @@ class UploadButton extends React.Component { } ContentMessages.sharedInstance().sendContentListToRoom( - tfiles, this.props.roomId, MatrixClientPeg.get(), + tfiles, this.props.roomId, this.props.relation, MatrixClientPeg.get(), ); // This is the onChange handler for a file form control, but we're @@ -479,7 +480,11 @@ export default class MessageComposer extends React.Component { ); } buttons.push( - , + , ); buttons.push( , diff --git a/src/components/views/rooms/SendMessageComposer.tsx b/src/components/views/rooms/SendMessageComposer.tsx index 35bc85ba23..cda78c36b0 100644 --- a/src/components/views/rooms/SendMessageComposer.tsx +++ b/src/components/views/rooms/SendMessageComposer.tsx @@ -615,7 +615,7 @@ export class SendMessageComposer extends React.Component;