Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 4d57440

Browse files
authored
Handle /me in rte (#10558)
* add /me handling * use typeguards to avoid fighting TS * improve clarity and use of typeguards * add createMessageContent tests * remove completed TODO * improve comments * remove duplication and renaming of argument
1 parent 7b5d180 commit 4d57440

File tree

3 files changed

+45
-18
lines changed

3 files changed

+45
-18
lines changed

src/components/views/rooms/wysiwyg_composer/utils/createMessageContent.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import SettingsStore from "../../../../../settings/SettingsStore";
2121
import { RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks";
2222
import { addReplyToMessageContent } from "../../../../../utils/Reply";
2323

24+
export const EMOTE_PREFIX = "/me ";
25+
2426
// Merges favouring the given relation
2527
function attachRelation(content: IContent, relation?: IEventRelation): void {
2628
if (relation) {
@@ -61,6 +63,8 @@ interface CreateMessageContentParams {
6163
editedEvent?: MatrixEvent;
6264
}
6365

66+
const isMatrixEvent = (e: MatrixEvent | undefined): e is MatrixEvent => e instanceof MatrixEvent;
67+
6468
export async function createMessageContent(
6569
message: string,
6670
isHTML: boolean,
@@ -72,22 +76,22 @@ export async function createMessageContent(
7276
editedEvent,
7377
}: CreateMessageContentParams,
7478
): Promise<IContent> {
75-
// TODO emote ?
76-
77-
const isEditing = Boolean(editedEvent);
78-
const isReply = isEditing ? Boolean(editedEvent?.replyEventId) : Boolean(replyToEvent);
79+
const isEditing = isMatrixEvent(editedEvent);
80+
const isReply = isEditing ? Boolean(editedEvent.replyEventId) : isMatrixEvent(replyToEvent);
7981
const isReplyAndEditing = isEditing && isReply;
8082

81-
/*const isEmote = containsEmote(model);
83+
const isEmote = message.startsWith(EMOTE_PREFIX);
8284
if (isEmote) {
83-
model = stripEmoteCommand(model);
85+
// if we are dealing with an emote we want to remove the prefix so that `/me` does not
86+
// appear after the `* <userName>` text in the timeline
87+
message = message.slice(EMOTE_PREFIX.length);
8488
}
85-
if (startsWith(model, "//")) {
86-
model = stripPrefix(model, "/");
89+
if (message.startsWith("//")) {
90+
// if user wants to enter a single slash at the start of a message, this
91+
// is how they have to do it (due to it clashing with commands), so here we
92+
// remove the first character to make sure //word displays as /word
93+
message = message.slice(1);
8794
}
88-
model = unescapeMessage(model);*/
89-
90-
// const body = textSerialize(model);
9195

9296
// if we're editing rich text, the message content is pure html
9397
// BUT if we're not, the message content will be plain text
@@ -96,8 +100,7 @@ export async function createMessageContent(
96100
const formattedBodyPrefix = (isReplyAndEditing && getHtmlReplyFallback(editedEvent)) || "";
97101

98102
const content: IContent = {
99-
// TODO emote
100-
msgtype: MsgType.Text,
103+
msgtype: isEmote ? MsgType.Emote : MsgType.Text,
101104
body: isEditing ? `${bodyPrefix} * ${body}` : body,
102105
};
103106

src/components/views/rooms/wysiwyg_composer/utils/message.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import dis from "../../../../../dispatcher/dispatcher";
3131
import { createRedactEventDialog } from "../../../dialogs/ConfirmRedactDialog";
3232
import { endEditing, cancelPreviousPendingEdit } from "./editing";
3333
import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
34-
import { createMessageContent } from "./createMessageContent";
34+
import { createMessageContent, EMOTE_PREFIX } from "./createMessageContent";
3535
import { isContentModified } from "./isContentModified";
3636
import { CommandCategories, getCommand } from "../../../../../SlashCommands";
3737
import { runSlashCommand, shouldSendAnyway } from "../../../../../editor/commands";
@@ -78,11 +78,11 @@ export async function sendMessage(
7878

7979
let content: IContent | null = null;
8080

81-
// Functionality here approximates what can be found in SendMessageComposer.sendMessage()
82-
if (message.startsWith("/") && !message.startsWith("//")) {
81+
// Slash command handling here approximates what can be found in SendMessageComposer.sendMessage()
82+
// but note that the /me and // special cases are handled by the call to createMessageContent
83+
if (message.startsWith("/") && !message.startsWith("//") && !message.startsWith(EMOTE_PREFIX)) {
8384
const { cmd, args } = getCommand(message);
8485
if (cmd) {
85-
// TODO handle /me special case separately, see end of SlashCommands.Commands
8686
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation?.event_id : null;
8787
let commandSuccessful: boolean;
8888
[content, commandSuccessful] = await runSlashCommand(cmd, args, roomId, threadId ?? null);

test/components/views/rooms/wysiwyg_composer/utils/createMessageContent-test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
16+
import { MsgType } from "matrix-js-sdk/src/matrix";
1617

1718
import { mkEvent } from "../../../../../test-utils";
1819
import { RoomPermalinkCreator } from "../../../../../../src/utils/permalinks/Permalinks";
19-
import { createMessageContent } from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/createMessageContent";
20+
import {
21+
createMessageContent,
22+
EMOTE_PREFIX,
23+
} from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/createMessageContent";
2024

2125
describe("createMessageContent", () => {
2226
const permalinkCreator = {
@@ -130,4 +134,24 @@ describe("createMessageContent", () => {
130134
},
131135
});
132136
});
137+
138+
it("Should strip the /me prefix from a message", async () => {
139+
const textBody = "some body text";
140+
const content = await createMessageContent(EMOTE_PREFIX + textBody, true, { permalinkCreator });
141+
142+
expect(content).toMatchObject({ body: textBody, formatted_body: textBody });
143+
});
144+
145+
it("Should strip single / from message prefixed with //", async () => {
146+
const content = await createMessageContent("//twoSlashes", true, { permalinkCreator });
147+
148+
expect(content).toMatchObject({ body: "/twoSlashes", formatted_body: "/twoSlashes" });
149+
});
150+
151+
it("Should set the content type to MsgType.Emote when /me prefix is used", async () => {
152+
const textBody = "some body text";
153+
const content = await createMessageContent(EMOTE_PREFIX + textBody, true, { permalinkCreator });
154+
155+
expect(content).toMatchObject({ msgtype: MsgType.Emote });
156+
});
133157
});

0 commit comments

Comments
 (0)