Remove goto cmds from msg input also fix #81

Signed-off-by: Ajay Bura <ajbura@gmail.com>
This commit is contained in:
Ajay Bura 2021-12-12 11:31:52 +05:30
parent fbeecc0479
commit 88a988d876
6 changed files with 145 additions and 331 deletions

View file

@ -0,0 +1,59 @@
/* eslint-disable react/prop-types */
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import './FollowingMembers.scss';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import { openReadReceipts } from '../../../client/action/navigation';
import Text from '../../atoms/text/Text';
import RawIcon from '../../atoms/system-icons/RawIcon';
import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
import { getUsersActionJsx } from '../../organisms/room/common';
function FollowingMembers({ roomTimeline }) {
const [followingMembers, setFollowingMembers] = useState([]);
const { roomId } = roomTimeline;
const mx = initMatrix.matrixClient;
const { roomsInput } = initMatrix;
const myUserId = mx.getUserId();
const handleOnMessageSent = () => setFollowingMembers([]);
useEffect(() => {
const updateFollowingMembers = () => {
setFollowingMembers(roomTimeline.getLiveReaders());
};
updateFollowingMembers();
roomTimeline.on(cons.events.roomTimeline.LIVE_RECEIPT, updateFollowingMembers);
roomsInput.on(cons.events.roomsInput.MESSAGE_SENT, handleOnMessageSent);
return () => {
roomTimeline.removeListener(cons.events.roomTimeline.LIVE_RECEIPT, updateFollowingMembers);
roomsInput.removeListener(cons.events.roomsInput.MESSAGE_SENT, handleOnMessageSent);
};
}, [roomTimeline]);
const filteredM = followingMembers.filter((userId) => userId !== myUserId);
return filteredM.length !== 0 && (
<button
className="following-members"
onClick={() => openReadReceipts(roomId, followingMembers)}
type="button"
>
<RawIcon
size="extra-small"
src={TickMarkIC}
/>
<Text variant="b2">{getUsersActionJsx(roomId, filteredM, 'following the conversation.')}</Text>
</button>
);
}
FollowingMembers.propTypes = {
roomTimeline: PropTypes.shape({}).isRequired,
};
export default FollowingMembers;

View file

@ -0,0 +1,32 @@
.following-members {
width: 100%;
padding: 0 var(--sp-normal);
display: flex;
justify-content: flex-end;
align-items: center;
cursor: pointer;
& .ic-raw {
min-width: var(--ic-extra-small);
opacity: 0.4;
margin: 0 var(--sp-extra-tight);
}
& .text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: var(--tc-surface-low);
b {
color: var(--tc-surface-normal);
}
}
&:hover,
&:focus {
background-color: var(--bg-surface-hover);
}
&:active {
background-color: var(--bg-surface-active);
}
}

View file

@ -10,7 +10,6 @@ import LeaveArraowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
import InviteArraowIC from '../../../../public/res/ic/outlined/invite-arrow.svg'; import InviteArraowIC from '../../../../public/res/ic/outlined/invite-arrow.svg';
import InviteCancelArraowIC from '../../../../public/res/ic/outlined/invite-cancel-arrow.svg'; import InviteCancelArraowIC from '../../../../public/res/ic/outlined/invite-cancel-arrow.svg';
import UserIC from '../../../../public/res/ic/outlined/user.svg'; import UserIC from '../../../../public/res/ic/outlined/user.svg';
import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
function TimelineChange({ function TimelineChange({
variant, content, time, onClick, variant, content, time, onClick,
@ -33,9 +32,6 @@ function TimelineChange({
case 'avatar': case 'avatar':
iconSrc = UserIC; iconSrc = UserIC;
break; break;
case 'follow':
iconSrc = TickMarkIC;
break;
default: default:
iconSrc = JoinArraowIC; iconSrc = JoinArraowIC;
break; break;
@ -67,7 +63,6 @@ TimelineChange.propTypes = {
variant: PropTypes.oneOf([ variant: PropTypes.oneOf([
'join', 'leave', 'invite', 'join', 'leave', 'invite',
'invite-cancel', 'avatar', 'other', 'invite-cancel', 'avatar', 'other',
'follow',
]), ]),
content: PropTypes.oneOfType([ content: PropTypes.oneOfType([
PropTypes.string, PropTypes.string,

View file

@ -6,31 +6,19 @@ import parse from 'html-react-parser';
import twemoji from 'twemoji'; import twemoji from 'twemoji';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import { toggleMarkdown } from '../../../client/action/settings'; import { toggleMarkdown } from '../../../client/action/settings';
import * as roomActions from '../../../client/action/room'; import * as roomActions from '../../../client/action/room';
import { import {
selectTab,
selectRoom,
openCreateRoom, openCreateRoom,
openPublicRooms, openPublicRooms,
openInviteUser, openInviteUser,
openReadReceipts,
} from '../../../client/action/navigation'; } from '../../../client/action/navigation';
import { emojis } from '../emoji-board/emoji'; import { emojis } from '../emoji-board/emoji';
import AsyncSearch from '../../../util/AsyncSearch'; import AsyncSearch from '../../../util/AsyncSearch';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button';
import IconButton from '../../atoms/button/IconButton';
import ContextMenu, { MenuHeader } from '../../atoms/context-menu/ContextMenu';
import ScrollView from '../../atoms/scroll/ScrollView'; import ScrollView from '../../atoms/scroll/ScrollView';
import SettingTile from '../../molecules/setting-tile/SettingTile'; import FollowingMembers from '../../molecules/following-members/FollowingMembers';
import TimelineChange from '../../molecules/message/TimelineChange';
import CmdIC from '../../../../public/res/ic/outlined/cmd.svg';
import { getUsersActionJsx } from './common';
const commands = [{ const commands = [{
name: 'markdown', name: 'markdown',
@ -61,121 +49,6 @@ const commands = [{
exe: (roomId, searchTerm) => openInviteUser(roomId, searchTerm), exe: (roomId, searchTerm) => openInviteUser(roomId, searchTerm),
}]; }];
function CmdHelp() {
return (
<ContextMenu
placement="top"
content={(
<>
<MenuHeader>General command</MenuHeader>
<Text variant="b2">/command_name</Text>
<MenuHeader>Go-to commands</MenuHeader>
<Text variant="b2">{'>*space_name'}</Text>
<Text variant="b2">{'>#room_name'}</Text>
<Text variant="b2">{'>@people_name'}</Text>
<MenuHeader>Autofill commands</MenuHeader>
<Text variant="b2">:emoji_name</Text>
<Text variant="b2">@name</Text>
</>
)}
render={(toggleMenu) => (
<IconButton
src={CmdIC}
size="extra-small"
onClick={toggleMenu}
tooltip="Commands"
/>
)}
/>
);
}
function ViewCmd() {
function renderAllCmds() {
return commands.map((command) => (
<SettingTile
key={command.name}
title={command.name}
content={(<Text variant="b3">{command.description}</Text>)}
/>
));
}
return (
<ContextMenu
maxWidth={250}
placement="top"
content={(
<>
<MenuHeader>General commands</MenuHeader>
{renderAllCmds()}
</>
)}
render={(toggleMenu) => (
<span>
<Button onClick={toggleMenu}><span className="text text-b3">View all</span></Button>
</span>
)}
/>
);
}
function FollowingMembers({ roomId, roomTimeline, viewEvent }) {
const [followingMembers, setFollowingMembers] = useState([]);
const mx = initMatrix.matrixClient;
const myUserId = mx.getUserId();
const handleOnMessageSent = () => setFollowingMembers([]);
useEffect(() => {
const updateFollowingMembers = () => {
setFollowingMembers(roomTimeline.getLiveReaders());
};
updateFollowingMembers();
roomTimeline.on(cons.events.roomTimeline.LIVE_RECEIPT, updateFollowingMembers);
viewEvent.on('message_sent', handleOnMessageSent);
return () => {
roomTimeline.removeListener(cons.events.roomTimeline.LIVE_RECEIPT, updateFollowingMembers);
viewEvent.removeListener('message_sent', handleOnMessageSent);
};
}, [roomTimeline]);
const filteredM = followingMembers.filter((userId) => userId !== myUserId);
return filteredM.length !== 0 && (
<TimelineChange
variant="follow"
content={getUsersActionJsx(roomId, filteredM, 'following the conversation.')}
time=""
onClick={() => openReadReceipts(roomId, followingMembers)}
/>
);
}
FollowingMembers.propTypes = {
roomId: PropTypes.string.isRequired,
roomTimeline: PropTypes.shape({}).isRequired,
viewEvent: PropTypes.shape({}).isRequired,
};
function getCmdActivationMessage(prefix) {
function genMessage(prime, secondary) {
return (
<>
<span>{prime}</span>
<span>{secondary}</span>
</>
);
}
const cmd = {
'/': () => genMessage('General command mode activated. ', 'Type command name for suggestions.'),
'>*': () => genMessage('Go-to command mode activated. ', 'Type space name for suggestions.'),
'>#': () => genMessage('Go-to command mode activated. ', 'Type room name for suggestions.'),
'>@': () => genMessage('Go-to command mode activated. ', 'Type people name for suggestions.'),
':': () => genMessage('Emoji autofill command mode activated. ', 'Type emoji shortcut for suggestions.'),
'@': () => genMessage('Name autofill command mode activated. ', 'Type name for suggestions.'),
};
return cmd[prefix]?.();
}
function CmdItem({ onClick, children }) { function CmdItem({ onClick, children }) {
return ( return (
<button className="cmd-item" onClick={onClick} type="button"> <button className="cmd-item" onClick={onClick} type="button">
@ -188,8 +61,8 @@ CmdItem.propTypes = {
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
}; };
function getCmdSuggestions({ prefix, option, suggestions }, fireCmd) { function renderSuggestions({ prefix, option, suggestions }, fireCmd) {
function getGenCmdSuggestions(cmdPrefix, cmds) { function renderCmdSuggestions(cmdPrefix, cmds) {
const cmdOptString = (typeof option === 'string') ? `/${option}` : '/?'; const cmdOptString = (typeof option === 'string') ? `/${option}` : '/?';
return cmds.map((cmd) => ( return cmds.map((cmd) => (
<CmdItem <CmdItem
@ -207,23 +80,7 @@ function getCmdSuggestions({ prefix, option, suggestions }, fireCmd) {
)); ));
} }
function getRoomsSuggestion(cmdPrefix, rooms) { function renderEmojiSuggestion(emPrefix, emos) {
return rooms.map((room) => (
<CmdItem
key={room.roomId}
onClick={() => {
fireCmd({
prefix: cmdPrefix,
result: room,
});
}}
>
<Text variant="b2">{room.name}</Text>
</CmdItem>
));
}
function getEmojiSuggestion(emPrefix, emos) {
return emos.map((emoji) => ( return emos.map((emoji) => (
<CmdItem <CmdItem
key={emoji.hexcode} key={emoji.hexcode}
@ -248,7 +105,7 @@ function getCmdSuggestions({ prefix, option, suggestions }, fireCmd) {
)); ));
} }
function getNameSuggestion(namePrefix, members) { function renderNameSuggestion(namePrefix, members) {
return members.map((member) => ( return members.map((member) => (
<CmdItem <CmdItem
key={member.userId} key={member.userId}
@ -265,12 +122,9 @@ function getCmdSuggestions({ prefix, option, suggestions }, fireCmd) {
} }
const cmd = { const cmd = {
'/': (cmds) => getGenCmdSuggestions(prefix, cmds), '/': (cmds) => renderCmdSuggestions(prefix, cmds),
'>*': (spaces) => getRoomsSuggestion(prefix, spaces), ':': (emos) => renderEmojiSuggestion(prefix, emos),
'>#': (rooms) => getRoomsSuggestion(prefix, rooms), '@': (members) => renderNameSuggestion(prefix, members),
'>@': (peoples) => getRoomsSuggestion(prefix, peoples),
':': (emos) => getEmojiSuggestion(prefix, emos),
'@': (members) => getNameSuggestion(prefix, members),
}; };
return cmd[prefix]?.(suggestions); return cmd[prefix]?.(suggestions);
} }
@ -319,29 +173,28 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
asyncSearch.search(searchTerm); asyncSearch.search(searchTerm);
} }
function activateCmd(prefix) { function activateCmd(prefix) {
setCmd({ prefix });
cmdPrefix = prefix; cmdPrefix = prefix;
cmdPrefix = undefined;
const { roomList, matrixClient } = initMatrix; const mx = initMatrix.matrixClient;
function getRooms(roomIds) {
return roomIds.map((rId) => {
const room = matrixClient.getRoom(rId);
return {
name: room.name,
roomId: room.roomId,
};
});
}
const setupSearch = { const setupSearch = {
'/': () => asyncSearch.setup(commands, { keys: ['name'], isContain: true }), '/': () => {
'>*': () => asyncSearch.setup(getRooms([...roomList.spaces]), { keys: ['name'], limit: 20 }), asyncSearch.setup(commands, { keys: ['name'], isContain: true });
'>#': () => asyncSearch.setup(getRooms([...roomList.rooms]), { keys: ['name'], limit: 20 }), setCmd({ prefix, suggestions: commands });
'>@': () => asyncSearch.setup(getRooms([...roomList.directs]), { keys: ['name'], limit: 20 }), },
':': () => asyncSearch.setup(emojis, { keys: ['shortcode'], isContain: true, limit: 20 }), ':': () => {
'@': () => asyncSearch.setup(matrixClient.getRoom(roomId).getJoinedMembers().map((member) => ({ asyncSearch.setup(emojis, { keys: ['shortcode'], isContain: true, limit: 20 });
name: member.name, setCmd({ prefix, suggestions: emojis.slice(26, 46) });
userId: member.userId.slice(1), },
})), { keys: ['name', 'userId'], limit: 20 }), '@': () => {
const members = mx.getRoom(roomId).getJoinedMembers().map((member) => ({
name: member.name,
userId: member.userId.slice(1),
}));
asyncSearch.setup(members, { keys: ['name', 'userId'], limit: 20 });
const endIndex = members.length > 20 ? 20 : members.length;
setCmd({ prefix, suggestions: members.slice(0, endIndex) });
},
}; };
setupSearch[prefix]?.(); setupSearch[prefix]?.();
} }
@ -351,11 +204,6 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
cmdPrefix = undefined; cmdPrefix = undefined;
} }
function fireCmd(myCmd) { function fireCmd(myCmd) {
if (myCmd.prefix.match(/^>[*#@]$/)) {
if (cmd.prefix === '>*') selectTab(myCmd.result.roomId);
else selectRoom(myCmd.result.roomId);
viewEvent.emit('cmd_fired');
}
if (myCmd.prefix === '/') { if (myCmd.prefix === '/') {
myCmd.result.exe(roomId, myCmd.option); myCmd.result.exe(roomId, myCmd.option);
viewEvent.emit('cmd_fired'); viewEvent.emit('cmd_fired');
@ -421,15 +269,11 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
}; };
}, [cmd]); }, [cmd]);
if (typeof cmd?.error === 'string') { const isError = typeof cmd?.error === 'string';
if (cmd === null || isError) {
return ( return (
<div className="cmd-bar"> <div className="cmd-bar">
<div className="cmd-bar__info"> <FollowingMembers roomTimeline={roomTimeline} />
<div className="cmd-bar__info-indicator--error" />
</div>
<div className="cmd-bar__content">
<Text className="cmd-bar__content-error" variant="b2">{cmd.error}</Text>
</div>
</div> </div>
); );
} }
@ -437,27 +281,14 @@ function RoomViewCmdBar({ roomId, roomTimeline, viewEvent }) {
return ( return (
<div className="cmd-bar"> <div className="cmd-bar">
<div className="cmd-bar__info"> <div className="cmd-bar__info">
{cmd === null && <CmdHelp />} <Text variant="b3">TAB</Text>
{cmd !== null && typeof cmd.suggestions === 'undefined' && <div className="cmd-bar__info-indicator" /> }
{cmd !== null && typeof cmd.suggestions !== 'undefined' && <Text variant="b3">TAB</Text>}
</div> </div>
<div className="cmd-bar__content"> <div className="cmd-bar__content">
{cmd === null && ( <ScrollView horizontal vertical={false} invisible>
<FollowingMembers <div className="cmd-bar__content-suggestions">
roomId={roomId} { renderSuggestions(cmd, fireCmd) }
roomTimeline={roomTimeline} </div>
viewEvent={viewEvent} </ScrollView>
/>
)}
{cmd !== null && typeof cmd.suggestions === 'undefined' && <Text className="cmd-bar__content-help" variant="b2">{getCmdActivationMessage(cmd.prefix)}</Text>}
{cmd !== null && typeof cmd.suggestions !== 'undefined' && (
<ScrollView horizontal vertical={false} invisible>
<div className="cmd-bar__content__suggestions">{getCmdSuggestions(cmd, fireCmd)}</div>
</ScrollView>
)}
</div>
<div className="cmd-bar__more">
{cmd !== null && cmd.prefix === '/' && <ViewCmd />}
</div> </div>
</div> </div>
); );

View file

@ -11,37 +11,14 @@
&__info { &__info {
display: flex; display: flex;
width: calc(2 * var(--sp-extra-loose)); width: 40px;
padding-left: var(--sp-ultra-tight); margin: 0 10px 0 14px;
[dir=rtl] & { [dir=rtl] & {
padding-left: 0; margin: 0 14px 0 10px;
padding-right: var(--sp-ultra-tight);
} }
& > * { & > * {
margin: auto; margin: auto;
} }
& .ic-btn-surface {
padding: 0;
& .ic-raw {
background-color: var(--tc-surface-low);
}
}
& .context-menu .text-b2 {
margin: var(--sp-extra-tight) var(--sp-tight);
}
&-indicator,
&-indicator--error {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--bg-positive);
}
&-indicator--error {
background-color: var(--bg-danger);
}
} }
&__content { &__content {
@ -49,58 +26,14 @@
flex: 1; flex: 1;
display: flex; display: flex;
&-help, &-suggestions {
&-error {
@extend .overflow-ellipsis;
align-self: center;
span {
color: var(--tc-surface-low);
&:first-child {
color: var(--tc-surface-normal)
}
}
}
&-error {
color: var(--bg-danger);
}
&__suggestions {
display: flex;
height: 100%; height: 100%;
white-space: nowrap; white-space: nowrap;
} display: flex;
} align-items: center;
&__more {
display: flex;
& button {
min-width: 0;
height: 100%;
margin: 0 var(--sp-normal);
padding: 0 var(--sp-extra-tight);
box-shadow: none;
border-radius: var(--bo-radius) var(--bo-radius) 0 0;
& .text {
color: var(--tc-surface-normal);
}
}
& .setting-tile {
margin: var(--sp-tight);
}
}
& .timeline-change {
width: 100%;
justify-content: flex-end;
padding: var(--sp-ultra-tight) var(--sp-normal);
border-radius: var(--bo-radius) var(--bo-radius) 0 0;
&__content {
margin: 0;
flex: unset;
& > .text { & > .text {
@extend .overflow-ellipsis; @extend .overflow-ellipsis;
& b {
color: var(--tc-surface-normal);
}
} }
} }
} }

View file

@ -30,7 +30,7 @@ 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';
const CMD_REGEX = /(^\/|^>[#*@]|:|@)(\S*)$/; const CMD_REGEX = /(^\/|:|@)(\S*)$/;
let isTyping = false; let isTyping = false;
let isCmdActivated = false; let isCmdActivated = false;
let cmdCursorPos = null; let cmdCursorPos = null;
@ -45,8 +45,6 @@ function RoomViewInput({
const inputBaseRef = useRef(null); const inputBaseRef = useRef(null);
const uploadInputRef = useRef(null); const uploadInputRef = useRef(null);
const uploadProgressRef = useRef(null); const uploadProgressRef = useRef(null);
const rightOptionsRef = useRef(null);
const escBtnRef = useRef(null);
const TYPING_TIMEOUT = 5000; const TYPING_TIMEOUT = 5000;
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -90,30 +88,11 @@ function RoomViewInput({
uploadInputRef.current.value = null; uploadInputRef.current.value = null;
} }
function rightOptionsA11Y(A11Y) {
const rightOptions = rightOptionsRef.current.children;
for (let index = 0; index < rightOptions.length; index += 1) {
rightOptions[index].disabled = !A11Y;
}
}
function activateCmd(prefix) { function activateCmd(prefix) {
isCmdActivated = true; isCmdActivated = true;
requestAnimationFrame(() => {
inputBaseRef.current.style.boxShadow = '0 0 0 1px var(--bg-positive)';
escBtnRef.current.style.display = 'block';
});
rightOptionsA11Y(false);
viewEvent.emit('cmd_activate', prefix); viewEvent.emit('cmd_activate', prefix);
} }
function deactivateCmd() { function deactivateCmd() {
if (inputBaseRef.current !== null) {
requestAnimationFrame(() => {
inputBaseRef.current.style.boxShadow = 'var(--bs-surface-border)';
escBtnRef.current.style.display = 'none';
});
rightOptionsA11Y(true);
}
isCmdActivated = false; isCmdActivated = false;
cmdCursorPos = null; cmdCursorPos = null;
} }
@ -121,11 +100,6 @@ function RoomViewInput({
deactivateCmd(); deactivateCmd();
viewEvent.emit('cmd_deactivate'); viewEvent.emit('cmd_deactivate');
} }
function errorCmd() {
requestAnimationFrame(() => {
inputBaseRef.current.style.boxShadow = '0 0 0 1px var(--bg-danger)';
});
}
function setCursorPosition(pos) { function setCursorPosition(pos) {
setTimeout(() => { setTimeout(() => {
textAreaRef.current.focus(); textAreaRef.current.focus();
@ -163,7 +137,6 @@ function RoomViewInput({
roomsInput.on(cons.events.roomsInput.UPLOAD_PROGRESS_CHANGES, uploadingProgress); roomsInput.on(cons.events.roomsInput.UPLOAD_PROGRESS_CHANGES, uploadingProgress);
roomsInput.on(cons.events.roomsInput.ATTACHMENT_CANCELED, clearAttachment); roomsInput.on(cons.events.roomsInput.ATTACHMENT_CANCELED, clearAttachment);
roomsInput.on(cons.events.roomsInput.FILE_UPLOADED, clearAttachment); roomsInput.on(cons.events.roomsInput.FILE_UPLOADED, clearAttachment);
viewEvent.on('cmd_error', errorCmd);
viewEvent.on('cmd_fired', firedCmd); viewEvent.on('cmd_fired', firedCmd);
navigation.on(cons.events.navigation.REPLY_TO_CLICKED, setUpReply); navigation.on(cons.events.navigation.REPLY_TO_CLICKED, setUpReply);
if (textAreaRef?.current !== null) { if (textAreaRef?.current !== null) {
@ -177,7 +150,6 @@ function RoomViewInput({
roomsInput.removeListener(cons.events.roomsInput.UPLOAD_PROGRESS_CHANGES, uploadingProgress); roomsInput.removeListener(cons.events.roomsInput.UPLOAD_PROGRESS_CHANGES, uploadingProgress);
roomsInput.removeListener(cons.events.roomsInput.ATTACHMENT_CANCELED, clearAttachment); roomsInput.removeListener(cons.events.roomsInput.ATTACHMENT_CANCELED, clearAttachment);
roomsInput.removeListener(cons.events.roomsInput.FILE_UPLOADED, clearAttachment); roomsInput.removeListener(cons.events.roomsInput.FILE_UPLOADED, clearAttachment);
viewEvent.removeListener('cmd_error', errorCmd);
viewEvent.removeListener('cmd_fired', firedCmd); viewEvent.removeListener('cmd_fired', firedCmd);
navigation.removeListener(cons.events.navigation.REPLY_TO_CLICKED, setUpReply); navigation.removeListener(cons.events.navigation.REPLY_TO_CLICKED, setUpReply);
if (isCmdActivated) deactivateCmd(); if (isCmdActivated) deactivateCmd();
@ -193,7 +165,7 @@ function RoomViewInput({
}; };
}, [roomId]); }, [roomId]);
async function sendMessage() { const sendMessage = async () => {
const msgBody = textAreaRef.current.value; const msgBody = textAreaRef.current.value;
if (roomsInput.isSending(roomId)) return; if (roomsInput.isSending(roomId)) return;
if (msgBody.trim() === '' && attachment === null) return; if (msgBody.trim() === '' && attachment === null) return;
@ -214,7 +186,7 @@ function RoomViewInput({
viewEvent.emit('message_sent'); viewEvent.emit('message_sent');
textAreaRef.current.style.height = 'unset'; textAreaRef.current.style.height = 'unset';
if (replyTo !== null) setReplyTo(null); if (replyTo !== null) setReplyTo(null);
} };
function processTyping(msg) { function processTyping(msg) {
const isEmptyMsg = msg === ''; const isEmptyMsg = msg === '';
@ -259,19 +231,16 @@ function RoomViewInput({
return; return;
} }
if (!isCmdActivated) activateCmd(cmdPrefix); if (!isCmdActivated) activateCmd(cmdPrefix);
requestAnimationFrame(() => {
inputBaseRef.current.style.boxShadow = '0 0 0 1px var(--bg-caution)';
});
viewEvent.emit('cmd_process', cmdPrefix, cmdSlug); viewEvent.emit('cmd_process', cmdPrefix, cmdSlug);
} }
function handleMsgTyping(e) { const handleMsgTyping = (e) => {
const msg = e.target.value; const msg = e.target.value;
recognizeCmd(e.target.value); recognizeCmd(e.target.value);
if (!isCmdActivated) processTyping(msg); if (!isCmdActivated) processTyping(msg);
} };
function handleKeyDown(e) { const handleKeyDown = (e) => {
if (e.keyCode === 13 && e.shiftKey === false) { if (e.keyCode === 13 && e.shiftKey === false) {
e.preventDefault(); e.preventDefault();
@ -279,13 +248,9 @@ function RoomViewInput({
viewEvent.emit('cmd_exe'); viewEvent.emit('cmd_exe');
} else sendMessage(); } else sendMessage();
} }
if (e.keyCode === 27 && isCmdActivated) { };
deactivateCmdAndEmit();
e.preventDefault();
}
}
function handlePaste(e) { const handlePaste = (e) => {
if (e.clipboardData === false) { if (e.clipboardData === false) {
return; return;
} }
@ -309,19 +274,19 @@ function RoomViewInput({
} }
} }
} }
} };
function addEmoji(emoji) { function addEmoji(emoji) {
textAreaRef.current.value += emoji.unicode; textAreaRef.current.value += emoji.unicode;
textAreaRef.current.focus(); textAreaRef.current.focus();
} }
function handleUploadClick() { const handleUploadClick = () => {
if (attachment === null) uploadInputRef.current.click(); if (attachment === null) uploadInputRef.current.click();
else { else {
roomsInput.cancelAttachment(roomId); roomsInput.cancelAttachment(roomId);
} }
} };
function uploadFileChange(e) { function uploadFileChange(e) {
const file = e.target.files.item(0); const file = e.target.files.item(0);
setAttachment(file); setAttachment(file);
@ -356,9 +321,8 @@ function RoomViewInput({
</Text> </Text>
</ScrollView> </ScrollView>
{isMarkdown && <RawIcon size="extra-small" src={MarkdownIC} />} {isMarkdown && <RawIcon size="extra-small" src={MarkdownIC} />}
<button ref={escBtnRef} tabIndex="-1" onClick={deactivateCmdAndEmit} className="btn-cmd-esc" type="button"><Text variant="b3">ESC</Text></button>
</div> </div>
<div ref={rightOptionsRef} className="room-input__option-container"> <div className="room-input__option-container">
<IconButton <IconButton
onClick={(e) => { onClick={(e) => {
const cords = getEventCords(e); const cords = getEventCords(e);