diff --git a/src/components/views/rooms/wysiwyg_composer/message.ts b/src/components/views/rooms/wysiwyg_composer/message.ts index c1569324ef..5569af02a9 100644 --- a/src/components/views/rooms/wysiwyg_composer/message.ts +++ b/src/components/views/rooms/wysiwyg_composer/message.ts @@ -22,13 +22,23 @@ import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread"; import { PosthogAnalytics } from "../../../../PosthogAnalytics"; import SettingsStore from "../../../../settings/SettingsStore"; import { decorateStartSendingTime, sendRoundTripMetric } from "../../../../sendTimePerformanceMetrics"; -import { attachRelation } from "../SendMessageComposer"; import { RoomPermalinkCreator } from "../../../../utils/permalinks/Permalinks"; import { doMaybeLocalRoomAction } from "../../../../utils/local-room"; import { CHAT_EFFECTS } from "../../../../effects"; import { containsEmoji } from "../../../../effects/utils"; import { IRoomState } from "../../../structures/RoomView"; import dis from '../../../../dispatcher/dispatcher'; +import { addReplyToMessageContent } from "../../../../utils/Reply"; + +// Merges favouring the given relation +function attachRelation(content: IContent, relation?: IEventRelation): void { + if (relation) { + content['m.relates_to'] = { + ...(content['m.relates_to'] || {}), + ...relation, + }; + } +} interface SendMessageParams { mxClient: MatrixClient; @@ -81,13 +91,12 @@ export function createMessageContent( attachRelation(content, relation); - // TODO reply - /*if (replyToEvent) { + if (replyToEvent) { addReplyToMessageContent(content, replyToEvent, { permalinkCreator, includeLegacyFallback: includeReplyLegacyFallback, }); - }*/ + } return content; } @@ -148,8 +157,7 @@ export function sendMessage( mxClient, ); - // TODO reply - /*if (replyToEvent) { + if (replyToEvent) { // Clear reply_to_event as we put the message into the queue // if the send fails, retry will handle resending. dis.dispatch({ @@ -157,7 +165,8 @@ export function sendMessage( event: null, context: roomContext.timelineRenderingType, }); - }*/ + } + dis.dispatch({ action: "message_sent" }); CHAT_EFFECTS.forEach((effect) => { if (containsEmoji(content, effect.emojis)) { diff --git a/test/components/views/rooms/wysiwyg_composer/message-test.ts b/test/components/views/rooms/wysiwyg_composer/message-test.ts index 605b3e35a7..712b671c9f 100644 --- a/test/components/views/rooms/wysiwyg_composer/message-test.ts +++ b/test/components/views/rooms/wysiwyg_composer/message-test.ts @@ -22,9 +22,14 @@ import { createTestClient, mkEvent, mkStubRoom } from "../../../../test-utils"; import defaultDispatcher from "../../../../../src/dispatcher/dispatcher"; import SettingsStore from "../../../../../src/settings/SettingsStore"; import { SettingLevel } from "../../../../../src/settings/SettingLevel"; +import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks"; describe('message', () => { - const permalinkCreator = jest.fn() as any; + const permalinkCreator = { + forEvent(eventId: string): string { + return "$$permalink$$"; + }, + } as RoomPermalinkCreator; const message = 'hello world'; const mockEvent = mkEvent({ type: "m.room.message", @@ -45,10 +50,51 @@ describe('message', () => { // Then expect(content).toEqual({ - body: message, - format: "org.matrix.custom.html", - formatted_body: message, - msgtype: "m.text", + "body": message, + "format": "org.matrix.custom.html", + "formatted_body": message, + "msgtype": "m.text", + }); + }); + + it('Should add reply to message content', () => { + // When + const content = createMessageContent(message, { permalinkCreator, replyToEvent: mockEvent }); + + // Then + expect(content).toEqual({ + "body": "> Replying to this\n\nhello world", + "format": "org.matrix.custom.html", + "formatted_body": "
In reply to" + + " myfakeuser"+ + "
Replying to this
hello world", + "msgtype": "m.text", + "m.relates_to": { + "m.in_reply_to": { + "event_id": mockEvent.getId(), + }, + }, + }); + }); + + it("Should add relation to message", () => { + // When + const relation = { + rel_type: "m.thread", + event_id: "myFakeThreadId", + }; + const content = createMessageContent(message, { permalinkCreator, relation }); + + // Then + expect(content).toEqual({ + "body": message, + "format": "org.matrix.custom.html", + "formatted_body": message, + "msgtype": "m.text", + "m.relates_to": { + "event_id": "myFakeThreadId", + "rel_type": "m.thread", + }, }); }); }); @@ -102,6 +148,15 @@ describe('message', () => { const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch"); it('Should not send empty html message', async () => { + // When + await sendMessage('', { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }); + + // Then + expect(mockClient.sendMessage).toBeCalledTimes(0); + expect(spyDispatcher).toBeCalledTimes(0); + }); + + it('Should send html message', async () => { // When await sendMessage(message, { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }); @@ -116,13 +171,44 @@ describe('message', () => { expect(spyDispatcher).toBeCalledWith({ action: 'message_sent' }); }); - it('Should send html message', async () => { + it('Should send reply to html message', async () => { + const mockReplyEvent = mkEvent({ + type: "m.room.message", + room: 'myfakeroom', + user: 'myfakeuser2', + content: { "msgtype": "m.text", "body": "My reply" }, + event: true, + }); + // When - await sendMessage('', { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }); + await sendMessage(message, { + roomContext: defaultRoomContext, + mxClient: mockClient, + permalinkCreator, + replyToEvent: mockReplyEvent, + }); // Then - expect(mockClient.sendMessage).toBeCalledTimes(0); - expect(spyDispatcher).toBeCalledTimes(0); + expect(spyDispatcher).toBeCalledWith({ + action: 'reply_to_event', + event: null, + context: defaultRoomContext.timelineRenderingType, + }); + + const expectedContent = { + "body": "> My reply\n\nhello world", + "format": "org.matrix.custom.html", + "formatted_body": "
In reply to" + + " myfakeuser2" + + "
My reply
hello world", + "msgtype": "m.text", + "m.relates_to": { + "m.in_reply_to": { + "event_id": mockReplyEvent.getId(), + }, + }, + }; + expect(mockClient.sendMessage).toBeCalledWith('myfakeroom', null, expectedContent); }); it('Should scroll to bottom after sending a html message', async () => {