From deba27c24d7b645743e648978a417397eb49a65c Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Tue, 17 Dec 2024 23:55:12 +0000 Subject: [PATCH 01/10] Remove reply fallbacks & add m.mentions (WIP) the typing on line 301 and 303 needs fixing but apart from that this is mint --- src/app/features/room/RoomInput.tsx | 47 ++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 4d43c7e96..4b89d6040 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -10,9 +10,9 @@ import React, { } from 'react'; import { useAtom, useAtomValue } from 'jotai'; import { isKeyHotkey } from 'is-hotkey'; -import { EventType, IContent, MsgType, RelationType, Room } from 'matrix-js-sdk'; +import { EventType, IContent, MsgType, RelationType, Room, IMentions } from 'matrix-js-sdk'; import { ReactEditor } from 'slate-react'; -import { Transforms, Editor } from 'slate'; +import {Transforms, Editor, Descendant} from 'slate'; import { Box, Dialog, @@ -109,6 +109,7 @@ import { useElementSizeObserver } from '../../hooks/useElementSizeObserver'; import { ReplyLayout, ThreadIndicator } from '../../components/message'; import { roomToParentsAtom } from '../../state/room/roomToParents'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; +import {CustomElement, InlineElement, MentionElement, ParagraphElement} from '../../components/editor/slate'; interface RoomInputProps { editor: Editor; @@ -248,7 +249,6 @@ export const RoomInput = forwardRef( uploadBoardHandlers.current?.handleSend(); const commandName = getBeginCommand(editor); - let plainText = toPlainText(editor.children).trim(); let customHtml = trimCustomHtml( toMatrixCustomHTML(editor.children, { @@ -289,25 +289,38 @@ export const RoomInput = forwardRef( if (plainText === '') return; - let body = plainText; - let formattedBody = customHtml; - if (replyDraft) { - body = parseReplyBody(replyDraft.userId, trimReplyFromBody(replyDraft.body)) + body; - formattedBody = - parseReplyFormattedBody( - roomId, - replyDraft.userId, - replyDraft.eventId, - replyDraft.formattedBody - ? trimReplyFromFormattedBody(replyDraft.formattedBody) - : sanitizeText(replyDraft.body) - ) + formattedBody; - } + const body = plainText; + const formattedBody = customHtml; const content: IContent = { msgtype: msgType, body, }; + const userIdMentions = new Set(); + let mentionsRoom = false; + editor.children.forEach((node: any): void => { + if (node.type === "paragraph") { + node.children.forEach((child: any): void => { + if (child.type !== undefined && child.type === "mention") { + if(child.name === "@room" && !child.id.startswith("@")) { + // Room mention, not MXID + mentionsRoom = true + } else { + userIdMentions.add(child.id) + } + } + }) + } + }) + const mMentions: IMentions = {} + if (userIdMentions.size > 0) { + mMentions.user_ids = Array.from(userIdMentions) + } + if(mentionsRoom) { + mMentions.room = true + } + + content["m.mentions"] = mMentions if (replyDraft || !customHtmlEqualsPlainText(formattedBody, body)) { content.format = 'org.matrix.custom.html'; content.formatted_body = formattedBody; From 4063956d37fbb37a37dc3138889011cbdbd27233 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Wed, 18 Dec 2024 00:30:19 +0000 Subject: [PATCH 02/10] Less jank typing --- src/app/features/room/RoomInput.tsx | 32 +++++++++++++---------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 4b89d6040..7ffb3e1c4 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -12,7 +12,7 @@ import { useAtom, useAtomValue } from 'jotai'; import { isKeyHotkey } from 'is-hotkey'; import { EventType, IContent, MsgType, RelationType, Room, IMentions } from 'matrix-js-sdk'; import { ReactEditor } from 'slate-react'; -import {Transforms, Editor, Descendant} from 'slate'; +import {Transforms, Editor} from 'slate'; import { Box, Dialog, @@ -96,12 +96,8 @@ import colorMXID from '../../../util/colorMXID'; import { getAllParents, getMemberDisplayName, - parseReplyBody, - parseReplyFormattedBody, trimReplyFromBody, - trimReplyFromFormattedBody, } from '../../utils/room'; -import { sanitizeText } from '../../utils/sanitize'; import { CommandAutocomplete } from './CommandAutocomplete'; import { Command, SHRUG, TABLEFLIP, UNFLIP, useCommands } from '../../hooks/useCommands'; import { mobileOrTablet } from '../../utils/user-agent'; @@ -109,7 +105,10 @@ import { useElementSizeObserver } from '../../hooks/useElementSizeObserver'; import { ReplyLayout, ThreadIndicator } from '../../components/message'; import { roomToParentsAtom } from '../../state/room/roomToParents'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; -import {CustomElement, InlineElement, MentionElement, ParagraphElement} from '../../components/editor/slate'; +import { + CustomElement, + MentionElement, +} from '../../components/editor/slate'; interface RoomInputProps { editor: Editor; @@ -298,19 +297,16 @@ export const RoomInput = forwardRef( }; const userIdMentions = new Set(); let mentionsRoom = false; - editor.children.forEach((node: any): void => { - if (node.type === "paragraph") { - node.children.forEach((child: any): void => { - if (child.type !== undefined && child.type === "mention") { - if(child.name === "@room" && !child.id.startswith("@")) { - // Room mention, not MXID - mentionsRoom = true - } else { - userIdMentions.add(child.id) - } + editor.children.forEach((node: CustomElement): void => { + node.children?.forEach((child: MentionElement): void => { + if (child.type === "mention") { + if(child.name === "@room" && !child.id?.startsWith("@")) { + mentionsRoom = true + } else { + userIdMentions.add(child.id) } - }) - } + } + }) }) const mMentions: IMentions = {} if (userIdMentions.size > 0) { From fee40654868b73e4ff334d3c94fc97d64038119c Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Sat, 28 Dec 2024 20:30:07 +0000 Subject: [PATCH 03/10] Mention the reply author in m.mentions --- src/app/features/room/RoomInput.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 7ffb3e1c4..02aa8ced5 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -12,7 +12,7 @@ import { useAtom, useAtomValue } from 'jotai'; import { isKeyHotkey } from 'is-hotkey'; import { EventType, IContent, MsgType, RelationType, Room, IMentions } from 'matrix-js-sdk'; import { ReactEditor } from 'slate-react'; -import {Transforms, Editor} from 'slate'; +import {Transforms, Editor, Descendant} from 'slate'; import { Box, Dialog, @@ -106,8 +106,8 @@ import { ReplyLayout, ThreadIndicator } from '../../components/message'; import { roomToParentsAtom } from '../../state/room/roomToParents'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; import { - CustomElement, - MentionElement, + CustomElement, InlineElement, + MentionElement, ParagraphElement, } from '../../components/editor/slate'; interface RoomInputProps { @@ -296,11 +296,15 @@ export const RoomInput = forwardRef( body, }; const userIdMentions = new Set(); + if (replyDraft) { + userIdMentions.add(replyDraft.userId); + } let mentionsRoom = false; - editor.children.forEach((node: CustomElement): void => { - node.children?.forEach((child: MentionElement): void => { + editor.children.forEach((node: Descendant): void => { + if(node.type !== "paragraph") return; + (node as ParagraphElement).children?.forEach((child: InlineElement): void => { if (child.type === "mention") { - if(child.name === "@room" && !child.id?.startsWith("@")) { + if(child.name === "@room" && !child.id.startsWith("@")) { mentionsRoom = true } else { userIdMentions.add(child.id) From a51538dd8126c344f3fe47147c97650455c6abf7 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Sat, 28 Dec 2024 20:51:02 +0000 Subject: [PATCH 04/10] Improve typing --- src/app/features/room/RoomInput.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 02aa8ced5..2989ef962 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -301,13 +301,15 @@ export const RoomInput = forwardRef( } let mentionsRoom = false; editor.children.forEach((node: Descendant): void => { - if(node.type !== "paragraph") return; - (node as ParagraphElement).children?.forEach((child: InlineElement): void => { + if(node.type === undefined || node.type !== "paragraph") return; + const paragraph: ParagraphElement = node as ParagraphElement; + paragraph.children?.forEach((child: InlineElement): void => { if (child.type === "mention") { - if(child.name === "@room" && !child.id.startsWith("@")) { + const mention: MentionElement = child as MentionElement; + if (mention.name === "@room" && !mention.id.startsWith("@")) { mentionsRoom = true } else { - userIdMentions.add(child.id) + userIdMentions.add(mention.id) } } }) From c67ecc2fb989f26a736baaff12c5d98a8f5b56f8 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Sun, 5 Jan 2025 18:48:48 +0000 Subject: [PATCH 05/10] Fix typing in m.mentions finder --- src/app/features/room/RoomInput.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 2989ef962..81c5084b5 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -301,10 +301,10 @@ export const RoomInput = forwardRef( } let mentionsRoom = false; editor.children.forEach((node: Descendant): void => { - if(node.type === undefined || node.type !== "paragraph") return; + if("type" in node && node.type !== "paragraph") return; const paragraph: ParagraphElement = node as ParagraphElement; paragraph.children?.forEach((child: InlineElement): void => { - if (child.type === "mention") { + if ("type" in child && child.type === "mention") { const mention: MentionElement = child as MentionElement; if (mention.name === "@room" && !mention.id.startsWith("@")) { mentionsRoom = true From 8e020090a0e04a39cb8a837d5b5d9a0621e8c516 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 13 Jan 2025 17:03:36 +0000 Subject: [PATCH 06/10] Correctly iterate through editor children, properly handle @room, ... ..., don't mention the reply author when the reply author is ourself, don't add own user IDs when mentioning intentionally --- src/app/features/room/RoomInput.tsx | 41 ++++++++++++++++------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 81c5084b5..ec397050f 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -52,11 +52,19 @@ import { trimCustomHtml, isEmptyEditor, getBeginCommand, - trimCommand, + trimCommand, BlockType, } from '../../components/editor'; import { EmojiBoard, EmojiBoardTab } from '../../components/emoji-board'; import { UseStateProvider } from '../../components/UseStateProvider'; -import { TUploadContent, encryptFile, getImageInfo, getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix'; +import { + TUploadContent, + encryptFile, + getImageInfo, + getMxIdLocalPart, + mxcUrlToHttp, + isUserId, + getCanonicalAliasOrRoomId +} from '../../utils/matrix'; import { useTypingStatusUpdater } from '../../hooks/useTypingStatusUpdater'; import { useFilePicker } from '../../hooks/useFilePicker'; import { useFilePasteHandler } from '../../hooks/useFilePasteHandler'; @@ -105,10 +113,7 @@ import { useElementSizeObserver } from '../../hooks/useElementSizeObserver'; import { ReplyLayout, ThreadIndicator } from '../../components/message'; import { roomToParentsAtom } from '../../state/room/roomToParents'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; -import { - CustomElement, InlineElement, - MentionElement, ParagraphElement, -} from '../../components/editor/slate'; +import { InlineElement } from '../../components/editor/slate'; interface RoomInputProps { editor: Editor; @@ -296,23 +301,23 @@ export const RoomInput = forwardRef( body, }; const userIdMentions = new Set(); - if (replyDraft) { + if (replyDraft && replyDraft.userId !== mx.getUserId()) { userIdMentions.add(replyDraft.userId); } let mentionsRoom = false; editor.children.forEach((node: Descendant): void => { - if("type" in node && node.type !== "paragraph") return; - const paragraph: ParagraphElement = node as ParagraphElement; - paragraph.children?.forEach((child: InlineElement): void => { - if ("type" in child && child.type === "mention") { - const mention: MentionElement = child as MentionElement; - if (mention.name === "@room" && !mention.id.startsWith("@")) { - mentionsRoom = true - } else { - userIdMentions.add(mention.id) + if ("type" in node && node.type === BlockType.Paragraph) { + node.children?.forEach((child: InlineElement): void => { + if ("type" in child && child.type === BlockType.Mention) { + const mention = child; + if (mention.id === getCanonicalAliasOrRoomId(mx, roomId)) { + mentionsRoom = true + } else if (isUserId(mention.id) && mention.id !== mx.getUserId()) { + userIdMentions.add(mention.id) + } } - } - }) + }) + } }) const mMentions: IMentions = {} if (userIdMentions.size > 0) { From 39dfbb0713d2daf23e6fcc5d39e7c7f23d40fb08 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 13 Jan 2025 17:05:09 +0000 Subject: [PATCH 07/10] Formatting --- src/app/features/room/RoomInput.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index ec397050f..ac41c1c67 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -52,7 +52,8 @@ import { trimCustomHtml, isEmptyEditor, getBeginCommand, - trimCommand, BlockType, + trimCommand, + BlockType, } from '../../components/editor'; import { EmojiBoard, EmojiBoardTab } from '../../components/emoji-board'; import { UseStateProvider } from '../../components/UseStateProvider'; From cc6b2b2934634d94d1e2fc1452533ee0f076ef6e Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 13 Jan 2025 17:08:36 +0000 Subject: [PATCH 08/10] Add intentional mentions to edited messages --- .../features/room/message/MessageEditor.tsx | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/app/features/room/message/MessageEditor.tsx b/src/app/features/room/message/MessageEditor.tsx index 0c9950308..f35f2f415 100644 --- a/src/app/features/room/message/MessageEditor.tsx +++ b/src/app/features/room/message/MessageEditor.tsx @@ -19,9 +19,9 @@ import { as, config, } from 'folds'; -import { Editor, Transforms } from 'slate'; +import {Descendant, Editor, Transforms} from 'slate'; import { ReactEditor } from 'slate-react'; -import { IContent, MatrixEvent, RelationType, Room } from 'matrix-js-sdk'; +import {IContent, IMentions, MatrixEvent, RelationType, Room} from 'matrix-js-sdk'; import { isKeyHotkey } from 'is-hotkey'; import { AUTOCOMPLETE_PREFIXES, @@ -42,7 +42,7 @@ import { toMatrixCustomHTML, toPlainText, trimCustomHtml, - useEditor, + useEditor, BlockType, } from '../../../components/editor'; import { useSetting } from '../../../state/hooks/settings'; import { settingsAtom } from '../../../state/settings'; @@ -52,6 +52,10 @@ import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { getEditedEvent, trimReplyFromFormattedBody } from '../../../utils/room'; import { mobileOrTablet } from '../../../utils/user-agent'; +import {InlineElement} from "../../../components/editor/slate"; +import {getCanonicalAliasOrRoomId, isUserId} from "../../../utils/matrix"; +import {useAtom} from "jotai/index"; +import {roomIdToReplyDraftAtomFamily} from "../../../state/room/roomInputDrafts"; type MessageEditorProps = { roomId: string; @@ -122,6 +126,36 @@ export const MessageEditor = as<'div', MessageEditorProps>( body: plainText, }; + const userIdMentions = new Set(); + // if (replyDraft && replyDraft.userId !== mx.getUserId()) { + // userIdMentions.add(replyDraft.userId); + // } + // TODO: Get the original message's reply to pick up the mention + let mentionsRoom = false; + editor.children.forEach((node: Descendant): void => { + if ("type" in node && node.type === BlockType.Paragraph) { + node.children?.forEach((child: InlineElement): void => { + if ("type" in child && child.type === BlockType.Mention) { + const mention = child; + if (mention.id === getCanonicalAliasOrRoomId(mx, roomId)) { + mentionsRoom = true + } else if (isUserId(mention.id) && mention.id !== mx.getUserId()) { + userIdMentions.add(mention.id) + } + } + }) + } + }) + const mMentions: IMentions = {} + if (userIdMentions.size > 0) { + mMentions.user_ids = Array.from(userIdMentions) + } + if(mentionsRoom) { + mMentions.room = true + } + + newContent["m.mentions"] = mMentions + if (!customHtmlEqualsPlainText(customHtml, plainText)) { newContent.format = 'org.matrix.custom.html'; newContent.formatted_body = customHtml; From 93ae507c0dd67fad52658c59502758b7cf5cd2c6 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Sun, 23 Feb 2025 13:12:02 +0530 Subject: [PATCH 09/10] refactor reusable code and fix todo --- src/app/components/editor/output.ts | 35 ++++++++++- src/app/features/room/RoomInput.tsx | 41 ++++--------- src/app/features/room/message/Message.tsx | 2 +- .../features/room/message/MessageEditor.tsx | 60 +++++++------------ src/app/utils/room.ts | 13 ++++ 5 files changed, 76 insertions(+), 75 deletions(-) diff --git a/src/app/components/editor/output.ts b/src/app/components/editor/output.ts index 256bdbd9e..8d5422c2f 100644 --- a/src/app/components/editor/output.ts +++ b/src/app/components/editor/output.ts @@ -1,8 +1,8 @@ -import { Descendant, Text } from 'slate'; - +import { Descendant, Editor, Text } from 'slate'; +import { MatrixClient } from 'matrix-js-sdk'; import { sanitizeText } from '../../utils/sanitize'; import { BlockType } from './types'; -import { CustomElement } from './slate'; +import { CustomElement, InlineElement } from './slate'; import { parseBlockMD, parseInlineMD, @@ -11,6 +11,7 @@ import { } from '../../plugins/markdown'; import { findAndReplace } from '../../utils/findAndReplace'; import { sanitizeForRegex } from '../../utils/regex'; +import { getCanonicalAliasOrRoomId, isUserId } from '../../utils/matrix'; export type OutputOptions = { allowTextFormatting?: boolean; @@ -195,3 +196,31 @@ export const trimCommand = (cmdName: string, str: string) => { if (!match) return str; return str.slice(match[0].length); }; + +export type MentionsData = { + room: boolean; + users: Set; +}; +export const getMentions = (mx: MatrixClient, roomId: string, editor: Editor): MentionsData => { + const mentionData: MentionsData = { + room: false, + users: new Set(), + }; + + editor.children.forEach((node: Descendant): void => { + if ('type' in node && node.type === BlockType.Paragraph) { + node.children?.forEach((child: InlineElement): void => { + if ('type' in child && child.type === BlockType.Mention) { + const mention = child; + if (mention.id === getCanonicalAliasOrRoomId(mx, roomId)) { + mentionData.room = true; + } else if (isUserId(mention.id) && mention.id !== mx.getUserId()) { + mentionData.users.add(mention.id); + } + } + }); + } + }); + + return mentionData; +}; diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 3366037ba..4d21f4401 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -10,9 +10,9 @@ import React, { } from 'react'; import { useAtom, useAtomValue } from 'jotai'; import { isKeyHotkey } from 'is-hotkey'; -import { EventType, IContent, MsgType, RelationType, Room, IMentions } from 'matrix-js-sdk'; +import { EventType, IContent, MsgType, RelationType, Room } from 'matrix-js-sdk'; import { ReactEditor } from 'slate-react'; -import {Transforms, Editor, Descendant} from 'slate'; +import { Transforms, Editor } from 'slate'; import { Box, Dialog, @@ -53,7 +53,7 @@ import { isEmptyEditor, getBeginCommand, trimCommand, - BlockType, + getMentions, } from '../../components/editor'; import { EmojiBoard, EmojiBoardTab } from '../../components/emoji-board'; import { UseStateProvider } from '../../components/UseStateProvider'; @@ -63,8 +63,6 @@ import { getImageInfo, getMxIdLocalPart, mxcUrlToHttp, - isUserId, - getCanonicalAliasOrRoomId, } from '../../utils/matrix'; import { useTypingStatusUpdater } from '../../hooks/useTypingStatusUpdater'; import { useFilePicker } from '../../hooks/useFilePicker'; @@ -105,6 +103,7 @@ import colorMXID from '../../../util/colorMXID'; import { getAllParents, getMemberDisplayName, + getMentionContent, trimReplyFromBody, } from '../../utils/room'; import { CommandAutocomplete } from './CommandAutocomplete'; @@ -114,7 +113,6 @@ import { useElementSizeObserver } from '../../hooks/useElementSizeObserver'; import { ReplyLayout, ThreadIndicator } from '../../components/message'; import { roomToParentsAtom } from '../../state/room/roomToParents'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; -import { InlineElement } from '../../components/editor/slate'; interface RoomInputProps { editor: Editor; @@ -310,39 +308,20 @@ export const RoomInput = forwardRef( const body = plainText; const formattedBody = customHtml; + const mentionData = getMentions(mx, roomId, editor); const content: IContent = { msgtype: msgType, body, }; - const userIdMentions = new Set(); + if (replyDraft && replyDraft.userId !== mx.getUserId()) { - userIdMentions.add(replyDraft.userId); - } - let mentionsRoom = false; - editor.children.forEach((node: Descendant): void => { - if ("type" in node && node.type === BlockType.Paragraph) { - node.children?.forEach((child: InlineElement): void => { - if ("type" in child && child.type === BlockType.Mention) { - const mention = child; - if (mention.id === getCanonicalAliasOrRoomId(mx, roomId)) { - mentionsRoom = true - } else if (isUserId(mention.id) && mention.id !== mx.getUserId()) { - userIdMentions.add(mention.id) - } - } - }) - } - }) - const mMentions: IMentions = {} - if (userIdMentions.size > 0) { - mMentions.user_ids = Array.from(userIdMentions) - } - if(mentionsRoom) { - mMentions.room = true + mentionData.users.add(replyDraft.userId); } - content["m.mentions"] = mMentions + const mMentions = getMentionContent(Array.from(mentionData.users), mentionData.room); + content['m.mentions'] = mMentions; + if (replyDraft || !customHtmlEqualsPlainText(formattedBody, body)) { content.format = 'org.matrix.custom.html'; content.formatted_body = formattedBody; diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index bdf520594..bde03eb2c 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -35,7 +35,7 @@ import { useHover, useFocusWithin } from 'react-aria'; import { MatrixEvent, Room } from 'matrix-js-sdk'; import { Relations } from 'matrix-js-sdk/lib/models/relations'; import classNames from 'classnames'; -import { EventType, RoomPinnedEventsEventContent } from 'matrix-js-sdk/lib/types'; +import { RoomPinnedEventsEventContent } from 'matrix-js-sdk/lib/types'; import { AvatarBase, BubbleLayout, diff --git a/src/app/features/room/message/MessageEditor.tsx b/src/app/features/room/message/MessageEditor.tsx index 9de647065..dc59dcdf6 100644 --- a/src/app/features/room/message/MessageEditor.tsx +++ b/src/app/features/room/message/MessageEditor.tsx @@ -19,9 +19,9 @@ import { as, config, } from 'folds'; -import {Descendant, Editor, Transforms} from 'slate'; +import { Editor, Transforms } from 'slate'; import { ReactEditor } from 'slate-react'; -import {IContent, IMentions, MatrixEvent, RelationType, Room} from 'matrix-js-sdk'; +import { IContent, IMentions, MatrixEvent, RelationType, Room } from 'matrix-js-sdk'; import { isKeyHotkey } from 'is-hotkey'; import { AUTOCOMPLETE_PREFIXES, @@ -42,7 +42,8 @@ import { toMatrixCustomHTML, toPlainText, trimCustomHtml, - useEditor, BlockType, + useEditor, + getMentions, } from '../../../components/editor'; import { useSetting } from '../../../state/hooks/settings'; import { settingsAtom } from '../../../state/settings'; @@ -50,12 +51,8 @@ import { UseStateProvider } from '../../../components/UseStateProvider'; import { EmojiBoard } from '../../../components/emoji-board'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; -import { getEditedEvent, trimReplyFromFormattedBody } from '../../../utils/room'; +import { getEditedEvent, getMentionContent, trimReplyFromFormattedBody } from '../../../utils/room'; import { mobileOrTablet } from '../../../utils/user-agent'; -import {InlineElement} from "../../../components/editor/slate"; -import {getCanonicalAliasOrRoomId, isUserId} from "../../../utils/matrix"; -import {useAtom} from "jotai/index"; -import {roomIdToReplyDraftAtomFamily} from "../../../state/room/roomInputDrafts"; type MessageEditorProps = { roomId: string; @@ -78,19 +75,23 @@ export const MessageEditor = as<'div', MessageEditorProps>( const getPrevBodyAndFormattedBody = useCallback((): [ string | undefined, - string | undefined + string | undefined, + IMentions | undefined ] => { const evtId = mEvent.getId()!; const evtTimeline = room.getTimelineForEvent(evtId); const editedEvent = evtTimeline && getEditedEvent(evtId, mEvent, evtTimeline.getTimelineSet()); - const { body, formatted_body: customHtml }: Record = - editedEvent?.getContent()['m.new_content'] ?? mEvent.getContent(); + const content: IContent = editedEvent?.getContent()['m.new_content'] ?? mEvent.getContent(); + const { body, formatted_body: customHtml }: Record = content; + + const mMentions: IMentions | undefined = content['m.mentions']; return [ typeof body === 'string' ? body : undefined, typeof customHtml === 'string' ? customHtml : undefined, + mMentions, ]; }, [room, mEvent]); @@ -105,7 +106,7 @@ export const MessageEditor = as<'div', MessageEditorProps>( }) ); - const [prevBody, prevCustomHtml] = getPrevBodyAndFormattedBody(); + const [prevBody, prevCustomHtml, prevMentions] = getPrevBodyAndFormattedBody(); if (plainText === '') return undefined; if (prevBody) { @@ -126,35 +127,14 @@ export const MessageEditor = as<'div', MessageEditorProps>( body: plainText, }; - const userIdMentions = new Set(); - // if (replyDraft && replyDraft.userId !== mx.getUserId()) { - // userIdMentions.add(replyDraft.userId); - // } - // TODO: Get the original message's reply to pick up the mention - let mentionsRoom = false; - editor.children.forEach((node: Descendant): void => { - if ("type" in node && node.type === BlockType.Paragraph) { - node.children?.forEach((child: InlineElement): void => { - if ("type" in child && child.type === BlockType.Mention) { - const mention = child; - if (mention.id === getCanonicalAliasOrRoomId(mx, roomId)) { - mentionsRoom = true - } else if (isUserId(mention.id) && mention.id !== mx.getUserId()) { - userIdMentions.add(mention.id) - } - } - }) - } - }) - const mMentions: IMentions = {} - if (userIdMentions.size > 0) { - mMentions.user_ids = Array.from(userIdMentions) - } - if(mentionsRoom) { - mMentions.room = true - } + const mentionData = getMentions(mx, roomId, editor); + + prevMentions?.user_ids?.forEach((prevMentionId) => { + mentionData.users.add(prevMentionId); + }); - newContent["m.mentions"] = mMentions + const mMentions = getMentionContent(Array.from(mentionData.users), mentionData.room); + newContent['m.mentions'] = mMentions; if (!customHtmlEqualsPlainText(customHtml, plainText)) { newContent.format = 'org.matrix.custom.html'; diff --git a/src/app/utils/room.ts b/src/app/utils/room.ts index 36de44939..3bf8cd5a4 100644 --- a/src/app/utils/room.ts +++ b/src/app/utils/room.ts @@ -4,6 +4,7 @@ import { EventTimeline, EventTimelineSet, EventType, + IMentions, IPushRule, IPushRules, JoinRule, @@ -430,3 +431,15 @@ export const getLatestEditableEvt = ( export const reactionOrEditEvent = (mEvent: MatrixEvent) => mEvent.getRelation()?.rel_type === RelationType.Annotation || mEvent.getRelation()?.rel_type === RelationType.Replace; + +export const getMentionContent = (userIds: string[], room: boolean): IMentions => { + const mMentions: IMentions = {}; + if (userIds.length > 0) { + mMentions.user_ids = userIds; + } + if (room) { + mMentions.room = true; + } + + return mMentions; +}; From f03d00aff14a3cdeac9c49db36b4e6a23dbe7810 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Sun, 23 Feb 2025 13:24:30 +0530 Subject: [PATCH 10/10] parse mentions from all nodes --- src/app/components/editor/output.ts | 33 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/app/components/editor/output.ts b/src/app/components/editor/output.ts index 8d5422c2f..dbdd51f37 100644 --- a/src/app/components/editor/output.ts +++ b/src/app/components/editor/output.ts @@ -2,7 +2,7 @@ import { Descendant, Editor, Text } from 'slate'; import { MatrixClient } from 'matrix-js-sdk'; import { sanitizeText } from '../../utils/sanitize'; import { BlockType } from './types'; -import { CustomElement, InlineElement } from './slate'; +import { CustomElement } from './slate'; import { parseBlockMD, parseInlineMD, @@ -207,20 +207,25 @@ export const getMentions = (mx: MatrixClient, roomId: string, editor: Editor): M users: new Set(), }; - editor.children.forEach((node: Descendant): void => { - if ('type' in node && node.type === BlockType.Paragraph) { - node.children?.forEach((child: InlineElement): void => { - if ('type' in child && child.type === BlockType.Mention) { - const mention = child; - if (mention.id === getCanonicalAliasOrRoomId(mx, roomId)) { - mentionData.room = true; - } else if (isUserId(mention.id) && mention.id !== mx.getUserId()) { - mentionData.users.add(mention.id); - } - } - }); + const parseMentions = (node: Descendant): void => { + if (Text.isText(node)) return; + if (node.type === BlockType.CodeBlock) return; + + if (node.type === BlockType.Mention) { + if (node.id === getCanonicalAliasOrRoomId(mx, roomId)) { + mentionData.room = true; + } + if (isUserId(node.id) && node.id !== mx.getUserId()) { + mentionData.users.add(node.id); + } + + return; } - }); + + node.children.forEach(parseMentions); + }; + + editor.children.forEach(parseMentions); return mentionData; };