diff --git a/packages/react/src/hooks/useSetExclusiveState.js b/packages/react/src/hooks/useSetExclusiveState.js index 9a59fdb41..df4ebc2a6 100644 --- a/packages/react/src/hooks/useSetExclusiveState.js +++ b/packages/react/src/hooks/useSetExclusiveState.js @@ -10,6 +10,7 @@ import { useStarredMessageStore, useFileStore, useSidebarStore, + useKeyboardShortcutStore, } from '../store'; const useSetExclusiveState = () => { @@ -31,6 +32,9 @@ const useSetExclusiveState = () => { const setShowChannelinfo = useChannelStore( (state) => state.setShowChannelinfo ); + const setShowKeyboardShortcuts = useKeyboardShortcutStore( + (state) => state.setShowKeyboardShortcuts + ); const stateSetters = useMemo( () => [ setShowStarred, @@ -42,12 +46,14 @@ const useSetExclusiveState = () => { setShowAllFiles, setShowMentions, setShowCurrentUserInfo, + setShowKeyboardShortcuts, ], [ setShowAllFiles, setShowAllThreads, setShowChannelinfo, setShowCurrentUserInfo, + setShowKeyboardShortcuts, setShowMembers, setShowMentions, setShowPinned, diff --git a/packages/react/src/store/index.js b/packages/react/src/store/index.js index bddd0bd1d..9ea6f6e47 100644 --- a/packages/react/src/store/index.js +++ b/packages/react/src/store/index.js @@ -11,3 +11,4 @@ export { default as useMentionsStore } from './mentionsStore'; export { default as usePinnedMessageStore } from './pinnedMessageStore'; export { default as useStarredMessageStore } from './starredMessageStore'; export { default as useSidebarStore } from './sidebarStore'; +export { default as useKeyboardShortcutStore } from './keyboardShortcutStore'; diff --git a/packages/react/src/store/keyboardShortcutStore.js b/packages/react/src/store/keyboardShortcutStore.js new file mode 100644 index 000000000..aeea56e4a --- /dev/null +++ b/packages/react/src/store/keyboardShortcutStore.js @@ -0,0 +1,9 @@ +import { create } from 'zustand'; + +const useKeyboardShortcutStore = create((set) => ({ + showKeyboardShortcuts: false, + setShowKeyboardShortcuts: (showKeyboardShortcuts) => + set(() => ({ showKeyboardShortcuts })), +})); + +export default useKeyboardShortcutStore; diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index 0986104ae..073f4a6a9 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -24,6 +24,7 @@ import { useStarredMessageStore, useFileStore, useSidebarStore, + useKeyboardShortcutStore, } from '../../store'; import { DynamicHeader } from '../DynamicHeader'; import useFetchChatData from '../../hooks/useFetchChatData'; @@ -50,6 +51,7 @@ const ChatHeader = ({ 'members', 'search', 'rInfo', + 'keyboard', 'logout', ], }, @@ -123,6 +125,10 @@ const ChatHeader = ({ const host = RCInstance.getHost(); return `${host}/avatar/${channelname}`; }; + const setShowKeyboardShortcuts = useKeyboardShortcutStore( + (state) => state.setShowKeyboardShortcuts + ); + const handleGoBack = async () => { if (isUserAuthenticated) { getMessagesAndRoles(); @@ -300,6 +306,13 @@ const ChatHeader = ({ iconName: 'info', visible: isUserAuthenticated, }, + keyboard: { + label: 'Keyboard Shortcuts', + id: 'keyboard', + onClick: () => setExclusiveState(setShowKeyboardShortcuts), + iconName: 'key', + visible: true, + }, logout: { label: 'Logout', id: 'logout', @@ -324,6 +337,7 @@ const ChatHeader = ({ setShowAllFiles, setShowSearch, setShowChannelinfo, + setShowKeyboardShortcuts, ] ); diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index e753b689a..43d98c6a1 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -19,7 +19,9 @@ import { useLoginStore, useChannelStore, useMemberStore, + useSearchMessageStore, } from '../../store'; +import useSetExclusiveState from '../../hooks/useSetExclusiveState'; import ChatInputFormattingToolbar from './ChatInputFormattingToolbar'; import useAttachmentWindowStore from '../../store/attachmentwindow'; import MembersList from '../Mentions/MembersList'; @@ -127,6 +129,9 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { const userInfo = { _id: userId, username, name }; + const setShowSearch = useSearchMessageStore((state) => state.setShowSearch); + const setExclusiveState = useSetExclusiveState(); + const dispatchToastMessage = useToastBarDispatch(); const showCommands = useShowCommands( commands, @@ -449,6 +454,26 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { formatSelection(messageRef, '*{{text}}*'); break; } + case (e.ctrlKey || e.metaKey) && e.code === 'KeyK': + e.preventDefault(); + setExclusiveState(setShowSearch); + break; + case e.code === 'ArrowUp' && + !e.shiftKey && + !e.ctrlKey && + !e.metaKey && + !e.altKey: { + const { value } = messageRef.current; + if (!value || value.trim() === '') { + const { messages } = useMessageStore.getState(); + const lastOwnMsg = messages.find((m) => m.u?.username === username); + if (lastOwnMsg) { + e.preventDefault(); + setEditMessage(lastOwnMsg); + } + } + break; + } case (e.ctrlKey || e.metaKey || e.shiftKey) && e.code === 'Enter': e.preventDefault(); handleNewLine(e); diff --git a/packages/react/src/views/ChatLayout/ChatLayout.js b/packages/react/src/views/ChatLayout/ChatLayout.js index f3b4262ac..beeeb379a 100644 --- a/packages/react/src/views/ChatLayout/ChatLayout.js +++ b/packages/react/src/views/ChatLayout/ChatLayout.js @@ -12,6 +12,7 @@ import { useThreadsMessageStore, useMemberStore, useSidebarStore, + useKeyboardShortcutStore, } from '../../store'; import RoomMembers from '../RoomMembers/RoomMember'; @@ -22,6 +23,7 @@ import PinnedMessages from '../MessageAggregators/PinnedMessages'; import SearchMessages from '../MessageAggregators/SearchMessages'; import FileGallery from '../MessageAggregators/FileGallery'; import Roominfo from '../RoomInformation/RoomInformation'; +import KeyboardShortcuts from '../KeyboardShortcuts/KeyboardShortcuts'; import UserInformation from '../UserInformation/UserInformation'; import ChatBody from '../ChatBody/ChatBody'; import ChatInput from '../ChatInput/ChatInput'; @@ -61,6 +63,9 @@ const ChatLayout = () => { const showCurrentUserInfo = useUserStore( (state) => state.showCurrentUserInfo ); + const showKeyboardShortcuts = useKeyboardShortcutStore( + (state) => state.showKeyboardShortcuts + ); const attachmentWindowOpen = useAttachmentWindowStore( (state) => state.attachmentWindowOpen ); @@ -134,6 +139,7 @@ const ChatLayout = () => { {showPinned && } {showStarred && } {showCurrentUserInfo && } + {showKeyboardShortcuts && } {uiKitContextualBarOpen && ( ({ + list: css` + display: flex; + flex-direction: column; + padding: 0.75rem; + gap: 0.25rem; + `, + row: css` + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 0.5rem; + border-bottom: 1px solid ${theme.colors.border}; + gap: 0.75rem; + `, + action: css` + font-size: 0.875rem; + color: ${theme.colors.foreground}; + flex: 1; + `, + keys: css` + font-size: 0.8125rem; + font-family: monospace; + color: ${theme.colors.secondaryForeground}; + background: ${theme.colors.background}; + border: 1px solid ${theme.colors.border}; + border-radius: 4px; + padding: 0.2rem 0.5rem; + white-space: nowrap; + `, +}); + +const KeyboardShortcuts = () => { + const { theme } = useTheme(); + const styles = getStyles(theme); + const setExclusiveState = useSetExclusiveState(); + + return ( + setExclusiveState(null)} + > + + {shortcuts.map((shortcut) => ( + + {shortcut.action} + {shortcut.keys} + + ))} + + + ); +}; + +export default KeyboardShortcuts;