Recalculate mentions metadata of forwarded messages based on message body (#31193)
* recalculate mentions of forwarded messages In transformEvent(), parse event body back into an EditorModel, and pass this into attachMentions(), so that it actually recalculates mentions. * refactor ForwardDialog-test.tsx Refactor test for stripping mentions on forwards to allow for more tests of mention recalculation * add test to recalculate mention pills Fails due to not mocking room membership * fix lint * fix test: add ability to provide source room stub
This commit is contained in:
@@ -58,6 +58,10 @@ import { getKeyBindingsManager } from "../../../KeyBindingsManager";
|
|||||||
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
||||||
import { OverflowTileView } from "../rooms/OverflowTileView";
|
import { OverflowTileView } from "../rooms/OverflowTileView";
|
||||||
import { attachMentions } from "../../../utils/messages";
|
import { attachMentions } from "../../../utils/messages";
|
||||||
|
import { CommandPartCreator } from "../../../editor/parts";
|
||||||
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
import { parseEvent } from "../../../editor/deserialize";
|
||||||
|
import EditorModel from "../../../editor/model";
|
||||||
|
|
||||||
const AVATAR_SIZE = 30;
|
const AVATAR_SIZE = 30;
|
||||||
|
|
||||||
@@ -184,13 +188,13 @@ const Entry: React.FC<IEntryProps<any>> = ({ room, type, content, matrixClient:
|
|||||||
* 1. Strip all relations.
|
* 1. Strip all relations.
|
||||||
* 2. Convert location events into a static pin-drop location share,
|
* 2. Convert location events into a static pin-drop location share,
|
||||||
* and remove description from self-location shares.
|
* and remove description from self-location shares.
|
||||||
* 3. Pass through attachMentions() to strip mentions (as no EditorModel is present to recalculate from).
|
* 3. Parse the event back into an EditorModel and recalculate mentions.
|
||||||
*
|
*
|
||||||
* @param event - The MatrixEvent to transform.
|
* @param event - The MatrixEvent to transform.
|
||||||
* @param userId - Current user MXID (passed through to attachMentions()).
|
* @param cli - The MatrixClient (used for recalculation of mentions).
|
||||||
* @returns The transformed event type and content.
|
* @returns The transformed event type and content.
|
||||||
*/
|
*/
|
||||||
const transformEvent = (event: MatrixEvent, userId: string): { type: string; content: IContent } => {
|
const transformEvent = (event: MatrixEvent, cli: MatrixClient): { type: string; content: IContent } => {
|
||||||
const {
|
const {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
"m.relates_to": _, // strip relations - in future we will attach a relation pointing at the original event
|
"m.relates_to": _, // strip relations - in future we will attach a relation pointing at the original event
|
||||||
@@ -225,12 +229,17 @@ const transformEvent = (event: MatrixEvent, userId: string): { type: string; con
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mentions can leak information about the context of the original message,
|
// Mentions can leak information about the context of the original message, so:
|
||||||
// so pass through attachMentions() to recalculate mentions.
|
// 1. Parse the event's message body back into an EditorModel, then
|
||||||
// Currently, this strips all mentions (forces an empty m.mentions),
|
// 2. Pass through attachMentions() to recalculate mentions.
|
||||||
// as there is no EditorModel to parse pills from.
|
const room = cli.getRoom(event.getRoomId())!;
|
||||||
// Future improvements could actually recalculate mentions based on the message body.
|
const partCreator = new CommandPartCreator(room, cli);
|
||||||
attachMentions(userId, content, null, undefined);
|
const parts = parseEvent(event, partCreator, {
|
||||||
|
shouldEscape: SettingsStore.getValue("MessageComposerInput.useMarkdown"),
|
||||||
|
});
|
||||||
|
const model = new EditorModel(parts, partCreator); // Temporary EditorModel to pass through
|
||||||
|
const userId = cli.getSafeUserId();
|
||||||
|
attachMentions(userId, content, model, undefined);
|
||||||
|
|
||||||
return { type, content };
|
return { type, content };
|
||||||
};
|
};
|
||||||
@@ -242,7 +251,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
|
|||||||
cli.getProfileInfo(userId).then((info) => setProfileInfo(info));
|
cli.getProfileInfo(userId).then((info) => setProfileInfo(info));
|
||||||
}, [cli, userId]);
|
}, [cli, userId]);
|
||||||
|
|
||||||
const { type, content } = transformEvent(event, userId);
|
const { type, content } = transformEvent(event, cli);
|
||||||
|
|
||||||
// For the message preview we fake the sender as ourselves
|
// For the message preview we fake the sender as ourselves
|
||||||
const mockEvent = new MatrixEvent({
|
const mockEvent = new MatrixEvent({
|
||||||
|
|||||||
@@ -78,9 +78,17 @@ describe("ForwardDialog", () => {
|
|||||||
});
|
});
|
||||||
const defaultRooms = ["a", "A", "b"].map((name) => mkStubRoom(name, name, mockClient));
|
const defaultRooms = ["a", "A", "b"].map((name) => mkStubRoom(name, name, mockClient));
|
||||||
|
|
||||||
const mountForwardDialog = (message = defaultMessage, rooms = defaultRooms) => {
|
const mountForwardDialog = (message = defaultMessage, rooms = defaultRooms, stubSource = false) => {
|
||||||
mockClient.getVisibleRooms.mockReturnValue(rooms);
|
mockClient.getVisibleRooms.mockReturnValue(rooms);
|
||||||
mockClient.getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId) || null);
|
|
||||||
|
const sourceRoomStub = mkStubRoom(sourceRoom, sourceRoom, mockClient);
|
||||||
|
|
||||||
|
mockClient.getRoom.mockImplementation((roomId) => {
|
||||||
|
if (stubSource && roomId === sourceRoom) {
|
||||||
|
return sourceRoomStub; // Return the source room stub, if enabled
|
||||||
|
}
|
||||||
|
return rooms.find((room) => room.roomId === roomId) || null;
|
||||||
|
});
|
||||||
|
|
||||||
const wrapper: RenderResult = render(
|
const wrapper: RenderResult = render(
|
||||||
<ForwardDialog
|
<ForwardDialog
|
||||||
@@ -249,34 +257,55 @@ describe("ForwardDialog", () => {
|
|||||||
expect(secondButton.getAttribute("aria-disabled")).toBeFalsy();
|
expect(secondButton.getAttribute("aria-disabled")).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("strips mentions from forwarded messages", async () => {
|
describe("Mention recalculation", () => {
|
||||||
const messageWithMention = mkEvent({
|
|
||||||
type: "m.room.message",
|
|
||||||
room: sourceRoom,
|
|
||||||
user: "@bob:example.org",
|
|
||||||
content: {
|
|
||||||
"msgtype": "m.text",
|
|
||||||
"body": "Hi @alice:example.org",
|
|
||||||
"m.mentions": {
|
|
||||||
user_ids: ["@alice:example.org"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
event: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { container } = mountForwardDialog(messageWithMention);
|
|
||||||
const roomId = "a";
|
const roomId = "a";
|
||||||
|
const sendClick = (container: HTMLElement): void =>
|
||||||
|
act(() => {
|
||||||
|
const sendButton = container.querySelector(".mx_ForwardList_sendButton");
|
||||||
|
fireEvent.click(sendButton!);
|
||||||
|
});
|
||||||
|
const makeMessage = (body: string, mentions: object, formattedBody?: string) => {
|
||||||
|
return mkEvent({
|
||||||
|
type: "m.room.message",
|
||||||
|
room: sourceRoom,
|
||||||
|
user: "@bob:example.org",
|
||||||
|
content: {
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"body": body,
|
||||||
|
"m.mentions": mentions,
|
||||||
|
...(formattedBody && {
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: formattedBody,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
event: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Click the send button.
|
it("strips extra mentions", async () => {
|
||||||
act(() => {
|
const message = makeMessage("Hi Alice", { user_ids: [aliceId] });
|
||||||
const sendButton = container.querySelector(".mx_ForwardList_sendButton");
|
const { container } = mountForwardDialog(message);
|
||||||
fireEvent.click(sendButton!);
|
sendClick(container);
|
||||||
|
// Expected content should have mentions empty.
|
||||||
|
expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, message.getType(), {
|
||||||
|
...message.getContent(),
|
||||||
|
"m.mentions": {},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Expected content should have mentions empty.
|
it("recalculates mention pills", async () => {
|
||||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, messageWithMention.getType(), {
|
const message = makeMessage(
|
||||||
...messageWithMention.getContent(),
|
"Hi Alice",
|
||||||
"m.mentions": {},
|
{ user_ids: [aliceId] },
|
||||||
|
`Hi <a href="https://matrix.to/#/${aliceId}">Alice</a>`,
|
||||||
|
);
|
||||||
|
const { container } = mountForwardDialog(message, defaultRooms, true);
|
||||||
|
sendClick(container);
|
||||||
|
// Expected content should have mentions empty.
|
||||||
|
expect(mockClient.sendEvent).toHaveBeenCalledWith(roomId, message.getType(), {
|
||||||
|
...message.getContent(),
|
||||||
|
"m.mentions": { user_ids: [aliceId] },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user