Move some message utils out to their own file (#31035)
* Move some message utils out to their own file In another attempt at import cycle breaking * Also add the file * Move tests
This commit is contained in:
@@ -53,7 +53,7 @@ import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
||||
import UploadFailureDialog from "./components/views/dialogs/UploadFailureDialog";
|
||||
import UploadConfirmDialog from "./components/views/dialogs/UploadConfirmDialog";
|
||||
import { createThumbnail } from "./utils/image-media";
|
||||
import { attachMentions, attachRelation } from "./components/views/rooms/SendMessageComposer";
|
||||
import { attachMentions, attachRelation } from "./utils/messages.ts";
|
||||
import { doMaybeLocalRoomAction } from "./utils/local-room";
|
||||
import { SdkContextClass } from "./contexts/SDKContext";
|
||||
import { blobIsAnimated } from "./utils/Image.ts";
|
||||
|
||||
@@ -43,7 +43,7 @@ import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
||||
import { PosthogAnalytics } from "../../../PosthogAnalytics";
|
||||
import { editorRoomKey, editorStateKey } from "../../../Editing";
|
||||
import type DocumentOffset from "../../../editor/offset";
|
||||
import { attachMentions, attachRelation } from "./SendMessageComposer";
|
||||
import { attachMentions, attachRelation } from "../../../utils/messages";
|
||||
import { filterBoolean } from "../../../utils/arrays";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
|
||||
|
||||
@@ -8,10 +8,8 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React, { createRef, type KeyboardEvent, type SyntheticEvent } from "react";
|
||||
import {
|
||||
type IContent,
|
||||
type MatrixEvent,
|
||||
type IEventRelation,
|
||||
type IMentions,
|
||||
type Room,
|
||||
EventType,
|
||||
MsgType,
|
||||
@@ -35,7 +33,7 @@ import {
|
||||
unescapeMessage,
|
||||
} from "../../../editor/serialize";
|
||||
import BasicMessageComposer, { REGEX_EMOTICON } from "./BasicMessageComposer";
|
||||
import { CommandPartCreator, type Part, type PartCreator, type SerializedPart, Type } from "../../../editor/parts";
|
||||
import { CommandPartCreator, type Part, type PartCreator, type SerializedPart } from "../../../editor/parts";
|
||||
import { findEditableEvent } from "../../../utils/EventUtils";
|
||||
import SendHistoryManager from "../../../SendHistoryManager";
|
||||
import { CommandCategories } from "../../../SlashCommands";
|
||||
@@ -61,108 +59,11 @@ import { type Caret } from "../../../editor/caret";
|
||||
import { type IDiff } from "../../../editor/diff";
|
||||
import { getBlobSafeMimeType } from "../../../utils/blobs";
|
||||
import { EMOJI_REGEX } from "../../../HtmlUtils";
|
||||
import { attachMentions, attachRelation } from "../../../utils/messages";
|
||||
|
||||
// The prefix used when persisting editor drafts to localstorage.
|
||||
export const EDITOR_STATE_STORAGE_PREFIX = "mx_cider_state_";
|
||||
|
||||
/**
|
||||
* Build the mentions information based on the editor model (and any related events):
|
||||
*
|
||||
* 1. Search the model parts for room or user pills and fill in the mentions object.
|
||||
* 2. If this is a reply to another event, include any user mentions from that
|
||||
* (but do not include a room mention).
|
||||
*
|
||||
* @param sender - The Matrix ID of the user sending the event.
|
||||
* @param content - The event content.
|
||||
* @param model - The editor model to search for mentions, null if there is no editor.
|
||||
* @param replyToEvent - The event being replied to or undefined if it is not a reply.
|
||||
* @param editedContent - The content of the parent event being edited.
|
||||
*/
|
||||
export function attachMentions(
|
||||
sender: string,
|
||||
content: IContent,
|
||||
model: EditorModel | null,
|
||||
replyToEvent: MatrixEvent | undefined,
|
||||
editedContent: IContent | null = null,
|
||||
): void {
|
||||
// We always attach the mentions even if the home server doesn't yet support
|
||||
// intentional mentions. This is safe because m.mentions is an additive change
|
||||
// that should simply be ignored by incapable home servers.
|
||||
|
||||
// The mentions property *always* gets included to disable legacy push rules.
|
||||
const mentions: IMentions = (content["m.mentions"] = {});
|
||||
|
||||
const userMentions = new Set<string>();
|
||||
let roomMention = false;
|
||||
|
||||
// If there's a reply, initialize the mentioned users as the sender of that event.
|
||||
if (replyToEvent) {
|
||||
userMentions.add(replyToEvent.sender!.userId);
|
||||
}
|
||||
|
||||
// If user provided content is available, check to see if any users are mentioned.
|
||||
if (model) {
|
||||
// Add any mentioned users in the current content.
|
||||
for (const part of model.parts) {
|
||||
if (part.type === Type.UserPill) {
|
||||
userMentions.add(part.resourceId);
|
||||
} else if (part.type === Type.AtRoomPill) {
|
||||
roomMention = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the *current* user isn't listed in the mentioned users.
|
||||
userMentions.delete(sender);
|
||||
|
||||
// Finally, if this event is editing a previous event, only include users who
|
||||
// were not previously mentioned and a room mention if the previous event was
|
||||
// not a room mention.
|
||||
if (editedContent) {
|
||||
// First, the new event content gets the *full* set of users.
|
||||
const newContent = content["m.new_content"];
|
||||
const newMentions: IMentions = (newContent["m.mentions"] = {});
|
||||
|
||||
// Only include the users/room if there is any content.
|
||||
if (userMentions.size) {
|
||||
newMentions.user_ids = [...userMentions];
|
||||
}
|
||||
if (roomMention) {
|
||||
newMentions.room = true;
|
||||
}
|
||||
|
||||
// Fetch the mentions from the original event and remove any previously
|
||||
// mentioned users.
|
||||
const prevMentions = editedContent["m.mentions"];
|
||||
if (Array.isArray(prevMentions?.user_ids)) {
|
||||
prevMentions!.user_ids.forEach((userId) => userMentions.delete(userId));
|
||||
}
|
||||
|
||||
// If the original event mentioned the room, nothing to do here.
|
||||
if (prevMentions?.room) {
|
||||
roomMention = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Only include the users/room if there is any content.
|
||||
if (userMentions.size) {
|
||||
mentions.user_ids = [...userMentions];
|
||||
}
|
||||
if (roomMention) {
|
||||
mentions.room = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Merges favouring the given relation
|
||||
export function attachRelation(content: IContent, relation?: IEventRelation): void {
|
||||
if (relation) {
|
||||
content["m.relates_to"] = {
|
||||
...(content["m.relates_to"] || {}),
|
||||
...relation,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// exported for tests
|
||||
export function createMessageContent(
|
||||
sender: string,
|
||||
|
||||
@@ -29,7 +29,7 @@ import InlineSpinner from "../elements/InlineSpinner";
|
||||
import { PlaybackManager } from "../../../audio/PlaybackManager";
|
||||
import { doMaybeLocalRoomAction } from "../../../utils/local-room";
|
||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import { attachMentions, attachRelation } from "./SendMessageComposer";
|
||||
import { attachMentions, attachRelation } from "../../../utils/messages";
|
||||
import { addReplyToMessageContent } from "../../../utils/Reply";
|
||||
import RoomContext from "../../../contexts/RoomContext";
|
||||
import { type IUpload, type VoiceMessageRecording } from "../../../audio/VoiceMessageRecording";
|
||||
|
||||
@@ -33,7 +33,7 @@ import { CommandCategories, getCommand } from "../../../../../SlashCommands";
|
||||
import { runSlashCommand, shouldSendAnyway } from "../../../../../editor/commands";
|
||||
import { Action } from "../../../../../dispatcher/actions";
|
||||
import { addReplyToMessageContent } from "../../../../../utils/Reply";
|
||||
import { attachRelation } from "../../SendMessageComposer";
|
||||
import { attachRelation } from "../../../../../utils/messages";
|
||||
|
||||
export interface SendMessageParams {
|
||||
mxClient: MatrixClient;
|
||||
|
||||
109
src/utils/messages.ts
Normal file
109
src/utils/messages.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type MatrixEvent, type IContent, type IMentions, type IEventRelation } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import type EditorModel from "../editor/model";
|
||||
import { Type } from "../editor/parts";
|
||||
|
||||
/**
|
||||
* Build the mentions information based on the editor model (and any related events):
|
||||
*
|
||||
* 1. Search the model parts for room or user pills and fill in the mentions object.
|
||||
* 2. If this is a reply to another event, include any user mentions from that
|
||||
* (but do not include a room mention).
|
||||
*
|
||||
* @param sender - The Matrix ID of the user sending the event.
|
||||
* @param content - The event content.
|
||||
* @param model - The editor model to search for mentions, null if there is no editor.
|
||||
* @param replyToEvent - The event being replied to or undefined if it is not a reply.
|
||||
* @param editedContent - The content of the parent event being edited.
|
||||
*/
|
||||
export function attachMentions(
|
||||
sender: string,
|
||||
content: IContent,
|
||||
model: EditorModel | null,
|
||||
replyToEvent: MatrixEvent | undefined,
|
||||
editedContent: IContent | null = null,
|
||||
): void {
|
||||
// We always attach the mentions even if the home server doesn't yet support
|
||||
// intentional mentions. This is safe because m.mentions is an additive change
|
||||
// that should simply be ignored by incapable home servers.
|
||||
|
||||
// The mentions property *always* gets included to disable legacy push rules.
|
||||
const mentions: IMentions = (content["m.mentions"] = {});
|
||||
|
||||
const userMentions = new Set<string>();
|
||||
let roomMention = false;
|
||||
|
||||
// If there's a reply, initialize the mentioned users as the sender of that event.
|
||||
if (replyToEvent) {
|
||||
userMentions.add(replyToEvent.sender!.userId);
|
||||
}
|
||||
|
||||
// If user provided content is available, check to see if any users are mentioned.
|
||||
if (model) {
|
||||
// Add any mentioned users in the current content.
|
||||
for (const part of model.parts) {
|
||||
if (part.type === Type.UserPill) {
|
||||
userMentions.add(part.resourceId);
|
||||
} else if (part.type === Type.AtRoomPill) {
|
||||
roomMention = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the *current* user isn't listed in the mentioned users.
|
||||
userMentions.delete(sender);
|
||||
|
||||
// Finally, if this event is editing a previous event, only include users who
|
||||
// were not previously mentioned and a room mention if the previous event was
|
||||
// not a room mention.
|
||||
if (editedContent) {
|
||||
// First, the new event content gets the *full* set of users.
|
||||
const newContent = content["m.new_content"];
|
||||
const newMentions: IMentions = (newContent["m.mentions"] = {});
|
||||
|
||||
// Only include the users/room if there is any content.
|
||||
if (userMentions.size) {
|
||||
newMentions.user_ids = [...userMentions];
|
||||
}
|
||||
if (roomMention) {
|
||||
newMentions.room = true;
|
||||
}
|
||||
|
||||
// Fetch the mentions from the original event and remove any previously
|
||||
// mentioned users.
|
||||
const prevMentions = editedContent["m.mentions"];
|
||||
if (Array.isArray(prevMentions?.user_ids)) {
|
||||
prevMentions!.user_ids.forEach((userId) => userMentions.delete(userId));
|
||||
}
|
||||
|
||||
// If the original event mentioned the room, nothing to do here.
|
||||
if (prevMentions?.room) {
|
||||
roomMention = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Only include the users/room if there is any content.
|
||||
if (userMentions.size) {
|
||||
mentions.user_ids = [...userMentions];
|
||||
}
|
||||
if (roomMention) {
|
||||
mentions.room = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Merges favouring the given relation
|
||||
export function attachRelation(content: IContent, relation?: IEventRelation): void {
|
||||
if (relation) {
|
||||
content["m.relates_to"] = {
|
||||
...(content["m.relates_to"] || {}),
|
||||
...relation,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,11 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, render, waitFor } from "jest-matrix-react";
|
||||
import { type IContent, type MatrixClient, MsgType } from "matrix-js-sdk/src/matrix";
|
||||
import { type MatrixClient, MsgType } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import SendMessageComposer, {
|
||||
attachMentions,
|
||||
createMessageContent,
|
||||
isQuickReaction,
|
||||
} from "../../../../../src/components/views/rooms/SendMessageComposer";
|
||||
@@ -157,186 +156,6 @@ describe("<SendMessageComposer/>", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("attachMentions", () => {
|
||||
const partsCreator = createPartCreator();
|
||||
|
||||
it("no mentions", () => {
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
});
|
||||
});
|
||||
|
||||
it("test user mentions", () => {
|
||||
const model = new EditorModel([partsCreator.userPill("Bob", "@bob:test")], partsCreator);
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { user_ids: ["@bob:test"] },
|
||||
});
|
||||
});
|
||||
|
||||
it("test reply", () => {
|
||||
// Replying to an event adds the sender to the list of mentioned users.
|
||||
const model = new EditorModel([], partsCreator);
|
||||
let replyToEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: "@bob:test",
|
||||
room: "!abc:test",
|
||||
content: { "m.mentions": {} },
|
||||
event: true,
|
||||
});
|
||||
let content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, replyToEvent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { user_ids: ["@bob:test"] },
|
||||
});
|
||||
|
||||
// It no longer adds any other mentioned users
|
||||
replyToEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: "@bob:test",
|
||||
room: "!abc:test",
|
||||
content: { "m.mentions": { user_ids: ["@alice:test", "@charlie:test"] } },
|
||||
event: true,
|
||||
});
|
||||
content = {};
|
||||
attachMentions("@alice:test", content, model, replyToEvent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { user_ids: ["@bob:test"] },
|
||||
});
|
||||
});
|
||||
|
||||
it("test room mention", () => {
|
||||
const model = new EditorModel([partsCreator.atRoomPill("@room")], partsCreator);
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { room: true },
|
||||
});
|
||||
});
|
||||
|
||||
it("test reply to room mention", () => {
|
||||
// Replying to a room mention shouldn't automatically be a room mention.
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const replyToEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: "@alice:test",
|
||||
room: "!abc:test",
|
||||
content: { "m.mentions": { room: true } },
|
||||
event: true,
|
||||
});
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, replyToEvent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
});
|
||||
});
|
||||
|
||||
it("test broken mentions", () => {
|
||||
// Replying to a room mention shouldn't automatically be a room mention.
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const replyToEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: "@alice:test",
|
||||
room: "!abc:test",
|
||||
// @ts-ignore - Purposefully testing invalid data.
|
||||
content: { "m.mentions": { user_ids: "@bob:test" } },
|
||||
event: true,
|
||||
});
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, replyToEvent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
});
|
||||
});
|
||||
|
||||
describe("attachMentions with edit", () => {
|
||||
it("no mentions", () => {
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": {} },
|
||||
});
|
||||
});
|
||||
|
||||
it("mentions do not propagate", () => {
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = {
|
||||
"m.mentions": { user_ids: ["@bob:test"], room: true },
|
||||
};
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": {} },
|
||||
});
|
||||
});
|
||||
|
||||
it("test user mentions", () => {
|
||||
const model = new EditorModel([partsCreator.userPill("Bob", "@bob:test")], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { user_ids: ["@bob:test"] },
|
||||
"m.new_content": { "m.mentions": { user_ids: ["@bob:test"] } },
|
||||
});
|
||||
});
|
||||
|
||||
it("test prev user mentions", () => {
|
||||
const model = new EditorModel([partsCreator.userPill("Bob", "@bob:test")], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = { "m.mentions": { user_ids: ["@bob:test"] } };
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": { user_ids: ["@bob:test"] } },
|
||||
});
|
||||
});
|
||||
|
||||
it("test room mention", () => {
|
||||
const model = new EditorModel([partsCreator.atRoomPill("@room")], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { room: true },
|
||||
"m.new_content": { "m.mentions": { room: true } },
|
||||
});
|
||||
});
|
||||
|
||||
it("test prev room mention", () => {
|
||||
const model = new EditorModel([partsCreator.atRoomPill("@room")], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = { "m.mentions": { room: true } };
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": { room: true } },
|
||||
});
|
||||
});
|
||||
|
||||
it("test broken mentions", () => {
|
||||
// Replying to a room mention shouldn't automatically be a room mention.
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
// @ts-ignore - Purposefully testing invalid data.
|
||||
const prevContent: IContent = { "m.mentions": { user_ids: "@bob:test" } };
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": {} },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("functions correctly mounted", () => {
|
||||
const mockClient = createTestClient();
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
|
||||
|
||||
194
test/unit-tests/utils/messages-test.ts
Normal file
194
test/unit-tests/utils/messages-test.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type IContent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { attachMentions } from "../../../src/utils/messages";
|
||||
import EditorModel from "../../../src/editor/model";
|
||||
import { mkEvent } from "../../test-utils";
|
||||
import { createPartCreator } from "../editor/mock";
|
||||
|
||||
describe("attachMentions", () => {
|
||||
const partsCreator = createPartCreator();
|
||||
|
||||
it("no mentions", () => {
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
});
|
||||
});
|
||||
|
||||
it("test user mentions", () => {
|
||||
const model = new EditorModel([partsCreator.userPill("Bob", "@bob:test")], partsCreator);
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { user_ids: ["@bob:test"] },
|
||||
});
|
||||
});
|
||||
|
||||
it("test reply", () => {
|
||||
// Replying to an event adds the sender to the list of mentioned users.
|
||||
const model = new EditorModel([], partsCreator);
|
||||
let replyToEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: "@bob:test",
|
||||
room: "!abc:test",
|
||||
content: { "m.mentions": {} },
|
||||
event: true,
|
||||
});
|
||||
let content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, replyToEvent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { user_ids: ["@bob:test"] },
|
||||
});
|
||||
|
||||
// It no longer adds any other mentioned users
|
||||
replyToEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: "@bob:test",
|
||||
room: "!abc:test",
|
||||
content: { "m.mentions": { user_ids: ["@alice:test", "@charlie:test"] } },
|
||||
event: true,
|
||||
});
|
||||
content = {};
|
||||
attachMentions("@alice:test", content, model, replyToEvent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { user_ids: ["@bob:test"] },
|
||||
});
|
||||
});
|
||||
|
||||
it("test room mention", () => {
|
||||
const model = new EditorModel([partsCreator.atRoomPill("@room")], partsCreator);
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { room: true },
|
||||
});
|
||||
});
|
||||
|
||||
it("test reply to room mention", () => {
|
||||
// Replying to a room mention shouldn't automatically be a room mention.
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const replyToEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: "@alice:test",
|
||||
room: "!abc:test",
|
||||
content: { "m.mentions": { room: true } },
|
||||
event: true,
|
||||
});
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, replyToEvent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
});
|
||||
});
|
||||
|
||||
it("test broken mentions", () => {
|
||||
// Replying to a room mention shouldn't automatically be a room mention.
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const replyToEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: "@alice:test",
|
||||
room: "!abc:test",
|
||||
// @ts-ignore - Purposefully testing invalid data.
|
||||
content: { "m.mentions": { user_ids: "@bob:test" } },
|
||||
event: true,
|
||||
});
|
||||
const content: IContent = {};
|
||||
attachMentions("@alice:test", content, model, replyToEvent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
});
|
||||
});
|
||||
|
||||
describe("attachMentions with edit", () => {
|
||||
it("no mentions", () => {
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": {} },
|
||||
});
|
||||
});
|
||||
|
||||
it("mentions do not propagate", () => {
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = {
|
||||
"m.mentions": { user_ids: ["@bob:test"], room: true },
|
||||
};
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": {} },
|
||||
});
|
||||
});
|
||||
|
||||
it("test user mentions", () => {
|
||||
const model = new EditorModel([partsCreator.userPill("Bob", "@bob:test")], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { user_ids: ["@bob:test"] },
|
||||
"m.new_content": { "m.mentions": { user_ids: ["@bob:test"] } },
|
||||
});
|
||||
});
|
||||
|
||||
it("test prev user mentions", () => {
|
||||
const model = new EditorModel([partsCreator.userPill("Bob", "@bob:test")], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = { "m.mentions": { user_ids: ["@bob:test"] } };
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": { user_ids: ["@bob:test"] } },
|
||||
});
|
||||
});
|
||||
|
||||
it("test room mention", () => {
|
||||
const model = new EditorModel([partsCreator.atRoomPill("@room")], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = {};
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": { room: true },
|
||||
"m.new_content": { "m.mentions": { room: true } },
|
||||
});
|
||||
});
|
||||
|
||||
it("test prev room mention", () => {
|
||||
const model = new EditorModel([partsCreator.atRoomPill("@room")], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
const prevContent: IContent = { "m.mentions": { room: true } };
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": { room: true } },
|
||||
});
|
||||
});
|
||||
|
||||
it("test broken mentions", () => {
|
||||
// Replying to a room mention shouldn't automatically be a room mention.
|
||||
const model = new EditorModel([], partsCreator);
|
||||
const content: IContent = { "m.new_content": {} };
|
||||
// @ts-ignore - Purposefully testing invalid data.
|
||||
const prevContent: IContent = { "m.mentions": { user_ids: "@bob:test" } };
|
||||
attachMentions("@alice:test", content, model, undefined, prevContent);
|
||||
expect(content).toEqual({
|
||||
"m.mentions": {},
|
||||
"m.new_content": { "m.mentions": {} },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user