Add reply support to WysiwygComposer

This commit is contained in:
Florian Duros 2022-10-14 15:52:05 +02:00
parent ba5bd74ac8
commit 4ba3f99489
No known key found for this signature in database
GPG key ID: 9700AA5870258A0B
2 changed files with 86 additions and 16 deletions

View file

@ -22,13 +22,23 @@ import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
import { PosthogAnalytics } from "../../../../PosthogAnalytics"; import { PosthogAnalytics } from "../../../../PosthogAnalytics";
import SettingsStore from "../../../../settings/SettingsStore"; import SettingsStore from "../../../../settings/SettingsStore";
import { decorateStartSendingTime, sendRoundTripMetric } from "../../../../sendTimePerformanceMetrics"; import { decorateStartSendingTime, sendRoundTripMetric } from "../../../../sendTimePerformanceMetrics";
import { attachRelation } from "../SendMessageComposer";
import { RoomPermalinkCreator } from "../../../../utils/permalinks/Permalinks"; import { RoomPermalinkCreator } from "../../../../utils/permalinks/Permalinks";
import { doMaybeLocalRoomAction } from "../../../../utils/local-room"; import { doMaybeLocalRoomAction } from "../../../../utils/local-room";
import { CHAT_EFFECTS } from "../../../../effects"; import { CHAT_EFFECTS } from "../../../../effects";
import { containsEmoji } from "../../../../effects/utils"; import { containsEmoji } from "../../../../effects/utils";
import { IRoomState } from "../../../structures/RoomView"; import { IRoomState } from "../../../structures/RoomView";
import dis from '../../../../dispatcher/dispatcher'; 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 { interface SendMessageParams {
mxClient: MatrixClient; mxClient: MatrixClient;
@ -81,13 +91,12 @@ export function createMessageContent(
attachRelation(content, relation); attachRelation(content, relation);
// TODO reply if (replyToEvent) {
/*if (replyToEvent) {
addReplyToMessageContent(content, replyToEvent, { addReplyToMessageContent(content, replyToEvent, {
permalinkCreator, permalinkCreator,
includeLegacyFallback: includeReplyLegacyFallback, includeLegacyFallback: includeReplyLegacyFallback,
}); });
}*/ }
return content; return content;
} }
@ -148,8 +157,7 @@ export function sendMessage(
mxClient, mxClient,
); );
// TODO reply if (replyToEvent) {
/*if (replyToEvent) {
// Clear reply_to_event as we put the message into the queue // Clear reply_to_event as we put the message into the queue
// if the send fails, retry will handle resending. // if the send fails, retry will handle resending.
dis.dispatch({ dis.dispatch({
@ -157,7 +165,8 @@ export function sendMessage(
event: null, event: null,
context: roomContext.timelineRenderingType, context: roomContext.timelineRenderingType,
}); });
}*/ }
dis.dispatch({ action: "message_sent" }); dis.dispatch({ action: "message_sent" });
CHAT_EFFECTS.forEach((effect) => { CHAT_EFFECTS.forEach((effect) => {
if (containsEmoji(content, effect.emojis)) { if (containsEmoji(content, effect.emojis)) {

View file

@ -22,9 +22,14 @@ import { createTestClient, mkEvent, mkStubRoom } from "../../../../test-utils";
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher"; import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
import SettingsStore from "../../../../../src/settings/SettingsStore"; import SettingsStore from "../../../../../src/settings/SettingsStore";
import { SettingLevel } from "../../../../../src/settings/SettingLevel"; import { SettingLevel } from "../../../../../src/settings/SettingLevel";
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
describe('message', () => { describe('message', () => {
const permalinkCreator = jest.fn() as any; const permalinkCreator = {
forEvent(eventId: string): string {
return "$$permalink$$";
},
} as RoomPermalinkCreator;
const message = '<i><b>hello</b> world</i>'; const message = '<i><b>hello</b> world</i>';
const mockEvent = mkEvent({ const mockEvent = mkEvent({
type: "m.room.message", type: "m.room.message",
@ -45,10 +50,28 @@ describe('message', () => {
// Then // Then
expect(content).toEqual({ expect(content).toEqual({
body: message, "body": message,
format: "org.matrix.custom.html", "format": "org.matrix.custom.html",
formatted_body: message, "formatted_body": message,
msgtype: "m.text", "msgtype": "m.text",
});
});
it('Should add reply to message content', () => {
// When
const content = createMessageContent(message, { permalinkCreator, replyToEvent: mockEvent });
// Then
expect(content).toEqual({
"body": "> <myfakeuser> Replying to this\n\n<i><b>hello</b> world</i>",
"format": "org.matrix.custom.html",
"formatted_body": "<mx-reply><blockquote><a href=\"$$permalink$$\">In reply to</a> <a href=\"https://matrix.to/#/myfakeuser\">myfakeuser</a><br>Replying to this</blockquote></mx-reply><i><b>hello</b> world</i>",
"msgtype": "m.text",
"m.relates_to": {
"m.in_reply_to": {
"event_id": mockEvent.getId(),
},
},
}); });
}); });
}); });
@ -102,6 +125,15 @@ describe('message', () => {
const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch"); const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");
it('Should not send empty html message', async () => { 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 // When
await sendMessage(message, { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }); await sendMessage(message, { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator });
@ -116,13 +148,42 @@ describe('message', () => {
expect(spyDispatcher).toBeCalledWith({ action: 'message_sent' }); 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 // When
await sendMessage('', { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }); await sendMessage(message, {
roomContext: defaultRoomContext,
mxClient: mockClient,
permalinkCreator,
replyToEvent: mockReplyEvent,
});
// Then // Then
expect(mockClient.sendMessage).toBeCalledTimes(0); expect(spyDispatcher).toBeCalledWith({
expect(spyDispatcher).toBeCalledTimes(0); action: 'reply_to_event',
event: null,
context: defaultRoomContext.timelineRenderingType,
});
const expectedContent = {
"body": "> <myfakeuser2> My reply\n\n<i><b>hello</b> world</i>",
"format": "org.matrix.custom.html",
"formatted_body": "<mx-reply><blockquote><a href=\"$$permalink$$\">In reply to</a> <a href=\"https://matrix.to/#/myfakeuser2\">myfakeuser2</a><br>My reply</blockquote></mx-reply><i><b>hello</b> world</i>",
"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 () => { it('Should scroll to bottom after sending a html message', async () => {