From 451cd73c1693cb0b5e8fbb86f7c0ff82d1f8f031 Mon Sep 17 00:00:00 2001 From: Alun Turner Date: Mon, 10 Apr 2023 09:48:49 +0100 Subject: [PATCH 1/7] add /me handling --- .../utils/createMessageContent.ts | 27 ++++++++++--------- .../rooms/wysiwyg_composer/utils/message.ts | 7 ++--- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts index 6f13d3fd272..6c64bbafc8d 100644 --- a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts +++ b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts @@ -21,6 +21,8 @@ import SettingsStore from "../../../../../settings/SettingsStore"; import { RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks"; import { addReplyToMessageContent } from "../../../../../utils/Reply"; +export const EMOTE_PREFIX = "/me "; + // Merges favouring the given relation function attachRelation(content: IContent, relation?: IEventRelation): void { if (relation) { @@ -62,7 +64,7 @@ interface CreateMessageContentParams { } export async function createMessageContent( - message: string, + messageProp: string, isHTML: boolean, { relation, @@ -72,22 +74,24 @@ export async function createMessageContent( editedEvent, }: CreateMessageContentParams, ): Promise { - // TODO emote ? - const isEditing = Boolean(editedEvent); const isReply = isEditing ? Boolean(editedEvent?.replyEventId) : Boolean(replyToEvent); const isReplyAndEditing = isEditing && isReply; - /*const isEmote = containsEmote(model); + let message = messageProp; + + const isEmote = message.startsWith(EMOTE_PREFIX); if (isEmote) { - model = stripEmoteCommand(model); + // if an emote, remove the emote prefix - nb assuming here it's plain + // text as wrapping /me in a

tag won't hit here + message = message.slice(EMOTE_PREFIX.length); } - if (startsWith(model, "//")) { - model = stripPrefix(model, "/"); + if (message.startsWith("//")) { + // if user wants to enter a single slash at the start of a message, this + // is how they have to do it (due to it clashing with commands), so we + // remove the first character to make sure //word displays as /word + message = message.slice(1); } - model = unescapeMessage(model);*/ - - // const body = textSerialize(model); // if we're editing rich text, the message content is pure html // BUT if we're not, the message content will be plain text @@ -96,8 +100,7 @@ export async function createMessageContent( const formattedBodyPrefix = (isReplyAndEditing && getHtmlReplyFallback(editedEvent)) || ""; const content: IContent = { - // TODO emote - msgtype: MsgType.Text, + msgtype: isEmote ? MsgType.Emote : MsgType.Text, body: isEditing ? `${bodyPrefix} * ${body}` : body, }; diff --git a/src/components/views/rooms/wysiwyg_composer/utils/message.ts b/src/components/views/rooms/wysiwyg_composer/utils/message.ts index 9753ebae494..6ba5b553fda 100644 --- a/src/components/views/rooms/wysiwyg_composer/utils/message.ts +++ b/src/components/views/rooms/wysiwyg_composer/utils/message.ts @@ -31,7 +31,7 @@ import dis from "../../../../../dispatcher/dispatcher"; import { createRedactEventDialog } from "../../../dialogs/ConfirmRedactDialog"; import { endEditing, cancelPreviousPendingEdit } from "./editing"; import EditorStateTransfer from "../../../../../utils/EditorStateTransfer"; -import { createMessageContent } from "./createMessageContent"; +import { createMessageContent, EMOTE_PREFIX } from "./createMessageContent"; import { isContentModified } from "./isContentModified"; import { CommandCategories, getCommand } from "../../../../../SlashCommands"; import { runSlashCommand, shouldSendAnyway } from "../../../../../editor/commands"; @@ -78,8 +78,9 @@ export async function sendMessage( let content: IContent | null = null; - // Functionality here approximates what can be found in SendMessageComposer.sendMessage() - if (message.startsWith("/") && !message.startsWith("//")) { + // Slash command handling here approximates what can be found in SendMessageComposer.sendMessage() + // but note that the /me and // special cases are handled by the call to createMessageContent + if (message.startsWith("/") && !message.startsWith("//") && !message.startsWith(EMOTE_PREFIX)) { const { cmd, args } = getCommand(message); if (cmd) { // TODO handle /me special case separately, see end of SlashCommands.Commands From 8984bbdedd03010cea585df21c524ad8627d48e6 Mon Sep 17 00:00:00 2001 From: Alun Turner Date: Mon, 10 Apr 2023 09:55:51 +0100 Subject: [PATCH 2/7] use typeguards to avoid fighting TS --- .../wysiwyg_composer/utils/createMessageContent.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts index 6c64bbafc8d..45112411243 100644 --- a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts +++ b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts @@ -63,6 +63,14 @@ interface CreateMessageContentParams { editedEvent?: MatrixEvent; } +function isMatrixEvent(e: MatrixEvent | undefined): e is MatrixEvent { + return Boolean(e); +} + +function isMatrixEventAndReply(areWeEditing: boolean, e: MatrixEvent | undefined): e is MatrixEvent { + return areWeEditing ? Boolean(e?.replyEventId) : Boolean(e); +} + export async function createMessageContent( messageProp: string, isHTML: boolean, @@ -74,8 +82,8 @@ export async function createMessageContent( editedEvent, }: CreateMessageContentParams, ): Promise { - const isEditing = Boolean(editedEvent); - const isReply = isEditing ? Boolean(editedEvent?.replyEventId) : Boolean(replyToEvent); + const isEditing = isMatrixEvent(editedEvent); + const isReply = isMatrixEventAndReply(isEditing, isEditing ? editedEvent : replyToEvent); const isReplyAndEditing = isEditing && isReply; let message = messageProp; From a1317f2a0b88b49a5a2733905e696e0e348b9dc9 Mon Sep 17 00:00:00 2001 From: Alun Turner Date: Mon, 10 Apr 2023 10:12:32 +0100 Subject: [PATCH 3/7] improve clarity and use of typeguards --- .../wysiwyg_composer/utils/createMessageContent.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts index 45112411243..5584830c641 100644 --- a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts +++ b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts @@ -63,13 +63,7 @@ interface CreateMessageContentParams { editedEvent?: MatrixEvent; } -function isMatrixEvent(e: MatrixEvent | undefined): e is MatrixEvent { - return Boolean(e); -} - -function isMatrixEventAndReply(areWeEditing: boolean, e: MatrixEvent | undefined): e is MatrixEvent { - return areWeEditing ? Boolean(e?.replyEventId) : Boolean(e); -} +const isMatrixEvent = (e: MatrixEvent | undefined): e is MatrixEvent => e instanceof MatrixEvent; export async function createMessageContent( messageProp: string, @@ -83,7 +77,7 @@ export async function createMessageContent( }: CreateMessageContentParams, ): Promise { const isEditing = isMatrixEvent(editedEvent); - const isReply = isMatrixEventAndReply(isEditing, isEditing ? editedEvent : replyToEvent); + const isReply = isEditing ? Boolean(editedEvent.replyEventId) : isMatrixEvent(replyToEvent); const isReplyAndEditing = isEditing && isReply; let message = messageProp; From b3c063da337c1f6862c2cf6802515bf3851df2a7 Mon Sep 17 00:00:00 2001 From: Alun Turner Date: Mon, 10 Apr 2023 12:32:36 +0100 Subject: [PATCH 4/7] add createMessageContent tests --- .../utils/createMessageContent-test.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/test/components/views/rooms/wysiwyg_composer/utils/createMessageContent-test.ts b/test/components/views/rooms/wysiwyg_composer/utils/createMessageContent-test.ts index 340a4c1af2c..a77c7dc6045 100644 --- a/test/components/views/rooms/wysiwyg_composer/utils/createMessageContent-test.ts +++ b/test/components/views/rooms/wysiwyg_composer/utils/createMessageContent-test.ts @@ -13,10 +13,14 @@ 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 { MsgType } from "matrix-js-sdk/src/matrix"; import { mkEvent } from "../../../../../test-utils"; import { RoomPermalinkCreator } from "../../../../../../src/utils/permalinks/Permalinks"; -import { createMessageContent } from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/createMessageContent"; +import { + createMessageContent, + EMOTE_PREFIX, +} from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/createMessageContent"; describe("createMessageContent", () => { const permalinkCreator = { @@ -130,4 +134,24 @@ describe("createMessageContent", () => { }, }); }); + + it("Should strip the /me prefix from a message", async () => { + const textBody = "some body text"; + const content = await createMessageContent(EMOTE_PREFIX + textBody, true, { permalinkCreator }); + + expect(content).toMatchObject({ body: textBody, formatted_body: textBody }); + }); + + it("Should strip single / from message prefixed with //", async () => { + const content = await createMessageContent("//twoSlashes", true, { permalinkCreator }); + + expect(content).toMatchObject({ body: "/twoSlashes", formatted_body: "/twoSlashes" }); + }); + + it("Should set the content type to MsgType.Emote when /me prefix is used", async () => { + const textBody = "some body text"; + const content = await createMessageContent(EMOTE_PREFIX + textBody, true, { permalinkCreator }); + + expect(content).toMatchObject({ msgtype: MsgType.Emote }); + }); }); From f31bbcf38ca7deedf249c9e70809964746270841 Mon Sep 17 00:00:00 2001 From: Alun Turner Date: Mon, 10 Apr 2023 14:40:41 +0100 Subject: [PATCH 5/7] remove completed TODO --- src/components/views/rooms/wysiwyg_composer/utils/message.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/rooms/wysiwyg_composer/utils/message.ts b/src/components/views/rooms/wysiwyg_composer/utils/message.ts index 6ba5b553fda..1c8c17f818a 100644 --- a/src/components/views/rooms/wysiwyg_composer/utils/message.ts +++ b/src/components/views/rooms/wysiwyg_composer/utils/message.ts @@ -83,7 +83,6 @@ export async function sendMessage( if (message.startsWith("/") && !message.startsWith("//") && !message.startsWith(EMOTE_PREFIX)) { const { cmd, args } = getCommand(message); if (cmd) { - // TODO handle /me special case separately, see end of SlashCommands.Commands const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation?.event_id : null; let commandSuccessful: boolean; [content, commandSuccessful] = await runSlashCommand(cmd, args, roomId, threadId ?? null); From 459b1dd45307c3cadb9a31f92b5ce143d9f661e6 Mon Sep 17 00:00:00 2001 From: Alun Turner Date: Mon, 10 Apr 2023 14:46:05 +0100 Subject: [PATCH 6/7] improve comments --- .../rooms/wysiwyg_composer/utils/createMessageContent.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts index 5584830c641..79331c28094 100644 --- a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts +++ b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts @@ -84,13 +84,13 @@ export async function createMessageContent( const isEmote = message.startsWith(EMOTE_PREFIX); if (isEmote) { - // if an emote, remove the emote prefix - nb assuming here it's plain - // text as wrapping /me in a

tag won't hit here + // if we are dealing with an emote we want to remove the prefix so that `/me` does not + // appear after the `* ` text in the timeline message = message.slice(EMOTE_PREFIX.length); } if (message.startsWith("//")) { // if user wants to enter a single slash at the start of a message, this - // is how they have to do it (due to it clashing with commands), so we + // is how they have to do it (due to it clashing with commands), so here we // remove the first character to make sure //word displays as /word message = message.slice(1); } From 879ccaa67d5a7dd6f162d2b28c3c0b0f4a849ce6 Mon Sep 17 00:00:00 2001 From: Alun Turner Date: Mon, 10 Apr 2023 15:22:26 +0100 Subject: [PATCH 7/7] remove duplication and renaming of argument --- .../rooms/wysiwyg_composer/utils/createMessageContent.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts index 79331c28094..1bde32ca2b3 100644 --- a/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts +++ b/src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts @@ -66,7 +66,7 @@ interface CreateMessageContentParams { const isMatrixEvent = (e: MatrixEvent | undefined): e is MatrixEvent => e instanceof MatrixEvent; export async function createMessageContent( - messageProp: string, + message: string, isHTML: boolean, { relation, @@ -80,8 +80,6 @@ export async function createMessageContent( const isReply = isEditing ? Boolean(editedEvent.replyEventId) : isMatrixEvent(replyToEvent); const isReplyAndEditing = isEditing && isReply; - let message = messageProp; - const isEmote = message.startsWith(EMOTE_PREFIX); if (isEmote) { // if we are dealing with an emote we want to remove the prefix so that `/me` does not