diff --git a/src/components/views/rooms/wysiwyg_composer/components/WysiwygAutocomplete.tsx b/src/components/views/rooms/wysiwyg_composer/components/WysiwygAutocomplete.tsx
index 709de5fbbc1..2b4e0f96820 100644
--- a/src/components/views/rooms/wysiwyg_composer/components/WysiwygAutocomplete.tsx
+++ b/src/components/views/rooms/wysiwyg_composer/components/WysiwygAutocomplete.tsx
@@ -59,21 +59,44 @@ const WysiwygAutocomplete = forwardRef(
const client = useMatrixClientContext();
function handleConfirm(completion: ICompletion): void {
- // TODO handle all of the completion types
- // Using this to pick out the ones we can handle during implementation
- if (completion.type === "command") {
- // TODO determine if utils in SlashCommands.tsx are required
-
- // trim the completion as some include trailing spaces, but we always insert a
- // trailing space in the rust model anyway
- handleCommand(completion.completion.trim());
+ if (client === undefined || room === undefined) {
+ return;
}
- if (client && room && completion.href && (completion.type === "room" || completion.type === "user")) {
- handleMention(
- completion.href,
- getMentionDisplayText(completion, client),
- getMentionAttributes(completion, client, room),
- );
+
+ switch (completion.type) {
+ case "command": {
+ // TODO determine if utils in SlashCommands.tsx are required.
+ // Trim the completion as some include trailing spaces, but we always insert a
+ // trailing space in the rust model anyway
+ handleCommand(completion.completion.trim());
+ return;
+ }
+ case "at-room": {
+ // TODO improve handling of at-room to either become a span or use a placeholder href
+ // We have an issue in that we can't use a placeholder because the rust model is always
+ // applying a prefix to the href, so an href of "#" becomes https://# and also we can not
+ // represent a plain span in rust
+ handleMention(
+ window.location.href,
+ getMentionDisplayText(completion, client),
+ getMentionAttributes(completion, client, room),
+ );
+ return;
+ }
+ case "room":
+ case "user": {
+ if (typeof completion.href === "string") {
+ handleMention(
+ completion.href,
+ getMentionDisplayText(completion, client),
+ getMentionAttributes(completion, client, room),
+ );
+ }
+ return;
+ }
+ // TODO - handle "community" type
+ default:
+ return;
}
}
diff --git a/src/components/views/rooms/wysiwyg_composer/utils/autocomplete.ts b/src/components/views/rooms/wysiwyg_composer/utils/autocomplete.ts
index 2bf68f5c767..7f48d8afea6 100644
--- a/src/components/views/rooms/wysiwyg_composer/utils/autocomplete.ts
+++ b/src/components/views/rooms/wysiwyg_composer/utils/autocomplete.ts
@@ -74,7 +74,7 @@ export function getRoomFromCompletion(completion: ICompletion, client: MatrixCli
* @returns the text to display in the mention
*/
export function getMentionDisplayText(completion: ICompletion, client: MatrixClient): string {
- if (completion.type === "user") {
+ if (completion.type === "user" || completion.type === "at-room") {
return completion.completion;
} else if (completion.type === "room") {
// try and get the room and use it's name, if not available, fall back to
@@ -132,7 +132,8 @@ export function getMentionAttributes(completion: ICompletion, client: MatrixClie
"data-mention-type": completion.type,
"style": `--avatar-background: url(${avatarUrl}); --avatar-letter: '${initialLetter}'`,
};
+ } else if (completion.type === "at-room") {
+ return { "data-mention-type": completion.type };
}
-
return {};
}
diff --git a/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx
index 3f9694e2a3c..25f3c8815e8 100644
--- a/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx
+++ b/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx
@@ -158,7 +158,7 @@ describe("WysiwygComposer", () => {
});
});
- describe("Mentions", () => {
+ describe("Mentions and commands", () => {
const dispatchSpy = jest.spyOn(defaultDispatcher, "dispatch");
const mockCompletions: ICompletion[] = [
@@ -181,6 +181,7 @@ describe("WysiwygComposer", () => {
{
// no href user
type: "user",
+ href: undefined,
completion: "user_without_href",
completionId: "@user_3:host.local",
range: { start: 1, end: 1 },
@@ -201,6 +202,24 @@ describe("WysiwygComposer", () => {
range: { start: 1, end: 1 },
component:
room_without_completion_id
,
},
+ {
+ type: "command",
+ completion: "/spoiler",
+ range: { start: 1, end: 1 },
+ component: /spoiler
,
+ },
+ {
+ type: "at-room",
+ completion: "@room",
+ range: { start: 1, end: 1 },
+ component: @room
,
+ },
+ {
+ type: "community",
+ completion: "community-completion",
+ range: { start: 1, end: 1 },
+ component: community
,
+ },
];
const constructMockProvider = (data: ICompletion[]) =>
@@ -211,9 +230,10 @@ describe("WysiwygComposer", () => {
} as unknown as AutocompleteProvider);
// for each test we will insert input simulating a user mention
+ const initialInput = "@abc";
const insertMentionInput = async () => {
fireEvent.input(screen.getByRole("textbox"), {
- data: "@abc",
+ data: initialInput,
inputType: "insertText",
});
@@ -349,6 +369,36 @@ describe("WysiwygComposer", () => {
// check that it has inserted a link and falls back to the completion text
expect(screen.getByRole("link", { name: "#room_without_completion_id" })).toBeInTheDocument();
});
+
+ it("selecting a command inserts the command", async () => {
+ await insertMentionInput();
+
+ // select the room suggestion
+ await userEvent.click(screen.getByText("/spoiler"));
+
+ // check that it has inserted the plain text
+ expect(screen.getByText("/spoiler")).toBeInTheDocument();
+ });
+
+ it("selecting an at-room completion inserts @room", async () => {
+ await insertMentionInput();
+
+ // select the room suggestion
+ await userEvent.click(screen.getByText("@room"));
+
+ // check that it has inserted the @room link
+ expect(screen.getByRole("link", { name: "@room" })).toBeInTheDocument();
+ });
+
+ it("allows a community completion to pass through", async () => {
+ await insertMentionInput();
+
+ // select the room suggestion
+ await userEvent.click(screen.getByText("community"));
+
+ // check that it we still have the initial text
+ expect(screen.getByText(initialInput)).toBeInTheDocument();
+ });
});
describe("When settings require Ctrl+Enter to send", () => {
diff --git a/test/components/views/rooms/wysiwyg_composer/utils/autocomplete-test.ts b/test/components/views/rooms/wysiwyg_composer/utils/autocomplete-test.ts
index 7629a5a2913..366375380c3 100644
--- a/test/components/views/rooms/wysiwyg_composer/utils/autocomplete-test.ts
+++ b/test/components/views/rooms/wysiwyg_composer/utils/autocomplete-test.ts
@@ -100,8 +100,8 @@ describe("getRoomFromCompletion", () => {
});
describe("getMentionDisplayText", () => {
- it("returns an empty string if we are not handling a user or a room type", () => {
- const nonHandledCompletionTypes = ["at-room", "community", "command"] as const;
+ it("returns an empty string if we are not handling a user, room or at-room type", () => {
+ const nonHandledCompletionTypes = ["community", "command"] as const;
const nonHandledCompletions = nonHandledCompletionTypes.map((type) => createMockCompletion({ type }));
nonHandledCompletions.forEach((completion) => {
@@ -131,12 +131,18 @@ describe("getMentionDisplayText", () => {
// as this uses the mockClient, the name will be the mock room name returned from there
expect(getMentionDisplayText(userCompletion, mockClient)).toBe(testCompletion);
});
+
+ it("returns the completion if we are handling an at-room completion", () => {
+ const testCompletion = "display this";
+ const atRoomCompletion = createMockCompletion({ type: "at-room", completion: testCompletion });
+
+ expect(getMentionDisplayText(atRoomCompletion, mockClient)).toBe(testCompletion);
+ });
});
describe("getMentionAttributes", () => {
- // TODO handle all completion types
- it("returns an empty object for completion types other than room or user", () => {
- const nonHandledCompletionTypes = ["at-room", "community", "command"] as const;
+ it("returns an empty object for completion types other than room, user or at-room", () => {
+ const nonHandledCompletionTypes = ["community", "command"] as const;
const nonHandledCompletions = nonHandledCompletionTypes.map((type) => createMockCompletion({ type }));
nonHandledCompletions.forEach((completion) => {
@@ -218,4 +224,14 @@ describe("getMentionAttributes", () => {
});
});
});
+
+ describe("at-room mentions", () => {
+ it("returns expected attributes", () => {
+ const atRoomCompletion = createMockCompletion({ type: "at-room" });
+
+ const result = getMentionAttributes(atRoomCompletion, mockClient, mockRoom);
+
+ expect(result).toEqual({ "data-mention-type": "at-room" });
+ });
+ });
});