Fix commands (#791)
* Fix commands and added more * Add /me & /shrug commands * Add help command * Fix cmd descriptions * Add reason in command
This commit is contained in:
parent
0f6f65045d
commit
ac155bbf4c
8 changed files with 351 additions and 72 deletions
|
@ -11,7 +11,7 @@ import { selectRoom, openReusableContextMenu } from '../../../client/action/navi
|
||||||
import * as roomActions from '../../../client/action/room';
|
import * as roomActions from '../../../client/action/room';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getUsername, getUsernameOfRoomMember, getPowerLabel, hasDMWith, hasDevices
|
getUsername, getUsernameOfRoomMember, getPowerLabel, hasDMWith, hasDevices,
|
||||||
} from '../../../util/matrixUtil';
|
} from '../../../util/matrixUtil';
|
||||||
import { getEventCords } from '../../../util/common';
|
import { getEventCords } from '../../../util/common';
|
||||||
import colorMXID from '../../../util/colorMXID';
|
import colorMXID from '../../../util/colorMXID';
|
||||||
|
@ -209,19 +209,18 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleIgnore = async () => {
|
const toggleIgnore = async () => {
|
||||||
const ignoredUsers = mx.getIgnoredUsers();
|
const isIgnored = mx.getIgnoredUsers().includes(userId);
|
||||||
const uIndex = ignoredUsers.indexOf(userId);
|
|
||||||
if (uIndex >= 0) {
|
|
||||||
if (uIndex === -1) return;
|
|
||||||
ignoredUsers.splice(uIndex, 1);
|
|
||||||
} else ignoredUsers.push(userId);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsIgnoring(true);
|
setIsIgnoring(true);
|
||||||
await mx.setIgnoredUsers(ignoredUsers);
|
if (isIgnored) {
|
||||||
|
await roomActions.unignore([userId]);
|
||||||
|
} else {
|
||||||
|
await roomActions.ignore([userId]);
|
||||||
|
}
|
||||||
|
|
||||||
if (isMountedRef.current === false) return;
|
if (isMountedRef.current === false) return;
|
||||||
setIsUserIgnored(uIndex < 0);
|
setIsUserIgnored(!isIgnored);
|
||||||
setIsIgnoring(false);
|
setIsIgnoring(false);
|
||||||
} catch {
|
} catch {
|
||||||
setIsIgnoring(false);
|
setIsIgnoring(false);
|
||||||
|
|
|
@ -8,13 +8,6 @@ import twemoji from 'twemoji';
|
||||||
import { twemojify } from '../../../util/twemojify';
|
import { twemojify } from '../../../util/twemojify';
|
||||||
|
|
||||||
import initMatrix from '../../../client/initMatrix';
|
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 { getEmojiForCompletion } from '../emoji-board/custom-emoji';
|
||||||
import AsyncSearch from '../../../util/AsyncSearch';
|
import AsyncSearch from '../../../util/AsyncSearch';
|
||||||
|
|
||||||
|
@ -22,37 +15,7 @@ import Text from '../../atoms/text/Text';
|
||||||
import ScrollView from '../../atoms/scroll/ScrollView';
|
import ScrollView from '../../atoms/scroll/ScrollView';
|
||||||
import FollowingMembers from '../../molecules/following-members/FollowingMembers';
|
import FollowingMembers from '../../molecules/following-members/FollowingMembers';
|
||||||
import { addRecentEmoji, getRecentEmojis } from '../emoji-board/recent';
|
import { addRecentEmoji, getRecentEmojis } from '../emoji-board/recent';
|
||||||
|
import commands from './commands';
|
||||||
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),
|
|
||||||
}];
|
|
||||||
|
|
||||||
function CmdItem({ onClick, children }) {
|
function CmdItem({ onClick, children }) {
|
||||||
return (
|
return (
|
||||||
|
@ -71,16 +34,16 @@ function renderSuggestions({ prefix, option, suggestions }, fireCmd) {
|
||||||
const cmdOptString = (typeof option === 'string') ? `/${option}` : '/?';
|
const cmdOptString = (typeof option === 'string') ? `/${option}` : '/?';
|
||||||
return cmds.map((cmd) => (
|
return cmds.map((cmd) => (
|
||||||
<CmdItem
|
<CmdItem
|
||||||
key={cmd.name}
|
key={cmd}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
fireCmd({
|
fireCmd({
|
||||||
prefix: cmdPrefix,
|
prefix: cmdPrefix,
|
||||||
option,
|
option,
|
||||||
result: cmd,
|
result: commands[cmd],
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text variant="b2">{`${cmd.name}${cmd.isOptions ? cmdOptString : ''}`}</Text>
|
<Text variant="b2">{`${cmd}${cmd.isOptions ? cmdOptString : ''}`}</Text>
|
||||||
</CmdItem>
|
</CmdItem>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -209,8 +172,8 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
const setupSearch = {
|
const setupSearch = {
|
||||||
'/': () => {
|
'/': () => {
|
||||||
asyncSearch.setup(commands, { keys: ['name'], isContain: true });
|
asyncSearch.setup(Object.keys(commands), { isContain: true });
|
||||||
setCmd({ prefix, suggestions: commands });
|
setCmd({ prefix, suggestions: Object.keys(commands) });
|
||||||
},
|
},
|
||||||
':': () => {
|
':': () => {
|
||||||
const parentIds = initMatrix.roomList.getAllParentSpaces(roomId);
|
const parentIds = initMatrix.roomList.getAllParentSpaces(roomId);
|
||||||
|
@ -242,8 +205,9 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
|
||||||
}
|
}
|
||||||
function fireCmd(myCmd) {
|
function fireCmd(myCmd) {
|
||||||
if (myCmd.prefix === '/') {
|
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.prefix === ':') {
|
||||||
if (!myCmd.result.mxc) addRecentEmoji(myCmd.result.unicode);
|
if (!myCmd.result.mxc) addRecentEmoji(myCmd.result.unicode);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import ScrollView from '../../atoms/scroll/ScrollView';
|
||||||
import { MessageReply } from '../../molecules/message/Message';
|
import { MessageReply } from '../../molecules/message/Message';
|
||||||
|
|
||||||
import StickerBoard from '../sticker-board/StickerBoard';
|
import StickerBoard from '../sticker-board/StickerBoard';
|
||||||
|
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
|
||||||
|
|
||||||
import CirclePlusIC from '../../../../public/res/ic/outlined/circle-plus.svg';
|
import CirclePlusIC from '../../../../public/res/ic/outlined/circle-plus.svg';
|
||||||
import EmojiIC from '../../../../public/res/ic/outlined/emoji.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 FileIC from '../../../../public/res/ic/outlined/file.svg';
|
||||||
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
|
import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
|
||||||
|
|
||||||
|
import commands from './commands';
|
||||||
|
|
||||||
const CMD_REGEX = /(^\/|:|@)(\S*)$/;
|
const CMD_REGEX = /(^\/|:|@)(\S*)$/;
|
||||||
let isTyping = false;
|
let isTyping = false;
|
||||||
let isCmdActivated = false;
|
let isCmdActivated = false;
|
||||||
|
@ -182,30 +185,54 @@ function RoomViewInput({
|
||||||
};
|
};
|
||||||
}, [roomId]);
|
}, [roomId]);
|
||||||
|
|
||||||
const sendMessage = async () => {
|
const sendBody = async (body, msgType = 'm.text') => {
|
||||||
requestAnimationFrame(() => deactivateCmdAndEmit());
|
|
||||||
const msgBody = textAreaRef.current.value;
|
|
||||||
if (roomsInput.isSending(roomId)) return;
|
if (roomsInput.isSending(roomId)) return;
|
||||||
if (msgBody.trim() === '' && attachment === null) return;
|
|
||||||
sendIsTyping(false);
|
sendIsTyping(false);
|
||||||
|
|
||||||
roomsInput.setMessage(roomId, msgBody);
|
roomsInput.setMessage(roomId, body);
|
||||||
if (attachment !== null) {
|
if (attachment !== null) {
|
||||||
roomsInput.setAttachment(roomId, attachment);
|
roomsInput.setAttachment(roomId, attachment);
|
||||||
}
|
}
|
||||||
textAreaRef.current.disabled = true;
|
textAreaRef.current.disabled = true;
|
||||||
textAreaRef.current.style.cursor = 'not-allowed';
|
textAreaRef.current.style.cursor = 'not-allowed';
|
||||||
await roomsInput.sendInput(roomId);
|
await roomsInput.sendInput(roomId, msgType);
|
||||||
textAreaRef.current.disabled = false;
|
textAreaRef.current.disabled = false;
|
||||||
textAreaRef.current.style.cursor = 'unset';
|
textAreaRef.current.style.cursor = 'unset';
|
||||||
focusInput();
|
focusInput();
|
||||||
|
|
||||||
textAreaRef.current.value = roomsInput.getMessage(roomId);
|
textAreaRef.current.value = roomsInput.getMessage(roomId);
|
||||||
viewEvent.emit('message_sent');
|
|
||||||
textAreaRef.current.style.height = 'unset';
|
textAreaRef.current.style.height = 'unset';
|
||||||
if (replyTo !== null) setReplyTo(null);
|
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) => {
|
const handleSendSticker = async (data) => {
|
||||||
roomsInput.sendSticker(roomId, data);
|
roomsInput.sendSticker(roomId, data);
|
||||||
};
|
};
|
||||||
|
|
211
src/app/organisms/room/commands.jsx
Normal file
211
src/app/organisms/room/commands.jsx
Normal file
|
@ -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;
|
10
src/app/organisms/room/commands.scss
Normal file
10
src/app/organisms/room/commands.scss
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import { getIdServer } from '../../util/matrixUtil';
|
||||||
/**
|
/**
|
||||||
* https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L73
|
* https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L73
|
||||||
* @param {string} roomId Id of room to add
|
* @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
|
* @returns {Promise} A promise
|
||||||
*/
|
*/
|
||||||
function addRoomToMDirect(roomId, userId) {
|
function addRoomToMDirect(roomId, userId) {
|
||||||
|
@ -79,13 +79,23 @@ function guessDMRoomTargetId(room, myUserId) {
|
||||||
return oldestMember.userId;
|
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 {string} roomId
|
||||||
* @param {boolean} isDM
|
* @param {boolean} isDM
|
||||||
* @param {string[]} via
|
* @param {string[]} via
|
||||||
*/
|
*/
|
||||||
async function join(roomIdOrAlias, isDM, via) {
|
async function join(roomIdOrAlias, isDM = false, via = undefined) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
const roomIdParts = roomIdOrAlias.split(':');
|
const roomIdParts = roomIdOrAlias.split(':');
|
||||||
const viaServers = via || [roomIdParts[1]];
|
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 = {
|
const options = {
|
||||||
is_direct: true,
|
is_direct: true,
|
||||||
invite: [userId],
|
invite: Array.isArray(userIdOrIds) ? userIdOrIds : [userIdOrIds],
|
||||||
visibility: 'private',
|
visibility: 'private',
|
||||||
preset: 'trusted_private_chat',
|
preset: 'trusted_private_chat',
|
||||||
initial_state: [],
|
initial_state: [],
|
||||||
|
@ -262,10 +272,10 @@ async function createRoom(opts) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function invite(roomId, userId) {
|
async function invite(roomId, userId, reason) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
|
|
||||||
const result = await mx.invite(roomId, userId);
|
const result = await mx.invite(roomId, userId, undefined, reason);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,6 +300,21 @@ async function unban(roomId, userId) {
|
||||||
return result;
|
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) {
|
async function setPowerLevel(roomId, userId, powerLevel) {
|
||||||
const mx = initMatrix.matrixClient;
|
const mx = initMatrix.matrixClient;
|
||||||
const room = mx.getRoom(roomId);
|
const room = mx.getRoom(roomId);
|
||||||
|
@ -300,9 +325,37 @@ async function setPowerLevel(roomId, userId, powerLevel) {
|
||||||
return result;
|
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 {
|
export {
|
||||||
|
convertToDm,
|
||||||
|
convertToRoom,
|
||||||
join, leave,
|
join, leave,
|
||||||
createDM, createRoom,
|
createDM, createRoom,
|
||||||
invite, kick, ban, unban,
|
invite, kick, ban, unban,
|
||||||
|
ignore, unignore,
|
||||||
setPowerLevel,
|
setPowerLevel,
|
||||||
|
setMyRoomNick, setMyRoomAvatar,
|
||||||
};
|
};
|
||||||
|
|
|
@ -257,10 +257,10 @@ class RoomList extends EventEmitter {
|
||||||
const latestMDirects = this.getMDirects();
|
const latestMDirects = this.getMDirects();
|
||||||
|
|
||||||
latestMDirects.forEach((directId) => {
|
latestMDirects.forEach((directId) => {
|
||||||
const myRoom = this.matrixClient.getRoom(directId);
|
|
||||||
if (this.mDirects.has(directId)) return;
|
if (this.mDirects.has(directId)) return;
|
||||||
this.mDirects.add(directId);
|
this.mDirects.add(directId);
|
||||||
|
|
||||||
|
const myRoom = this.matrixClient.getRoom(directId);
|
||||||
if (myRoom === null) return;
|
if (myRoom === null) return;
|
||||||
if (myRoom.getMyMembership() === 'join') {
|
if (myRoom.getMyMembership() === 'join') {
|
||||||
this.directs.add(directId);
|
this.directs.add(directId);
|
||||||
|
@ -268,6 +268,19 @@ class RoomList extends EventEmitter {
|
||||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
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) => {
|
this.matrixClient.on('Room.name', (room) => {
|
||||||
|
|
|
@ -274,7 +274,7 @@ class RoomsInput extends EventEmitter {
|
||||||
return this.roomIdToInput.get(roomId)?.isSending || false;
|
return this.roomIdToInput.get(roomId)?.isSending || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendInput(roomId) {
|
async sendInput(roomId, msgType) {
|
||||||
const room = this.matrixClient.getRoom(roomId);
|
const room = this.matrixClient.getRoom(roomId);
|
||||||
const input = this.getInput(roomId);
|
const input = this.getInput(roomId);
|
||||||
input.isSending = true;
|
input.isSending = true;
|
||||||
|
@ -288,7 +288,7 @@ class RoomsInput extends EventEmitter {
|
||||||
const rawMessage = input.message;
|
const rawMessage = input.message;
|
||||||
let content = {
|
let content = {
|
||||||
body: rawMessage,
|
body: rawMessage,
|
||||||
msgtype: 'm.text',
|
msgtype: msgType ?? 'm.text',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply formatting if relevant
|
// Apply formatting if relevant
|
||||||
|
@ -459,12 +459,14 @@ class RoomsInput extends EventEmitter {
|
||||||
const room = this.matrixClient.getRoom(roomId);
|
const room = this.matrixClient.getRoom(roomId);
|
||||||
const isReply = typeof mEvent.getWireContent()['m.relates_to']?.['m.in_reply_to'] !== 'undefined';
|
const isReply = typeof mEvent.getWireContent()['m.relates_to']?.['m.in_reply_to'] !== 'undefined';
|
||||||
|
|
||||||
|
const msgtype = mEvent.getWireContent().msgtype ?? 'm.text';
|
||||||
|
|
||||||
const content = {
|
const content = {
|
||||||
body: ` * ${editedBody}`,
|
body: ` * ${editedBody}`,
|
||||||
msgtype: 'm.text',
|
msgtype,
|
||||||
'm.new_content': {
|
'm.new_content': {
|
||||||
body: editedBody,
|
body: editedBody,
|
||||||
msgtype: 'm.text',
|
msgtype,
|
||||||
},
|
},
|
||||||
'm.relates_to': {
|
'm.relates_to': {
|
||||||
event_id: mEvent.getId(),
|
event_id: mEvent.getId(),
|
||||||
|
|
Loading…
Reference in a new issue