Fix commands (#791)
authorAjay Bura <32841439+ajbura@users.noreply.github.com>
Sat, 3 Sep 2022 16:16:40 +0000 (21:46 +0530)
committerGitHub <noreply@github.com>
Sat, 3 Sep 2022 16:16:40 +0000 (21:46 +0530)
* Fix commands and added more

* Add /me & /shrug commands

* Add help command

* Fix cmd descriptions

* Add reason in command

src/app/organisms/profile-viewer/ProfileViewer.jsx
src/app/organisms/room/RoomViewCmdBar.jsx
src/app/organisms/room/RoomViewInput.jsx
src/app/organisms/room/commands.jsx [new file with mode: 0644]
src/app/organisms/room/commands.scss [new file with mode: 0644]
src/client/action/room.js
src/client/state/RoomList.js
src/client/state/RoomsInput.js

index 26e39682956029e4b939ddc21724b291b5e283c9..fec9189a54e5be370d5c1baa2eb7d9e4966679e6 100644 (file)
@@ -11,7 +11,7 @@ import { selectRoom, openReusableContextMenu } from '../../../client/action/navi
 import * as roomActions from '../../../client/action/room';
 
 import {
-  getUsername, getUsernameOfRoomMember, getPowerLabel, hasDMWith, hasDevices
+  getUsername, getUsernameOfRoomMember, getPowerLabel, hasDMWith, hasDevices,
 } from '../../../util/matrixUtil';
 import { getEventCords } from '../../../util/common';
 import colorMXID from '../../../util/colorMXID';
@@ -209,19 +209,18 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
   };
 
   const toggleIgnore = async () => {
-    const ignoredUsers = mx.getIgnoredUsers();
-    const uIndex = ignoredUsers.indexOf(userId);
-    if (uIndex >= 0) {
-      if (uIndex === -1) return;
-      ignoredUsers.splice(uIndex, 1);
-    } else ignoredUsers.push(userId);
+    const isIgnored = mx.getIgnoredUsers().includes(userId);
 
     try {
       setIsIgnoring(true);
-      await mx.setIgnoredUsers(ignoredUsers);
+      if (isIgnored) {
+        await roomActions.unignore([userId]);
+      } else {
+        await roomActions.ignore([userId]);
+      }
 
       if (isMountedRef.current === false) return;
-      setIsUserIgnored(uIndex < 0);
+      setIsUserIgnored(!isIgnored);
       setIsIgnoring(false);
     } catch {
       setIsIgnoring(false);
index 68919aacc38d2ff73b27302dad93c7f2d905251b..8c390a06891d9cb66515d6cffed5bd41bd0bb508 100644 (file)
@@ -8,13 +8,6 @@ import twemoji from 'twemoji';
 import { twemojify } from '../../../util/twemojify';
 
 import initMatrix from '../../../client/initMatrix';
-import { toggleMarkdown } from '../../../client/action/settings';
-import * as roomActions from '../../../client/action/room';
-import {
-  openCreateRoom,
-  openPublicRooms,
-  openInviteUser,
-} from '../../../client/action/navigation';
 import { getEmojiForCompletion } from '../emoji-board/custom-emoji';
 import AsyncSearch from '../../../util/AsyncSearch';
 
@@ -22,37 +15,7 @@ import Text from '../../atoms/text/Text';
 import ScrollView from '../../atoms/scroll/ScrollView';
 import FollowingMembers from '../../molecules/following-members/FollowingMembers';
 import { addRecentEmoji, getRecentEmojis } from '../emoji-board/recent';
-
-const commands = [{
-  name: 'markdown',
-  description: 'Toggle markdown for messages.',
-  exe: () => toggleMarkdown(),
-}, {
-  name: 'startDM',
-  isOptions: true,
-  description: 'Start direct message with user. Example: /startDM/@johndoe.matrix.org',
-  exe: (roomId, searchTerm) => openInviteUser(undefined, searchTerm),
-}, {
-  name: 'createRoom',
-  description: 'Create new room',
-  exe: () => openCreateRoom(),
-}, {
-  name: 'join',
-  isOptions: true,
-  description: 'Join room with alias. Example: /join/#cinny:matrix.org',
-  exe: (roomId, searchTerm) => openPublicRooms(searchTerm),
-}, {
-  name: 'leave',
-  description: 'Leave current room',
-  exe: (roomId) => {
-    roomActions.leave(roomId);
-  },
-}, {
-  name: 'invite',
-  isOptions: true,
-  description: 'Invite user to room. Example: /invite/@johndoe:matrix.org',
-  exe: (roomId, searchTerm) => openInviteUser(roomId, searchTerm),
-}];
+import commands from './commands';
 
 function CmdItem({ onClick, children }) {
   return (
@@ -71,16 +34,16 @@ function renderSuggestions({ prefix, option, suggestions }, fireCmd) {
     const cmdOptString = (typeof option === 'string') ? `/${option}` : '/?';
     return cmds.map((cmd) => (
       <CmdItem
-        key={cmd.name}
+        key={cmd}
         onClick={() => {
           fireCmd({
             prefix: cmdPrefix,
             option,
-            result: cmd,
+            result: commands[cmd],
           });
         }}
       >
-        <Text variant="b2">{`${cmd.name}${cmd.isOptions ? cmdOptString : ''}`}</Text>
+        <Text variant="b2">{`${cmd}${cmd.isOptions ? cmdOptString : ''}`}</Text>
       </CmdItem>
     ));
   }
@@ -209,8 +172,8 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
     const mx = initMatrix.matrixClient;
     const setupSearch = {
       '/': () => {
-        asyncSearch.setup(commands, { keys: ['name'], isContain: true });
-        setCmd({ prefix, suggestions: commands });
+        asyncSearch.setup(Object.keys(commands), { isContain: true });
+        setCmd({ prefix, suggestions: Object.keys(commands) });
       },
       ':': () => {
         const parentIds = initMatrix.roomList.getAllParentSpaces(roomId);
@@ -242,8 +205,9 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
   }
   function fireCmd(myCmd) {
     if (myCmd.prefix === '/') {
-      myCmd.result.exe(roomId, myCmd.option);
-      viewEvent.emit('cmd_fired');
+      viewEvent.emit('cmd_fired', {
+        replace: `/${myCmd.result.name}`,
+      });
     }
     if (myCmd.prefix === ':') {
       if (!myCmd.result.mxc) addRecentEmoji(myCmd.result.unicode);
index 930eae10e002e5d6f891e8f1d218b7e2ed9c1de6..0a0a171c728d12ce36c1de9209e29cfcc8475d60 100644 (file)
@@ -21,6 +21,7 @@ import ScrollView from '../../atoms/scroll/ScrollView';
 import { MessageReply } from '../../molecules/message/Message';
 
 import StickerBoard from '../sticker-board/StickerBoard';
+import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
 
 import CirclePlusIC from '../../../../public/res/ic/outlined/circle-plus.svg';
 import EmojiIC from '../../../../public/res/ic/outlined/emoji.svg';
@@ -33,6 +34,8 @@ import MarkdownIC from '../../../../public/res/ic/outlined/markdown.svg';
 import FileIC from '../../../../public/res/ic/outlined/file.svg';
 import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
 
+import commands from './commands';
+
 const CMD_REGEX = /(^\/|:|@)(\S*)$/;
 let isTyping = false;
 let isCmdActivated = false;
@@ -182,30 +185,54 @@ function RoomViewInput({
     };
   }, [roomId]);
 
-  const sendMessage = async () => {
-    requestAnimationFrame(() => deactivateCmdAndEmit());
-    const msgBody = textAreaRef.current.value;
+  const sendBody = async (body, msgType = 'm.text') => {
     if (roomsInput.isSending(roomId)) return;
-    if (msgBody.trim() === '' && attachment === null) return;
     sendIsTyping(false);
 
-    roomsInput.setMessage(roomId, msgBody);
+    roomsInput.setMessage(roomId, body);
     if (attachment !== null) {
       roomsInput.setAttachment(roomId, attachment);
     }
     textAreaRef.current.disabled = true;
     textAreaRef.current.style.cursor = 'not-allowed';
-    await roomsInput.sendInput(roomId);
+    await roomsInput.sendInput(roomId, msgType);
     textAreaRef.current.disabled = false;
     textAreaRef.current.style.cursor = 'unset';
     focusInput();
 
     textAreaRef.current.value = roomsInput.getMessage(roomId);
-    viewEvent.emit('message_sent');
     textAreaRef.current.style.height = 'unset';
     if (replyTo !== null) setReplyTo(null);
   };
 
+  const processCommand = (cmdBody) => {
+    const spaceIndex = cmdBody.indexOf(' ');
+    const cmdName = cmdBody.slice(1, spaceIndex > -1 ? spaceIndex : undefined);
+    const cmdData = spaceIndex > -1 ? cmdBody.slice(spaceIndex + 1) : '';
+    if (!commands[cmdName]) {
+      confirmDialog('Invalid Command', `"${cmdName}" is not a valid command.`, 'Alright');
+      return;
+    }
+    if (['me', 'shrug'].includes(cmdName)) {
+      commands[cmdName].exe(roomId, cmdData, (message, msgType) => sendBody(message, msgType));
+      return;
+    }
+    commands[cmdName].exe(roomId, cmdData);
+  };
+
+  const sendMessage = async () => {
+    requestAnimationFrame(() => deactivateCmdAndEmit());
+    const msgBody = textAreaRef.current.value.trim();
+    if (msgBody.startsWith('/')) {
+      processCommand(msgBody.trim());
+      textAreaRef.current.value = '';
+      textAreaRef.current.style.height = 'unset';
+      return;
+    }
+    if (msgBody === '' && attachment === null) return;
+    sendBody(msgBody, 'm.text');
+  };
+
   const handleSendSticker = async (data) => {
     roomsInput.sendSticker(roomId, data);
   };
diff --git a/src/app/organisms/room/commands.jsx b/src/app/organisms/room/commands.jsx
new file mode 100644 (file)
index 0000000..410fd74
--- /dev/null
@@ -0,0 +1,211 @@
+import React from 'react';
+import './commands.scss';
+
+import initMatrix from '../../../client/initMatrix';
+import * as roomActions from '../../../client/action/room';
+import { hasDMWith, hasDevices } from '../../../util/matrixUtil';
+import { selectRoom, openReusableDialog } from '../../../client/action/navigation';
+
+import Text from '../../atoms/text/Text';
+import SettingTile from '../../molecules/setting-tile/SettingTile';
+
+const MXID_REG = /^@\S+:\S+$/;
+const ROOM_ID_ALIAS_REG = /^(#|!)\S+:\S+$/;
+const ROOM_ID_REG = /^!\S+:\S+$/;
+const MXC_REG = /^mxc:\/\/\S+$/;
+
+export function processMxidAndReason(data) {
+  let reason;
+  let idData = data;
+  const reasonMatch = data.match(/\s-r\s/);
+  if (reasonMatch) {
+    idData = data.slice(0, reasonMatch.index);
+    reason = data.slice(reasonMatch.index + reasonMatch[0].length);
+    if (reason.trim() === '') reason = undefined;
+  }
+  const rawIds = idData.split(' ');
+  const userIds = rawIds.filter((id) => id.match(MXID_REG));
+  return {
+    userIds,
+    reason,
+  };
+}
+
+const commands = {
+  me: {
+    name: 'me',
+    description: 'Display action',
+    exe: (roomId, data, onSuccess) => {
+      const body = data.trim();
+      if (body === '') return;
+      onSuccess(body, 'm.emote');
+    },
+  },
+  shrug: {
+    name: 'shrug',
+    description: 'Send ¯\\_(ツ)_/¯ as message',
+    exe: (roomId, data, onSuccess) => onSuccess(
+      `¯\\_(ツ)_/¯${data.trim() !== '' ? ` ${data}` : ''}`,
+      'm.text',
+    ),
+  },
+  help: {
+    name: 'help',
+    description: 'View all commands',
+    // eslint-disable-next-line no-use-before-define
+    exe: () => openHelpDialog(),
+  },
+  startdm: {
+    name: 'startdm',
+    description: 'Start DM with user. Example: /startdm userId1 userId2',
+    exe: async (roomId, data) => {
+      const mx = initMatrix.matrixClient;
+      const rawIds = data.split(' ');
+      const userIds = rawIds.filter((id) => id.match(MXID_REG) && id !== mx.getUserId());
+      if (userIds.length === 0) return;
+      if (userIds.length === 1) {
+        const dmRoomId = hasDMWith(userIds[0]);
+        if (dmRoomId) {
+          selectRoom(dmRoomId);
+          return;
+        }
+      }
+      const devices = await Promise.all(userIds.map(hasDevices));
+      const isEncrypt = devices.every((hasDevice) => hasDevice);
+      const result = await roomActions.createDM(userIds, isEncrypt);
+      selectRoom(result.room_id);
+    },
+  },
+  join: {
+    name: 'join',
+    description: 'Join room with alias. Example: /join alias1 alias2',
+    exe: (roomId, data) => {
+      const rawIds = data.split(' ');
+      const roomIds = rawIds.filter((id) => id.match(ROOM_ID_ALIAS_REG));
+      roomIds.map((id) => roomActions.join(id));
+    },
+  },
+  leave: {
+    name: 'leave',
+    description: 'Leave current room.',
+    exe: (roomId, data) => {
+      if (data.trim() === '') {
+        roomActions.leave(roomId);
+        return;
+      }
+      const rawIds = data.split(' ');
+      const roomIds = rawIds.filter((id) => id.match(ROOM_ID_REG));
+      roomIds.map((id) => roomActions.leave(id));
+    },
+  },
+  invite: {
+    name: 'invite',
+    description: 'Invite user to room. Example: /invite userId1 userId2 [-r reason]',
+    exe: (roomId, data) => {
+      const { userIds, reason } = processMxidAndReason(data);
+      userIds.map((id) => roomActions.invite(roomId, id, reason));
+    },
+  },
+  disinvite: {
+    name: 'disinvite',
+    description: 'Disinvite user to room. Example: /disinvite userId1 userId2 [-r reason]',
+    exe: (roomId, data) => {
+      const { userIds, reason } = processMxidAndReason(data);
+      userIds.map((id) => roomActions.kick(roomId, id, reason));
+    },
+  },
+  kick: {
+    name: 'kick',
+    description: 'Kick user from room. Example: /kick userId1 userId2 [-r reason]',
+    exe: (roomId, data) => {
+      const { userIds, reason } = processMxidAndReason(data);
+      userIds.map((id) => roomActions.kick(roomId, id, reason));
+    },
+  },
+  ban: {
+    name: 'ban',
+    description: 'Ban user from room. Example: /ban userId1 userId2 [-r reason]',
+    exe: (roomId, data) => {
+      const { userIds, reason } = processMxidAndReason(data);
+      userIds.map((id) => roomActions.ban(roomId, id, reason));
+    },
+  },
+  unban: {
+    name: 'unban',
+    description: 'Unban user from room. Example: /unban userId1 userId2',
+    exe: (roomId, data) => {
+      const rawIds = data.split(' ');
+      const userIds = rawIds.filter((id) => id.match(MXID_REG));
+      userIds.map((id) => roomActions.unban(roomId, id));
+    },
+  },
+  ignore: {
+    name: 'ignore',
+    description: 'Ignore user. Example: /ignore userId1 userId2',
+    exe: (roomId, data) => {
+      const rawIds = data.split(' ');
+      const userIds = rawIds.filter((id) => id.match(MXID_REG));
+      if (userIds.length > 0) roomActions.ignore(userIds);
+    },
+  },
+  unignore: {
+    name: 'unignore',
+    description: 'Unignore user. Example: /unignore userId1 userId2',
+    exe: (roomId, data) => {
+      const rawIds = data.split(' ');
+      const userIds = rawIds.filter((id) => id.match(MXID_REG));
+      if (userIds.length > 0) roomActions.unignore(userIds);
+    },
+  },
+  myroomnick: {
+    name: 'myroomnick',
+    description: 'Change my room nick',
+    exe: (roomId, data) => {
+      const nick = data.trim();
+      if (nick === '') return;
+      roomActions.setMyRoomNick(roomId, nick);
+    },
+  },
+  myroomavatar: {
+    name: 'myroomavatar',
+    description: 'Change my room avatar. Example /myroomavatar mxc://xyzabc',
+    exe: (roomId, data) => {
+      if (data.match(MXC_REG)) {
+        roomActions.setMyRoomAvatar(roomId, data);
+      }
+    },
+  },
+  converttodm: {
+    name: 'converttodm',
+    description: 'Convert room to direct message',
+    exe: (roomId) => {
+      roomActions.convertToDm(roomId);
+    },
+  },
+  converttoroom: {
+    name: 'converttoroom',
+    description: 'Convert direct message to room',
+    exe: (roomId) => {
+      roomActions.convertToRoom(roomId);
+    },
+  },
+};
+
+function openHelpDialog() {
+  openReusableDialog(
+    <Text variant="s1" weight="medium">Commands</Text>,
+    () => (
+      <div className="commands-dialog">
+        {Object.keys(commands).map((cmdName) => (
+          <SettingTile
+            key={cmdName}
+            title={cmdName}
+            content={<Text variant="b3">{commands[cmdName].description}</Text>}
+          />
+        ))}
+      </div>
+    ),
+  );
+}
+
+export default commands;
diff --git a/src/app/organisms/room/commands.scss b/src/app/organisms/room/commands.scss
new file mode 100644 (file)
index 0000000..6283937
--- /dev/null
@@ -0,0 +1,10 @@
+.commands-dialog {
+  & > * {
+    padding: var(--sp-tight) var(--sp-normal);
+    border-bottom: 1px solid var(--bg-surface-border);
+    &:last-child {
+      border-bottom: none;
+      margin-bottom: var(--sp-extra-loose);
+    }
+  }
+}
\ No newline at end of file
index 1fe721de6e17367471f86468bd5f313b71c922be..a0a7525fbf4915afe63e9359e71faa49b9611371 100644 (file)
@@ -6,7 +6,7 @@ import { getIdServer } from '../../util/matrixUtil';
 /**
  * https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L73
  * @param {string} roomId Id of room to add
- * @param {string} userId User id to which dm
+ * @param {string} userId User id to which dm || undefined to remove
  * @returns {Promise} A promise
  */
 function addRoomToMDirect(roomId, userId) {
@@ -79,13 +79,23 @@ function guessDMRoomTargetId(room, myUserId) {
   return oldestMember.userId;
 }
 
+function convertToDm(roomId) {
+  const mx = initMatrix.matrixClient;
+  const room = mx.getRoom(roomId);
+  return addRoomToMDirect(roomId, guessDMRoomTargetId(room, mx.getUserId()));
+}
+
+function convertToRoom(roomId) {
+  return addRoomToMDirect(roomId, undefined);
+}
+
 /**
  *
  * @param {string} roomId
  * @param {boolean} isDM
  * @param {string[]} via
  */
-async function join(roomIdOrAlias, isDM, via) {
+async function join(roomIdOrAlias, isDM = false, via = undefined) {
   const mx = initMatrix.matrixClient;
   const roomIdParts = roomIdOrAlias.split(':');
   const viaServers = via || [roomIdParts[1]];
@@ -150,10 +160,10 @@ async function create(options, isDM = false) {
   }
 }
 
-async function createDM(userId, isEncrypted = true) {
+async function createDM(userIdOrIds, isEncrypted = true) {
   const options = {
     is_direct: true,
-    invite: [userId],
+    invite: Array.isArray(userIdOrIds) ? userIdOrIds : [userIdOrIds],
     visibility: 'private',
     preset: 'trusted_private_chat',
     initial_state: [],
@@ -262,10 +272,10 @@ async function createRoom(opts) {
   return result;
 }
 
-async function invite(roomId, userId) {
+async function invite(roomId, userId, reason) {
   const mx = initMatrix.matrixClient;
 
-  const result = await mx.invite(roomId, userId);
+  const result = await mx.invite(roomId, userId, undefined, reason);
   return result;
 }
 
@@ -290,6 +300,21 @@ async function unban(roomId, userId) {
   return result;
 }
 
+async function ignore(userIds) {
+  const mx = initMatrix.matrixClient;
+
+  let ignoredUsers = mx.getIgnoredUsers().concat(userIds);
+  ignoredUsers = [...new Set(ignoredUsers)];
+  await mx.setIgnoredUsers(ignoredUsers);
+}
+
+async function unignore(userIds) {
+  const mx = initMatrix.matrixClient;
+
+  const ignoredUsers = mx.getIgnoredUsers();
+  await mx.setIgnoredUsers(ignoredUsers.filter((id) => !userIds.includes(id)));
+}
+
 async function setPowerLevel(roomId, userId, powerLevel) {
   const mx = initMatrix.matrixClient;
   const room = mx.getRoom(roomId);
@@ -300,9 +325,37 @@ async function setPowerLevel(roomId, userId, powerLevel) {
   return result;
 }
 
+async function setMyRoomNick(roomId, nick) {
+  const mx = initMatrix.matrixClient;
+  const room = mx.getRoom(roomId);
+  const mEvent = room.currentState.getStateEvents('m.room.member', mx.getUserId());
+  const content = mEvent?.getContent();
+  if (!content) return;
+  await mx.sendStateEvent(roomId, 'm.room.member', {
+    ...content,
+    displayname: nick,
+  }, mx.getUserId());
+}
+
+async function setMyRoomAvatar(roomId, mxc) {
+  const mx = initMatrix.matrixClient;
+  const room = mx.getRoom(roomId);
+  const mEvent = room.currentState.getStateEvents('m.room.member', mx.getUserId());
+  const content = mEvent?.getContent();
+  if (!content) return;
+  await mx.sendStateEvent(roomId, 'm.room.member', {
+    ...content,
+    avatar_url: mxc,
+  }, mx.getUserId());
+}
+
 export {
+  convertToDm,
+  convertToRoom,
   join, leave,
   createDM, createRoom,
   invite, kick, ban, unban,
+  ignore, unignore,
   setPowerLevel,
+  setMyRoomNick, setMyRoomAvatar,
 };
index b24379b0ada49b3eea95c16ae95ff0325186412a..a1570480a338ea294082757d6fd69527f9bcf695 100644 (file)
@@ -257,10 +257,10 @@ class RoomList extends EventEmitter {
       const latestMDirects = this.getMDirects();
 
       latestMDirects.forEach((directId) => {
-        const myRoom = this.matrixClient.getRoom(directId);
         if (this.mDirects.has(directId)) return;
         this.mDirects.add(directId);
 
+        const myRoom = this.matrixClient.getRoom(directId);
         if (myRoom === null) return;
         if (myRoom.getMyMembership() === 'join') {
           this.directs.add(directId);
@@ -268,6 +268,19 @@ class RoomList extends EventEmitter {
           this.emit(cons.events.roomList.ROOMLIST_UPDATED);
         }
       });
+
+      [...this.directs].forEach((directId) => {
+        if (latestMDirects.has(directId)) return;
+        this.mDirects.delete(directId);
+
+        const myRoom = this.matrixClient.getRoom(directId);
+        if (myRoom === null) return;
+        if (myRoom.getMyMembership() === 'join') {
+          this.directs.delete(directId);
+          this.rooms.add(directId);
+          this.emit(cons.events.roomList.ROOMLIST_UPDATED);
+        }
+      });
     });
 
     this.matrixClient.on('Room.name', (room) => {
index 03dd074515f8a5e3244f3b2d4eff73e5e3a7acf1..4277b2f0c3730df875acc2f101b989fe7450c1e1 100644 (file)
@@ -274,7 +274,7 @@ class RoomsInput extends EventEmitter {
     return this.roomIdToInput.get(roomId)?.isSending || false;
   }
 
-  async sendInput(roomId) {
+  async sendInput(roomId, msgType) {
     const room = this.matrixClient.getRoom(roomId);
     const input = this.getInput(roomId);
     input.isSending = true;
@@ -288,7 +288,7 @@ class RoomsInput extends EventEmitter {
       const rawMessage = input.message;
       let content = {
         body: rawMessage,
-        msgtype: 'm.text',
+        msgtype: msgType ?? 'm.text',
       };
 
       // Apply formatting if relevant
@@ -459,12 +459,14 @@ class RoomsInput extends EventEmitter {
     const room = this.matrixClient.getRoom(roomId);
     const isReply = typeof mEvent.getWireContent()['m.relates_to']?.['m.in_reply_to'] !== 'undefined';
 
+    const msgtype = mEvent.getWireContent().msgtype ?? 'm.text';
+
     const content = {
       body: ` * ${editedBody}`,
-      msgtype: 'm.text',
+      msgtype,
       'm.new_content': {
         body: editedBody,
-        msgtype: 'm.text',
+        msgtype,
       },
       'm.relates_to': {
         event_id: mEvent.getId(),