diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx index 99be5174f6d..3d9b29ac154 100644 --- a/src/ContentMessages.tsx +++ b/src/ContentMessages.tsx @@ -39,6 +39,7 @@ import { import { IUpload } from "./models/IUpload"; import { IAbortablePromise, IImageInfo } from "matrix-js-sdk/src/@types/partials"; import { BlurhashEncoder } from "./BlurhashEncoder"; +import SettingsStore from "./settings/SettingsStore"; const MAX_WIDTH = 800; const MAX_HEIGHT = 600; @@ -442,7 +443,7 @@ export default class ContentMessages { } const isQuoting = Boolean(RoomViewStore.getQuotingEvent()); - if (isQuoting) { + if (isQuoting && !SettingsStore.getValue("feature_message_attachments")) { // FIXME: Using an import will result in Element crashing const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Reply Warning', '', QuestionDialog, { diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 03170584864..7ca9f8c09d3 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -754,7 +754,7 @@ export default class RoomView extends React.Component { break; case 'picture_snapshot': { const promAfter = (SettingsStore.getValue("feature_message_attachments") && this.messageComposer - && !this.messageComposer.state.isComposerEmpty) ? + && (!this.messageComposer.state.isComposerEmpty || this.messageComposer.props.replyToEvent)) ? (event: ISendEventResponse) => { return this.messageComposer.sendMessage(event.event_id); } : null; @@ -1255,7 +1255,8 @@ export default class RoomView extends React.Component { ev.stopPropagation(); ev.preventDefault(); const promAfter = (SettingsStore.getValue("feature_message_attachments") && this.messageComposer - && ev.dataTransfer.files.length === 1 && !this.messageComposer.state.isComposerEmpty) ? + && ev.dataTransfer.files.length === 1 + && (!this.messageComposer.state.isComposerEmpty || this.messageComposer.props.replyToEvent)) ? (event: ISendEventResponse) => { return this.messageComposer.sendMessage(event.event_id); } : null; diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 07cc465f8f9..b57afc5c434 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -150,7 +150,8 @@ class UploadButton extends React.Component { } const promAfter = (SettingsStore.getValue("feature_message_attachments") && this.props.composer - && ev.target.files.length === 1 && !this.props.composer.state.isComposerEmpty) ? + && ev.target.files.length === 1 + && (!this.props.composer.state.isComposerEmpty || this.props.composer.props.replyToEvent)) ? (event: ISendEventResponse) => { return this.props.composer.sendMessage(event.event_id); } : null; diff --git a/src/components/views/rooms/SendMessageComposer.tsx b/src/components/views/rooms/SendMessageComposer.tsx index 7519729e53a..90830fba913 100644 --- a/src/components/views/rooms/SendMessageComposer.tsx +++ b/src/components/views/rooms/SendMessageComposer.tsx @@ -55,6 +55,7 @@ import ErrorDialog from "../dialogs/ErrorDialog"; import QuestionDialog from "../dialogs/QuestionDialog"; import { ActionPayload } from "../../../dispatcher/payloads"; import { ISendEventResponse } from 'matrix-js-sdk/src/@types/requests'; +import DocumentOffset from '../../../editor/offset'; function addReplyToMessageContent( content: IContent, @@ -110,15 +111,18 @@ export function createMessageContent( body: body, }; const formattedBody = htmlSerializeIfNeeded(model, { forceHTML: !!replyToEvent }); - if (formattedBody) { + if (formattedBody || replyToEvent) { content.format = "org.matrix.custom.html"; - content.formatted_body = formattedBody; + content.formatted_body = formattedBody || body; } if (replyToEvent) { addReplyToMessageContent(content, replyToEvent, permalinkCreator); } + // TODO: Currently, an attachment will override a reply. + // This allows replying with images, but removes the reply relation from the message. + // When/if we get the ability to add multiple relations, this will be fixed. if (attachEventId) { addAttachmentToMessageContent(content, attachEventId); } @@ -363,7 +367,12 @@ export default class SendMessageComposer extends React.Component { public async sendMessage(attachmentEventId?: string): Promise { if (this.model.isEmpty) { - return; + if (!attachmentEventId) { + return; + } + // If replying with just an attachment, add empty text to model so it has at least one part. + // Otherwise, various functions expecting at least one part will fail. + this.model.update(" ", "insertText", new DocumentOffset(1, true)); } const replyToEvent = this.props.replyToEvent; @@ -543,8 +552,8 @@ export default class SendMessageComposer extends React.Component { // We check text/rtf instead of text/plain as when copy+pasting a file from Finder or Gnome Image Viewer // it puts the filename in as text/plain which we want to ignore. if (clipboardData.files.length && !clipboardData.types.includes("text/rtf")) { - const promAfter = (SettingsStore.getValue("feature_message_attachents") - && clipboardData.files.length === 1 && !this.model.isEmpty) ? + const promAfter = (SettingsStore.getValue("feature_message_attachments") + && clipboardData.files.length === 1 && (!this.model.isEmpty || this.props.replyToEvent)) ? (event: ISendEventResponse) => { return this.sendMessage(event.event_id); } : null;