mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-02-26 07:03:05 +01:00
Remove fallback replies & implement intentional mentions (#2138)
* Remove reply fallbacks & add m.mentions (WIP) the typing on line 301 and 303 needs fixing but apart from that this is mint * Less jank typing * Mention the reply author in m.mentions * Improve typing * Fix typing in m.mentions finder * 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 * Formatting * Add intentional mentions to edited messages * refactor reusable code and fix todo * parse mentions from all nodes --------- Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
This commit is contained in:
parent
dd4c1a94e6
commit
8d95758ed7
5 changed files with 83 additions and 28 deletions
|
@ -1,5 +1,5 @@
|
|||
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';
|
||||
|
@ -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,36 @@ export const trimCommand = (cmdName: string, str: string) => {
|
|||
if (!match) return str;
|
||||
return str.slice(match[0].length);
|
||||
};
|
||||
|
||||
export type MentionsData = {
|
||||
room: boolean;
|
||||
users: Set<string>;
|
||||
};
|
||||
export const getMentions = (mx: MatrixClient, roomId: string, editor: Editor): MentionsData => {
|
||||
const mentionData: MentionsData = {
|
||||
room: false,
|
||||
users: new Set(),
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -53,6 +53,7 @@ import {
|
|||
isEmptyEditor,
|
||||
getBeginCommand,
|
||||
trimCommand,
|
||||
getMentions,
|
||||
} from '../../components/editor';
|
||||
import { EmojiBoard, EmojiBoardTab } from '../../components/emoji-board';
|
||||
import { UseStateProvider } from '../../components/UseStateProvider';
|
||||
|
@ -102,12 +103,9 @@ import colorMXID from '../../../util/colorMXID';
|
|||
import {
|
||||
getAllParents,
|
||||
getMemberDisplayName,
|
||||
parseReplyBody,
|
||||
parseReplyFormattedBody,
|
||||
getMentionContent,
|
||||
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';
|
||||
|
@ -268,7 +266,6 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
|||
uploadBoardHandlers.current?.handleSend();
|
||||
|
||||
const commandName = getBeginCommand(editor);
|
||||
|
||||
let plainText = toPlainText(editor.children, isMarkdown).trim();
|
||||
let customHtml = trimCustomHtml(
|
||||
toMatrixCustomHTML(editor.children, {
|
||||
|
@ -309,25 +306,22 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
|||
|
||||
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 mentionData = getMentions(mx, roomId, editor);
|
||||
|
||||
const content: IContent = {
|
||||
msgtype: msgType,
|
||||
body,
|
||||
};
|
||||
|
||||
if (replyDraft && replyDraft.userId !== mx.getUserId()) {
|
||||
mentionData.users.add(replyDraft.userId);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
} from 'folds';
|
||||
import { 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,
|
||||
|
@ -43,6 +43,7 @@ import {
|
|||
toPlainText,
|
||||
trimCustomHtml,
|
||||
useEditor,
|
||||
getMentions,
|
||||
} from '../../../components/editor';
|
||||
import { useSetting } from '../../../state/hooks/settings';
|
||||
import { settingsAtom } from '../../../state/settings';
|
||||
|
@ -50,7 +51,7 @@ 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';
|
||||
|
||||
type MessageEditorProps = {
|
||||
|
@ -74,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<string, unknown> =
|
||||
editedEvent?.getContent()['m.new_content'] ?? mEvent.getContent();
|
||||
const content: IContent = editedEvent?.getContent()['m.new_content'] ?? mEvent.getContent();
|
||||
const { body, formatted_body: customHtml }: Record<string, unknown> = content;
|
||||
|
||||
const mMentions: IMentions | undefined = content['m.mentions'];
|
||||
|
||||
return [
|
||||
typeof body === 'string' ? body : undefined,
|
||||
typeof customHtml === 'string' ? customHtml : undefined,
|
||||
mMentions,
|
||||
];
|
||||
}, [room, mEvent]);
|
||||
|
||||
|
@ -101,7 +106,7 @@ export const MessageEditor = as<'div', MessageEditorProps>(
|
|||
})
|
||||
);
|
||||
|
||||
const [prevBody, prevCustomHtml] = getPrevBodyAndFormattedBody();
|
||||
const [prevBody, prevCustomHtml, prevMentions] = getPrevBodyAndFormattedBody();
|
||||
|
||||
if (plainText === '') return undefined;
|
||||
if (prevBody) {
|
||||
|
@ -122,6 +127,15 @@ export const MessageEditor = as<'div', MessageEditorProps>(
|
|||
body: plainText,
|
||||
};
|
||||
|
||||
const mentionData = getMentions(mx, roomId, editor);
|
||||
|
||||
prevMentions?.user_ids?.forEach((prevMentionId) => {
|
||||
mentionData.users.add(prevMentionId);
|
||||
});
|
||||
|
||||
const mMentions = getMentionContent(Array.from(mentionData.users), mentionData.room);
|
||||
newContent['m.mentions'] = mMentions;
|
||||
|
||||
if (!customHtmlEqualsPlainText(customHtml, plainText)) {
|
||||
newContent.format = 'org.matrix.custom.html';
|
||||
newContent.formatted_body = customHtml;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue