From 41733c6f1817e22a589cb53596db7efe2580bf33 Mon Sep 17 00:00:00 2001
From: Ajay Bura <32841439+ajbura@users.noreply.github.com>
Date: Thu, 18 Jul 2024 09:39:34 +0530
Subject: [PATCH] make hotkeys work again

---
 src/app/components/Pdf-viewer/PdfViewer.tsx   |  2 +
 src/app/components/UIAFlowOverlay.tsx         |  3 +-
 src/app/components/editor/Editor.preview.tsx  |  2 +
 src/app/components/editor/Toolbar.tsx         |  2 +
 .../editor/autocomplete/AutocompleteMenu.tsx  |  3 +-
 src/app/components/emoji-board/EmojiBoard.tsx |  3 +-
 .../leave-room-prompt/LeaveRoomPrompt.tsx     |  2 +
 .../leave-space-prompt/LeaveSpacePrompt.tsx   |  2 +
 .../message/content/FileContent.tsx           |  3 +
 .../message/content/ImageContent.tsx          |  2 +
 src/app/components/room-card/RoomCard.tsx     |  4 +-
 src/app/features/lobby/HierarchyItemMenu.tsx  |  2 +
 src/app/features/lobby/LobbyHeader.tsx        |  2 +
 src/app/features/lobby/LobbyHero.tsx          |  3 +-
 src/app/features/lobby/RoomItem.tsx           |  3 +-
 src/app/features/lobby/SpaceItem.tsx          |  3 +
 .../features/message-search/SearchFilters.tsx |  3 +
 src/app/features/room-nav/RoomNavItem.tsx     |  2 +
 src/app/features/room/MembersDrawer.tsx       |  3 +
 src/app/features/room/Room.tsx                | 17 +++++-
 src/app/features/room/RoomView.tsx            | 60 +++++++++++++++++--
 src/app/features/room/RoomViewFollowing.tsx   |  2 +
 src/app/features/room/RoomViewHeader.tsx      |  3 +
 src/app/features/room/message/Message.tsx     |  8 +++
 src/app/features/room/message/Reactions.tsx   |  2 +
 src/app/organisms/search/Search.jsx           | 25 +++++++-
 src/app/pages/auth/ServerPicker.tsx           |  2 +
 .../pages/auth/login/PasswordLoginForm.tsx    |  2 +
 src/app/pages/client/ClientRoot.tsx           |  2 -
 src/app/pages/client/direct/Direct.tsx        |  2 +
 src/app/pages/client/explore/Explore.tsx      |  2 +
 src/app/pages/client/explore/Server.tsx       |  3 +
 src/app/pages/client/home/Home.tsx            |  2 +
 src/app/pages/client/inbox/Invites.tsx        |  3 +-
 src/app/pages/client/sidebar/DirectTab.tsx    |  2 +
 src/app/pages/client/sidebar/HomeTab.tsx      |  2 +
 src/app/pages/client/sidebar/SpaceTabs.tsx    |  2 +
 src/app/pages/client/space/Space.tsx          |  2 +
 src/app/utils/keyboard.ts                     |  5 ++
 src/client/event/hotkeys.js                   | 24 --------
 40 files changed, 182 insertions(+), 39 deletions(-)
 delete mode 100644 src/client/event/hotkeys.js

diff --git a/src/app/components/Pdf-viewer/PdfViewer.tsx b/src/app/components/Pdf-viewer/PdfViewer.tsx
index a78c13f2..9c7fd980 100644
--- a/src/app/components/Pdf-viewer/PdfViewer.tsx
+++ b/src/app/components/Pdf-viewer/PdfViewer.tsx
@@ -26,6 +26,7 @@ import * as css from './PdfViewer.css';
 import { AsyncStatus } from '../../hooks/useAsyncCallback';
 import { useZoom } from '../../hooks/useZoom';
 import { createPage, usePdfDocumentLoader, usePdfJSLoader } from '../../plugins/pdfjs-dist';
+import { stopPropagation } from '../../utils/keyboard';
 
 export type PdfViewerProps = {
   name: string;
@@ -201,6 +202,7 @@ export const PdfViewer = as<'div', PdfViewerProps>(
                       initialFocus: false,
                       onDeactivate: () => setJumpAnchor(undefined),
                       clickOutsideDeactivates: true,
+                      escapeDeactivates: stopPropagation,
                     }}
                   >
                     <Menu variant="Surface">
diff --git a/src/app/components/UIAFlowOverlay.tsx b/src/app/components/UIAFlowOverlay.tsx
index f788eb0f..dc517b48 100644
--- a/src/app/components/UIAFlowOverlay.tsx
+++ b/src/app/components/UIAFlowOverlay.tsx
@@ -13,6 +13,7 @@ import {
   IconButton,
 } from 'folds';
 import FocusTrap from 'focus-trap-react';
+import { stopPropagation } from '../utils/keyboard';
 
 export type UIAFlowOverlayProps = {
   currentStep: number;
@@ -28,7 +29,7 @@ export function UIAFlowOverlay({
 }: UIAFlowOverlayProps) {
   return (
     <Overlay open backdrop={<OverlayBackdrop />}>
-      <FocusTrap focusTrapOptions={{ initialFocus: false }}>
+      <FocusTrap focusTrapOptions={{ initialFocus: false, escapeDeactivates: stopPropagation }}>
         <Box style={{ height: '100%' }} direction="Column" grow="Yes" gap="400">
           <Box grow="Yes" direction="Column" alignItems="Center" justifyContent="Center">
             {children}
diff --git a/src/app/components/editor/Editor.preview.tsx b/src/app/components/editor/Editor.preview.tsx
index ad67dc12..b760dddc 100644
--- a/src/app/components/editor/Editor.preview.tsx
+++ b/src/app/components/editor/Editor.preview.tsx
@@ -14,6 +14,7 @@ import {
 
 import { CustomEditor, useEditor } from './Editor';
 import { Toolbar } from './Toolbar';
+import { stopPropagation } from '../../utils/keyboard';
 
 export function EditorPreview() {
   const [open, setOpen] = useState(false);
@@ -32,6 +33,7 @@ export function EditorPreview() {
               initialFocus: false,
               onDeactivate: () => setOpen(false),
               clickOutsideDeactivates: true,
+              escapeDeactivates: stopPropagation,
             }}
           >
             <Modal size="500">
diff --git a/src/app/components/editor/Toolbar.tsx b/src/app/components/editor/Toolbar.tsx
index 0c82855d..e5c7d16e 100644
--- a/src/app/components/editor/Toolbar.tsx
+++ b/src/app/components/editor/Toolbar.tsx
@@ -35,6 +35,7 @@ import { isMacOS } from '../../utils/user-agent';
 import { KeySymbol } from '../../utils/key-symbol';
 import { useSetting } from '../../state/hooks/settings';
 import { settingsAtom } from '../../state/settings';
+import { stopPropagation } from '../../utils/keyboard';
 
 function BtnTooltip({ text, shortCode }: { text: string; shortCode?: string }) {
   return (
@@ -151,6 +152,7 @@ export function HeadingBlockButton() {
             isKeyForward: (evt: KeyboardEvent) =>
               evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
             isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Menu style={{ padding: config.space.S100 }}>
diff --git a/src/app/components/editor/autocomplete/AutocompleteMenu.tsx b/src/app/components/editor/autocomplete/AutocompleteMenu.tsx
index fc4327da..5d2d917c 100644
--- a/src/app/components/editor/autocomplete/AutocompleteMenu.tsx
+++ b/src/app/components/editor/autocomplete/AutocompleteMenu.tsx
@@ -4,7 +4,7 @@ import { isKeyHotkey } from 'is-hotkey';
 import { Header, Menu, Scroll, config } from 'folds';
 
 import * as css from './AutocompleteMenu.css';
-import { preventScrollWithArrowKey } from '../../../utils/keyboard';
+import { preventScrollWithArrowKey, stopPropagation } from '../../../utils/keyboard';
 
 type AutocompleteMenuProps = {
   requestClose: () => void;
@@ -24,6 +24,7 @@ export function AutocompleteMenu({ headerContent, requestClose, children }: Auto
             allowOutsideClick: true,
             isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
             isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt),
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Menu className={css.AutocompleteMenu}>
diff --git a/src/app/components/emoji-board/EmojiBoard.tsx b/src/app/components/emoji-board/EmojiBoard.tsx
index 408ce85d..53172efd 100644
--- a/src/app/components/emoji-board/EmojiBoard.tsx
+++ b/src/app/components/emoji-board/EmojiBoard.tsx
@@ -37,7 +37,7 @@ import * as css from './EmojiBoard.css';
 import { EmojiGroupId, IEmoji, IEmojiGroup, emojiGroups, emojis } from '../../plugins/emoji';
 import { IEmojiGroupLabels, useEmojiGroupLabels } from './useEmojiGroupLabels';
 import { IEmojiGroupIcons, useEmojiGroupIcons } from './useEmojiGroupIcons';
-import { preventScrollWithArrowKey } from '../../utils/keyboard';
+import { preventScrollWithArrowKey, stopPropagation } from '../../utils/keyboard';
 import { useRelevantImagePacks } from '../../hooks/useImagePacks';
 import { useMatrixClient } from '../../hooks/useMatrixClient';
 import { useRecentEmoji } from '../../hooks/useRecentEmoji';
@@ -775,6 +775,7 @@ export function EmojiBoard({
           !editableActiveElement() && isKeyHotkey(['arrowdown', 'arrowright'], evt),
         isKeyBackward: (evt: KeyboardEvent) =>
           !editableActiveElement() && isKeyHotkey(['arrowup', 'arrowleft'], evt),
+        escapeDeactivates: stopPropagation,
       }}
     >
       <EmojiBoardLayout
diff --git a/src/app/components/leave-room-prompt/LeaveRoomPrompt.tsx b/src/app/components/leave-room-prompt/LeaveRoomPrompt.tsx
index f4de9edf..217491e6 100644
--- a/src/app/components/leave-room-prompt/LeaveRoomPrompt.tsx
+++ b/src/app/components/leave-room-prompt/LeaveRoomPrompt.tsx
@@ -19,6 +19,7 @@ import {
 import { MatrixError } from 'matrix-js-sdk';
 import { useMatrixClient } from '../../hooks/useMatrixClient';
 import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
+import { stopPropagation } from '../../utils/keyboard';
 
 type LeaveRoomPromptProps = {
   roomId: string;
@@ -52,6 +53,7 @@ export function LeaveRoomPrompt({ roomId, onDone, onCancel }: LeaveRoomPromptPro
             initialFocus: false,
             onDeactivate: onCancel,
             clickOutsideDeactivates: true,
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Dialog variant="Surface">
diff --git a/src/app/components/leave-space-prompt/LeaveSpacePrompt.tsx b/src/app/components/leave-space-prompt/LeaveSpacePrompt.tsx
index 1132b44d..8709b942 100644
--- a/src/app/components/leave-space-prompt/LeaveSpacePrompt.tsx
+++ b/src/app/components/leave-space-prompt/LeaveSpacePrompt.tsx
@@ -19,6 +19,7 @@ import {
 import { MatrixError } from 'matrix-js-sdk';
 import { useMatrixClient } from '../../hooks/useMatrixClient';
 import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
+import { stopPropagation } from '../../utils/keyboard';
 
 type LeaveSpacePromptProps = {
   roomId: string;
@@ -52,6 +53,7 @@ export function LeaveSpacePrompt({ roomId, onDone, onCancel }: LeaveSpacePromptP
             initialFocus: false,
             onDeactivate: onCancel,
             clickOutsideDeactivates: true,
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Dialog variant="Surface">
diff --git a/src/app/components/message/content/FileContent.tsx b/src/app/components/message/content/FileContent.tsx
index af064a32..f09c1e07 100644
--- a/src/app/components/message/content/FileContent.tsx
+++ b/src/app/components/message/content/FileContent.tsx
@@ -29,6 +29,7 @@ import {
   mimeTypeToExt,
 } from '../../../utils/mimeTypes';
 import * as css from './style.css';
+import { stopPropagation } from '../../../utils/keyboard';
 
 const renderErrorButton = (retry: () => void, text: string) => (
   <TooltipProvider
@@ -101,6 +102,7 @@ export function ReadTextFile({ body, mimeType, url, encInfo, renderViewer }: Rea
                 initialFocus: false,
                 onDeactivate: () => setTextViewer(false),
                 clickOutsideDeactivates: true,
+                escapeDeactivates: stopPropagation,
               }}
             >
               <Modal
@@ -184,6 +186,7 @@ export function ReadPdfFile({ body, mimeType, url, encInfo, renderViewer }: Read
                 initialFocus: false,
                 onDeactivate: () => setPdfViewer(false),
                 clickOutsideDeactivates: true,
+                escapeDeactivates: stopPropagation,
               }}
             >
               <Modal
diff --git a/src/app/components/message/content/ImageContent.tsx b/src/app/components/message/content/ImageContent.tsx
index a64b8e91..90c46354 100644
--- a/src/app/components/message/content/ImageContent.tsx
+++ b/src/app/components/message/content/ImageContent.tsx
@@ -26,6 +26,7 @@ import { getFileSrcUrl } from './util';
 import * as css from './style.css';
 import { bytesToSize } from '../../../utils/common';
 import { FALLBACK_MIMETYPE } from '../../../utils/mimeTypes';
+import { stopPropagation } from '../../../utils/keyboard';
 
 type RenderViewerProps = {
   src: string;
@@ -108,6 +109,7 @@ export const ImageContent = as<'div', ImageContentProps>(
                   initialFocus: false,
                   onDeactivate: () => setViewer(false),
                   clickOutsideDeactivates: true,
+                  escapeDeactivates: stopPropagation,
                 }}
               >
                 <Modal
diff --git a/src/app/components/room-card/RoomCard.tsx b/src/app/components/room-card/RoomCard.tsx
index 370b790d..79dd87db 100644
--- a/src/app/components/room-card/RoomCard.tsx
+++ b/src/app/components/room-card/RoomCard.tsx
@@ -26,7 +26,7 @@ import { nameInitials } from '../../utils/common';
 import { millify } from '../../plugins/millify';
 import { useMatrixClient } from '../../hooks/useMatrixClient';
 import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
-import { onEnterOrSpace } from '../../utils/keyboard';
+import { onEnterOrSpace, stopPropagation } from '../../utils/keyboard';
 import { RoomType, StateEvent } from '../../../types/matrix/room';
 import { useJoinedRoomId } from '../../hooks/useJoinedRoomId';
 import { useElementSizeObserver } from '../../hooks/useElementSizeObserver';
@@ -107,6 +107,7 @@ function ErrorDialog({
               initialFocus: false,
               clickOutsideDeactivates: true,
               onDeactivate: closeError,
+              escapeDeactivates: stopPropagation,
             }}
           >
             <Dialog variant="Surface">
@@ -236,6 +237,7 @@ export const RoomCard = as<'div', RoomCardProps>(
                   initialFocus: false,
                   clickOutsideDeactivates: true,
                   onDeactivate: closeTopic,
+                  escapeDeactivates: stopPropagation,
                 }}
               >
                 {renderTopicViewer(roomName, roomTopic, closeTopic)}
diff --git a/src/app/features/lobby/HierarchyItemMenu.tsx b/src/app/features/lobby/HierarchyItemMenu.tsx
index 489bb9ba..30a4f632 100644
--- a/src/app/features/lobby/HierarchyItemMenu.tsx
+++ b/src/app/features/lobby/HierarchyItemMenu.tsx
@@ -27,6 +27,7 @@ import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
 import { UseStateProvider } from '../../components/UseStateProvider';
 import { LeaveSpacePrompt } from '../../components/leave-space-prompt';
 import { LeaveRoomPrompt } from '../../components/leave-room-prompt';
+import { stopPropagation } from '../../utils/keyboard';
 
 type HierarchyItemWithParent = HierarchyItem & {
   parentId: string;
@@ -227,6 +228,7 @@ export function HierarchyItemMenu({
                 clickOutsideDeactivates: true,
                 isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                 isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                escapeDeactivates: stopPropagation,
               }}
             >
               <Menu style={{ maxWidth: toRem(150), width: '100vw' }}>
diff --git a/src/app/features/lobby/LobbyHeader.tsx b/src/app/features/lobby/LobbyHeader.tsx
index a23faada..e01d3ad5 100644
--- a/src/app/features/lobby/LobbyHeader.tsx
+++ b/src/app/features/lobby/LobbyHeader.tsx
@@ -30,6 +30,7 @@ import { openInviteUser, openSpaceSettings } from '../../../client/action/naviga
 import { IPowerLevels, usePowerLevelsAPI } from '../../hooks/usePowerLevels';
 import { UseStateProvider } from '../../components/UseStateProvider';
 import { LeaveSpacePrompt } from '../../components/leave-space-prompt';
+import { stopPropagation } from '../../utils/keyboard';
 
 type LobbyMenuProps = {
   roomId: string;
@@ -197,6 +198,7 @@ export function LobbyHeader({ showProfile, powerLevels }: LobbyHeaderProps) {
                   clickOutsideDeactivates: true,
                   isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                   isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                  escapeDeactivates: stopPropagation,
                 }}
               >
                 <LobbyMenu
diff --git a/src/app/features/lobby/LobbyHero.tsx b/src/app/features/lobby/LobbyHero.tsx
index a92a49f3..a2b31e8f 100644
--- a/src/app/features/lobby/LobbyHero.tsx
+++ b/src/app/features/lobby/LobbyHero.tsx
@@ -10,7 +10,7 @@ import { UseStateProvider } from '../../components/UseStateProvider';
 import { RoomTopicViewer } from '../../components/room-topic-viewer';
 import * as css from './LobbyHero.css';
 import { PageHero } from '../../components/page';
-import { onEnterOrSpace } from '../../utils/keyboard';
+import { onEnterOrSpace, stopPropagation } from '../../utils/keyboard';
 
 export function LobbyHero() {
   const mx = useMatrixClient();
@@ -46,6 +46,7 @@ export function LobbyHero() {
                         initialFocus: false,
                         clickOutsideDeactivates: true,
                         onDeactivate: () => setViewTopic(false),
+                        escapeDeactivates: stopPropagation,
                       }}
                     >
                       <RoomTopicViewer
diff --git a/src/app/features/lobby/RoomItem.tsx b/src/app/features/lobby/RoomItem.tsx
index 4e7dd6af..3cc425f2 100644
--- a/src/app/features/lobby/RoomItem.tsx
+++ b/src/app/features/lobby/RoomItem.tsx
@@ -31,7 +31,7 @@ import {
 } from '../../components/RoomSummaryLoader';
 import { UseStateProvider } from '../../components/UseStateProvider';
 import { RoomTopicViewer } from '../../components/room-topic-viewer';
-import { onEnterOrSpace } from '../../utils/keyboard';
+import { onEnterOrSpace, stopPropagation } from '../../utils/keyboard';
 import { Membership, RoomType } from '../../../types/matrix/room';
 import * as css from './RoomItem.css';
 import * as styleCss from './style.css';
@@ -264,6 +264,7 @@ function RoomProfile({
                           initialFocus: false,
                           clickOutsideDeactivates: true,
                           onDeactivate: () => setView(false),
+                          escapeDeactivates: stopPropagation,
                         }}
                       >
                         <RoomTopicViewer
diff --git a/src/app/features/lobby/SpaceItem.tsx b/src/app/features/lobby/SpaceItem.tsx
index e9d9356a..04f7e2cc 100644
--- a/src/app/features/lobby/SpaceItem.tsx
+++ b/src/app/features/lobby/SpaceItem.tsx
@@ -34,6 +34,7 @@ import * as styleCss from './style.css';
 import { ErrorCode } from '../../cs-errorcode';
 import { useDraggableItem } from './DnD';
 import { openCreateRoom, openSpaceAddExisting } from '../../../client/action/navigation';
+import { stopPropagation } from '../../utils/keyboard';
 
 function SpaceProfileLoading() {
   return (
@@ -277,6 +278,7 @@ function AddRoomButton({ item }: { item: HierarchyItem }) {
             clickOutsideDeactivates: true,
             isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
             isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Menu style={{ padding: config.space.S100 }}>
@@ -338,6 +340,7 @@ function AddSpaceButton({ item }: { item: HierarchyItem }) {
             clickOutsideDeactivates: true,
             isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
             isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Menu style={{ padding: config.space.S100 }}>
diff --git a/src/app/features/message-search/SearchFilters.tsx b/src/app/features/message-search/SearchFilters.tsx
index 5de188d4..929dd1e9 100644
--- a/src/app/features/message-search/SearchFilters.tsx
+++ b/src/app/features/message-search/SearchFilters.tsx
@@ -38,6 +38,7 @@ import {
 } from '../../hooks/useAsyncSearch';
 import { DebounceOptions, useDebounce } from '../../hooks/useDebounce';
 import { VirtualTile } from '../../components/virtualizer';
+import { stopPropagation } from '../../utils/keyboard';
 
 type OrderButtonProps = {
   order?: string;
@@ -66,6 +67,7 @@ function OrderButton({ order, onChange }: OrderButtonProps) {
             initialFocus: false,
             onDeactivate: () => setMenuAnchor(undefined),
             clickOutsideDeactivates: true,
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Menu variant="Surface">
@@ -202,6 +204,7 @@ function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButto
             initialFocus: false,
             onDeactivate: () => setMenuAnchor(undefined),
             clickOutsideDeactivates: true,
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Menu variant="Surface" style={{ width: toRem(250) }}>
diff --git a/src/app/features/room-nav/RoomNavItem.tsx b/src/app/features/room-nav/RoomNavItem.tsx
index fce62375..8ecc81a3 100644
--- a/src/app/features/room-nav/RoomNavItem.tsx
+++ b/src/app/features/room-nav/RoomNavItem.tsx
@@ -36,6 +36,7 @@ import { LeaveRoomPrompt } from '../../components/leave-room-prompt';
 import { useClientConfig } from '../../hooks/useClientConfig';
 import { useRoomTypingMember } from '../../hooks/useRoomTypingMembers';
 import { TypingIndicator } from '../../components/typing-indicator';
+import { stopPropagation } from '../../utils/keyboard';
 
 type RoomNavItemMenuProps = {
   room: Room;
@@ -269,6 +270,7 @@ export function RoomNavItem({
                   clickOutsideDeactivates: true,
                   isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                   isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                  escapeDeactivates: stopPropagation,
                 }}
               >
                 <RoomNavItemMenu
diff --git a/src/app/features/room/MembersDrawer.tsx b/src/app/features/room/MembersDrawer.tsx
index 8a96b84a..70a9aa34 100644
--- a/src/app/features/room/MembersDrawer.tsx
+++ b/src/app/features/room/MembersDrawer.tsx
@@ -55,6 +55,7 @@ import { millify } from '../../plugins/millify';
 import { ScrollTopContainer } from '../../components/scroll-top-container';
 import { UserAvatar } from '../../components/user-avatar';
 import { useRoomTypingMember } from '../../hooks/useRoomTypingMembers';
+import { stopPropagation } from '../../utils/keyboard';
 
 export const MembershipFilters = {
   filterJoined: (m: RoomMember) => m.membership === Membership.Join,
@@ -300,6 +301,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
                             clickOutsideDeactivates: true,
                             isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                             isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                            escapeDeactivates: stopPropagation,
                           }}
                         >
                           <Menu style={{ padding: config.space.S100 }}>
@@ -358,6 +360,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
                             clickOutsideDeactivates: true,
                             isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                             isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                            escapeDeactivates: stopPropagation,
                           }}
                         >
                           <Menu style={{ padding: config.space.S100 }}>
diff --git a/src/app/features/room/Room.tsx b/src/app/features/room/Room.tsx
index 764e9686..fd578ec6 100644
--- a/src/app/features/room/Room.tsx
+++ b/src/app/features/room/Room.tsx
@@ -1,6 +1,7 @@
-import React from 'react';
+import React, { useCallback } from 'react';
 import { Box, Line } from 'folds';
 import { useParams } from 'react-router-dom';
+import { isKeyHotkey } from 'is-hotkey';
 import { RoomView } from './RoomView';
 import { MembersDrawer } from './MembersDrawer';
 import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
@@ -8,6 +9,8 @@ import { useSetting } from '../../state/hooks/settings';
 import { settingsAtom } from '../../state/settings';
 import { PowerLevelsContextProvider, usePowerLevels } from '../../hooks/usePowerLevels';
 import { useRoom } from '../../hooks/useRoom';
+import { useKeyDown } from '../../hooks/useKeyDown';
+import { markAsRead } from '../../../client/action/notifications';
 
 export function Room() {
   const { eventId } = useParams();
@@ -17,6 +20,18 @@ export function Room() {
   const screenSize = useScreenSizeContext();
   const powerLevels = usePowerLevels(room);
 
+  useKeyDown(
+    window,
+    useCallback(
+      (evt) => {
+        if (isKeyHotkey('escape', evt)) {
+          markAsRead(room.roomId);
+        }
+      },
+      [room.roomId]
+    )
+  );
+
   return (
     <PowerLevelsContextProvider value={powerLevels}>
       <Box grow="Yes">
diff --git a/src/app/features/room/RoomView.tsx b/src/app/features/room/RoomView.tsx
index fe145b37..84162fcc 100644
--- a/src/app/features/room/RoomView.tsx
+++ b/src/app/features/room/RoomView.tsx
@@ -1,7 +1,8 @@
-import React, { useRef } from 'react';
+import React, { useCallback, useRef } from 'react';
 import { Box, Text, config } from 'folds';
 import { EventType, Room } from 'matrix-js-sdk';
-
+import { ReactEditor } from 'slate-react';
+import { isKeyHotkey } from 'is-hotkey';
 import { useStateEvent } from '../../hooks/useStateEvent';
 import { StateEvent } from '../../../types/matrix/room';
 import { usePowerLevelsAPI, usePowerLevelsContext } from '../../hooks/usePowerLevels';
@@ -15,10 +16,42 @@ import { RoomInput } from './RoomInput';
 import { RoomViewFollowing } from './RoomViewFollowing';
 import { Page } from '../../components/page';
 import { RoomViewHeader } from './RoomViewHeader';
+import { useKeyDown } from '../../hooks/useKeyDown';
+import { editableActiveElement } from '../../utils/dom';
+import navigation from '../../../client/state/navigation';
+
+const shouldFocusMessageField = (evt: KeyboardEvent): boolean => {
+  const { code } = evt;
+  console.log(code);
+  if (evt.metaKey || evt.altKey || evt.ctrlKey) {
+    return false;
+  }
+  // do not focus on F keys
+  if (/^F\d+$/.test(code)) return false;
+
+  // do not focus on numlock/scroll lock
+  if (
+    code.startsWith('OS') ||
+    code.startsWith('Meta') ||
+    code.startsWith('Shift') ||
+    code.startsWith('Alt') ||
+    code.startsWith('Control') ||
+    code.startsWith('Arrow') ||
+    code === 'Tab' ||
+    code === 'Space' ||
+    code === 'Enter' ||
+    code === 'NumLock' ||
+    code === 'ScrollLock'
+  ) {
+    return false;
+  }
+
+  return true;
+};
 
 export function RoomView({ room, eventId }: { room: Room; eventId?: string }) {
-  const roomInputRef = useRef(null);
-  const roomViewRef = useRef(null);
+  const roomInputRef = useRef<HTMLDivElement>(null);
+  const roomViewRef = useRef<HTMLDivElement>(null);
 
   const { roomId } = room;
   const editor = useEditor();
@@ -33,6 +66,25 @@ export function RoomView({ room, eventId }: { room: Room; eventId?: string }) {
     ? canSendEvent(EventType.RoomMessage, getPowerLevel(myUserId))
     : false;
 
+  useKeyDown(
+    window,
+    useCallback(
+      (evt) => {
+        if (editableActiveElement()) return;
+        if (
+          document.body.lastElementChild?.className !== 'ReactModalPortal' ||
+          navigation.isRawModalVisible
+        ) {
+          return;
+        }
+        if (shouldFocusMessageField(evt) || isKeyHotkey('mod+v')) {
+          ReactEditor.focus(editor);
+        }
+      },
+      [editor]
+    )
+  );
+
   return (
     <Page ref={roomViewRef}>
       <RoomViewHeader />
diff --git a/src/app/features/room/RoomViewFollowing.tsx b/src/app/features/room/RoomViewFollowing.tsx
index 2f7a583e..58d3f64f 100644
--- a/src/app/features/room/RoomViewFollowing.tsx
+++ b/src/app/features/room/RoomViewFollowing.tsx
@@ -22,6 +22,7 @@ import { useMatrixClient } from '../../hooks/useMatrixClient';
 import { useRoomLatestRenderedEvent } from '../../hooks/useRoomLatestRenderedEvent';
 import { useRoomEventReaders } from '../../hooks/useRoomEventReaders';
 import { EventReaders } from '../../components/event-readers';
+import { stopPropagation } from '../../utils/keyboard';
 
 export type RoomViewFollowingProps = {
   room: Room;
@@ -50,6 +51,7 @@ export const RoomViewFollowing = as<'div', RoomViewFollowingProps>(
                   initialFocus: false,
                   onDeactivate: () => setOpen(false),
                   clickOutsideDeactivates: true,
+                  escapeDeactivates: stopPropagation,
                 }}
               >
                 <Modal variant="Surface" size="300">
diff --git a/src/app/features/room/RoomViewHeader.tsx b/src/app/features/room/RoomViewHeader.tsx
index 61c730f7..aa267c53 100644
--- a/src/app/features/room/RoomViewHeader.tsx
+++ b/src/app/features/room/RoomViewHeader.tsx
@@ -57,6 +57,7 @@ import { useRoomAvatar, useRoomName, useRoomTopic } from '../../hooks/useRoomMet
 import { mDirectAtom } from '../../state/mDirectList';
 import { useClientConfig } from '../../hooks/useClientConfig';
 import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
+import { stopPropagation } from '../../utils/keyboard';
 
 type RoomMenuProps = {
   room: Room;
@@ -240,6 +241,7 @@ export function RoomViewHeader() {
                             initialFocus: false,
                             clickOutsideDeactivates: true,
                             onDeactivate: () => setViewTopic(false),
+                            escapeDeactivates: stopPropagation,
                           }}
                         >
                           <RoomTopicViewer
@@ -331,6 +333,7 @@ export function RoomViewHeader() {
                   clickOutsideDeactivates: true,
                   isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                   isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                  escapeDeactivates: stopPropagation,
                 }}
               >
                 <RoomMenu
diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx
index 70d5c555..6db366ac 100644
--- a/src/app/features/room/message/Message.tsx
+++ b/src/app/features/room/message/Message.tsx
@@ -74,6 +74,7 @@ import {
 } from '../../../pages/pathUtils';
 import { copyToClipboard } from '../../../utils/dom';
 import { useClientConfig } from '../../../hooks/useClientConfig';
+import { stopPropagation } from '../../../utils/keyboard';
 
 export type ReactionHandler = (keyOrMxc: string, shortcode: string) => void;
 
@@ -148,6 +149,7 @@ export const MessageAllReactionItem = as<
               returnFocusOnDeactivate: false,
               onDeactivate: () => handleClose(),
               clickOutsideDeactivates: true,
+              escapeDeactivates: stopPropagation,
             }}
           >
             <Modal variant="Surface" size="300">
@@ -201,6 +203,7 @@ export const MessageReadReceiptItem = as<
               initialFocus: false,
               onDeactivate: handleClose,
               clickOutsideDeactivates: true,
+              escapeDeactivates: stopPropagation,
             }}
           >
             <Modal variant="Surface" size="300">
@@ -278,6 +281,7 @@ export const MessageSourceCodeItem = as<
               initialFocus: false,
               onDeactivate: handleClose,
               clickOutsideDeactivates: true,
+              escapeDeactivates: stopPropagation,
             }}
           >
             <Modal variant="Surface" size="500">
@@ -401,6 +405,7 @@ export const MessageDeleteItem = as<
               initialFocus: false,
               onDeactivate: handleClose,
               clickOutsideDeactivates: true,
+              escapeDeactivates: stopPropagation,
             }}
           >
             <Dialog variant="Surface">
@@ -530,6 +535,7 @@ export const MessageReportItem = as<
               initialFocus: false,
               onDeactivate: handleClose,
               clickOutsideDeactivates: true,
+              escapeDeactivates: stopPropagation,
             }}
           >
             <Dialog variant="Surface">
@@ -875,6 +881,7 @@ export const Message = as<'div', MessageProps>(
                         clickOutsideDeactivates: true,
                         isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                         isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                        escapeDeactivates: stopPropagation,
                       }}
                     >
                       <Menu>
@@ -1089,6 +1096,7 @@ export const Event = as<'div', EventProps>(
                         clickOutsideDeactivates: true,
                         isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                         isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                        escapeDeactivates: stopPropagation,
                       }}
                     >
                       <Menu {...props} ref={ref}>
diff --git a/src/app/features/room/message/Reactions.tsx b/src/app/features/room/message/Reactions.tsx
index 728cf810..a6d7f553 100644
--- a/src/app/features/room/message/Reactions.tsx
+++ b/src/app/features/room/message/Reactions.tsx
@@ -21,6 +21,7 @@ import { Reaction, ReactionTooltipMsg } from '../../../components/message';
 import { useRelations } from '../../../hooks/useRelations';
 import * as css from './styles.css';
 import { ReactionViewer } from '../reaction-viewer';
+import { stopPropagation } from '../../../utils/keyboard';
 
 export type ReactionsProps = {
   room: Room;
@@ -105,6 +106,7 @@ export const Reactions = as<'div', ReactionsProps>(
                   returnFocusOnDeactivate: false,
                   onDeactivate: () => setViewer(false),
                   clickOutsideDeactivates: true,
+                  escapeDeactivates: stopPropagation,
                 }}
               >
                 <Modal variant="Surface" size="300">
diff --git a/src/app/organisms/search/Search.jsx b/src/app/organisms/search/Search.jsx
index c9d1d991..0990a03f 100644
--- a/src/app/organisms/search/Search.jsx
+++ b/src/app/organisms/search/Search.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect, useRef, useCallback } from 'react';
 import { useAtomValue } from 'jotai';
 import './Search.scss';
 
@@ -25,6 +25,8 @@ import { roomToUnreadAtom } from '../../state/room/roomToUnread';
 import { roomToParentsAtom } from '../../state/room/roomToParents';
 import { allRoomsAtom } from '../../state/room-list/roomList';
 import { mDirectAtom } from '../../state/mDirectList';
+import { useKeyDown } from '../../hooks/useKeyDown';
+import { openSearch } from '../../../client/action/navigation';
 
 function useVisiblityToggle(setResult) {
   const [isOpen, setIsOpen] = useState(false);
@@ -49,6 +51,27 @@ function useVisiblityToggle(setResult) {
     }
   }, [isOpen]);
 
+  useKeyDown(
+    window,
+    useCallback((event) => {
+      // Ctrl/Cmd +
+      if (event.ctrlKey || event.metaKey) {
+        // open search modal
+        if (event.key === 'k') {
+          event.preventDefault();
+          // means some menu or modal window is open
+          if (
+            document.body.lastChild.className !== 'ReactModalPortal' ||
+            navigation.isRawModalVisible
+          ) {
+            return;
+          }
+          openSearch();
+        }
+      }
+    }, [])
+  );
+
   const requestClose = () => setIsOpen(false);
 
   return [isOpen, requestClose];
diff --git a/src/app/pages/auth/ServerPicker.tsx b/src/app/pages/auth/ServerPicker.tsx
index 18201c98..a2a78106 100644
--- a/src/app/pages/auth/ServerPicker.tsx
+++ b/src/app/pages/auth/ServerPicker.tsx
@@ -22,6 +22,7 @@ import {
 import FocusTrap from 'focus-trap-react';
 
 import { useDebounce } from '../../hooks/useDebounce';
+import { stopPropagation } from '../../utils/keyboard';
 
 export function ServerPicker({
   server,
@@ -103,6 +104,7 @@ export function ServerPicker({
                   clickOutsideDeactivates: true,
                   isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                   isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                  escapeDeactivates: stopPropagation,
                 }}
               >
                 <Menu>
diff --git a/src/app/pages/auth/login/PasswordLoginForm.tsx b/src/app/pages/auth/login/PasswordLoginForm.tsx
index b9dd14b7..087d384d 100644
--- a/src/app/pages/auth/login/PasswordLoginForm.tsx
+++ b/src/app/pages/auth/login/PasswordLoginForm.tsx
@@ -36,6 +36,7 @@ import {
 import { PasswordInput } from '../../../components/password-input/PasswordInput';
 import { FieldError } from '../FiledError';
 import { getResetPasswordPath } from '../../pathUtils';
+import { stopPropagation } from '../../../utils/keyboard';
 
 function UsernameHint({ server }: { server: string }) {
   const [anchor, setAnchor] = useState<RectCords>();
@@ -54,6 +55,7 @@ function UsernameHint({ server }: { server: string }) {
             initialFocus: false,
             onDeactivate: () => setAnchor(undefined),
             clickOutsideDeactivates: true,
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Menu>
diff --git a/src/app/pages/client/ClientRoot.tsx b/src/app/pages/client/ClientRoot.tsx
index 6a1dbcb1..c36adfc6 100644
--- a/src/app/pages/client/ClientRoot.tsx
+++ b/src/app/pages/client/ClientRoot.tsx
@@ -1,7 +1,6 @@
 import { Box, Spinner, Text } from 'folds';
 import React, { ReactNode, useEffect, useState } from 'react';
 import initMatrix from '../../../client/initMatrix';
-import { initHotkeys } from '../../../client/event/hotkeys';
 import { getSecret } from '../../../client/state/auth';
 import { SplashScreen } from '../../components/splash-screen';
 import { CapabilitiesAndMediaConfigLoader } from '../../components/CapabilitiesAndMediaConfigLoader';
@@ -47,7 +46,6 @@ export function ClientRoot({ children }: ClientRootProps) {
 
   useEffect(() => {
     const handleStart = () => {
-      initHotkeys();
       setLoading(false);
     };
     initMatrix.once('init_loading_finished', handleStart);
diff --git a/src/app/pages/client/direct/Direct.tsx b/src/app/pages/client/direct/Direct.tsx
index 673a5d9f..c62ef160 100644
--- a/src/app/pages/client/direct/Direct.tsx
+++ b/src/app/pages/client/direct/Direct.tsx
@@ -44,6 +44,7 @@ import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page
 import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
 import { useRoomsUnread } from '../../../state/hooks/unread';
 import { markAsRead } from '../../../../client/action/notifications';
+import { stopPropagation } from '../../../utils/keyboard';
 
 type DirectMenuProps = {
   requestClose: () => void;
@@ -118,6 +119,7 @@ function DirectHeader() {
               clickOutsideDeactivates: true,
               isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
               isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+              escapeDeactivates: stopPropagation,
             }}
           >
             <DirectMenu requestClose={() => setMenuAnchor(undefined)} />
diff --git a/src/app/pages/client/explore/Explore.tsx b/src/app/pages/client/explore/Explore.tsx
index 67f8dc3f..420e1a16 100644
--- a/src/app/pages/client/explore/Explore.tsx
+++ b/src/app/pages/client/explore/Explore.tsx
@@ -36,6 +36,7 @@ import { getMxIdServer } from '../../../utils/matrix';
 import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
 import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
 import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
+import { stopPropagation } from '../../../utils/keyboard';
 
 export function AddServer() {
   const mx = useMatrixClient();
@@ -80,6 +81,7 @@ export function AddServer() {
               initialFocus: false,
               clickOutsideDeactivates: true,
               onDeactivate: () => setDialog(false),
+              escapeDeactivates: stopPropagation,
             }}
           >
             <Dialog variant="Surface">
diff --git a/src/app/pages/client/explore/Server.tsx b/src/app/pages/client/explore/Server.tsx
index 9fe4e78e..1a81c225 100644
--- a/src/app/pages/client/explore/Server.tsx
+++ b/src/app/pages/client/explore/Server.tsx
@@ -41,6 +41,7 @@ import * as css from './style.css';
 import { allRoomsAtom } from '../../../state/room-list/roomList';
 import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
 import { getMxIdServer } from '../../../utils/matrix';
+import { stopPropagation } from '../../../utils/keyboard';
 
 const useServerSearchParams = (searchParams: URLSearchParams): ExploreServerPathSearchParams =>
   useMemo(
@@ -182,6 +183,7 @@ function ThirdPartyProtocolsSelector({
             initialFocus: false,
             onDeactivate: () => setMenuAnchor(undefined),
             clickOutsideDeactivates: true,
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Menu variant="Surface">
@@ -277,6 +279,7 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
             initialFocus: false,
             onDeactivate: () => setMenuAnchor(undefined),
             clickOutsideDeactivates: true,
+            escapeDeactivates: stopPropagation,
           }}
         >
           <Menu variant="Surface">
diff --git a/src/app/pages/client/home/Home.tsx b/src/app/pages/client/home/Home.tsx
index 33714191..76034cfe 100644
--- a/src/app/pages/client/home/Home.tsx
+++ b/src/app/pages/client/home/Home.tsx
@@ -47,6 +47,7 @@ import { PageNav, PageNavHeader, PageNavContent } from '../../../components/page
 import { useRoomsUnread } from '../../../state/hooks/unread';
 import { markAsRead } from '../../../../client/action/notifications';
 import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
+import { stopPropagation } from '../../../utils/keyboard';
 
 type HomeMenuProps = {
   requestClose: () => void;
@@ -121,6 +122,7 @@ function HomeHeader() {
               clickOutsideDeactivates: true,
               isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
               isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+              escapeDeactivates: stopPropagation,
             }}
           >
             <HomeMenu requestClose={() => setMenuAnchor(undefined)} />
diff --git a/src/app/pages/client/inbox/Invites.tsx b/src/app/pages/client/inbox/Invites.tsx
index 91dc0291..06e5f6c6 100644
--- a/src/app/pages/client/inbox/Invites.tsx
+++ b/src/app/pages/client/inbox/Invites.tsx
@@ -34,7 +34,7 @@ import { RoomAvatar } from '../../../components/room-avatar';
 import { addRoomIdToMDirect, getMxIdLocalPart, guessDmRoomUserId } from '../../../utils/matrix';
 import { Time } from '../../../components/message';
 import { useElementSizeObserver } from '../../../hooks/useElementSizeObserver';
-import { onEnterOrSpace } from '../../../utils/keyboard';
+import { onEnterOrSpace, stopPropagation } from '../../../utils/keyboard';
 import { RoomTopicViewer } from '../../../components/room-topic-viewer';
 import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
 import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
@@ -140,6 +140,7 @@ function InviteCard({ room, userId, direct, compact, onNavigate }: InviteCardPro
                       initialFocus: false,
                       clickOutsideDeactivates: true,
                       onDeactivate: closeTopic,
+                      escapeDeactivates: stopPropagation,
                     }}
                   >
                     <RoomTopicViewer
diff --git a/src/app/pages/client/sidebar/DirectTab.tsx b/src/app/pages/client/sidebar/DirectTab.tsx
index f25d7bdc..0beb17d8 100644
--- a/src/app/pages/client/sidebar/DirectTab.tsx
+++ b/src/app/pages/client/sidebar/DirectTab.tsx
@@ -22,6 +22,7 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
 import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
 import { useDirectRooms } from '../direct/useDirectRooms';
 import { markAsRead } from '../../../../client/action/notifications';
+import { stopPropagation } from '../../../utils/keyboard';
 
 type DirectMenuProps = {
   requestClose: () => void;
@@ -120,6 +121,7 @@ export function DirectTab() {
                 clickOutsideDeactivates: true,
                 isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                 isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                escapeDeactivates: stopPropagation,
               }}
             >
               <DirectMenu requestClose={() => setMenuAnchor(undefined)} />
diff --git a/src/app/pages/client/sidebar/HomeTab.tsx b/src/app/pages/client/sidebar/HomeTab.tsx
index 0b5135ca..41f7c648 100644
--- a/src/app/pages/client/sidebar/HomeTab.tsx
+++ b/src/app/pages/client/sidebar/HomeTab.tsx
@@ -23,6 +23,7 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
 import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
 import { useHomeRooms } from '../home/useHomeRooms';
 import { markAsRead } from '../../../../client/action/notifications';
+import { stopPropagation } from '../../../utils/keyboard';
 
 type HomeMenuProps = {
   requestClose: () => void;
@@ -122,6 +123,7 @@ export function HomeTab() {
                 clickOutsideDeactivates: true,
                 isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                 isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                escapeDeactivates: stopPropagation,
               }}
             >
               <HomeMenu requestClose={() => setMenuAnchor(undefined)} />
diff --git a/src/app/pages/client/sidebar/SpaceTabs.tsx b/src/app/pages/client/sidebar/SpaceTabs.tsx
index 99c04965..8635a35f 100644
--- a/src/app/pages/client/sidebar/SpaceTabs.tsx
+++ b/src/app/pages/client/sidebar/SpaceTabs.tsx
@@ -90,6 +90,7 @@ import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
 import { markAsRead } from '../../../../client/action/notifications';
 import { copyToClipboard } from '../../../utils/dom';
 import { openInviteUser, openSpaceSettings } from '../../../../client/action/navigation';
+import { stopPropagation } from '../../../utils/keyboard';
 
 type SpaceMenuProps = {
   room: Room;
@@ -463,6 +464,7 @@ function SpaceTab({
                     clickOutsideDeactivates: true,
                     isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                     isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                    escapeDeactivates: stopPropagation,
                   }}
                 >
                   <SpaceMenu
diff --git a/src/app/pages/client/space/Space.tsx b/src/app/pages/client/space/Space.tsx
index 8eab6b49..e947373b 100644
--- a/src/app/pages/client/space/Space.tsx
+++ b/src/app/pages/client/space/Space.tsx
@@ -73,6 +73,7 @@ import { useClientConfig } from '../../../hooks/useClientConfig';
 import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
 import { useStateEvent } from '../../../hooks/useStateEvent';
 import { StateEvent } from '../../../../types/matrix/room';
+import { stopPropagation } from '../../../utils/keyboard';
 
 type SpaceMenuProps = {
   room: Room;
@@ -248,6 +249,7 @@ function SpaceHeader() {
                 clickOutsideDeactivates: true,
                 isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                 isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+                escapeDeactivates: stopPropagation,
               }}
             >
               <SpaceMenu room={space} requestClose={() => setMenuAnchor(undefined)} />
diff --git a/src/app/utils/keyboard.ts b/src/app/utils/keyboard.ts
index 8ec435d3..da3fe8cb 100644
--- a/src/app/utils/keyboard.ts
+++ b/src/app/utils/keyboard.ts
@@ -30,3 +30,8 @@ export const onEnterOrSpace = (callback: () => void) => (evt: KeyboardEventLike)
     callback();
   }
 };
+
+export const stopPropagation = (evt: KeyboardEvent): boolean => {
+  evt.stopPropagation();
+  return true;
+};
diff --git a/src/client/event/hotkeys.js b/src/client/event/hotkeys.js
deleted file mode 100644
index 856fcadc..00000000
--- a/src/client/event/hotkeys.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { openSearch } from '../action/navigation';
-import navigation from '../state/navigation';
-
-function listenKeyboard(event) {
-  // Ctrl/Cmd +
-  if (event.ctrlKey || event.metaKey) {
-    // open search modal
-    if (event.key === 'k') {
-      event.preventDefault();
-      if (navigation.isRawModalVisible) return;
-      openSearch();
-    }
-  }
-}
-
-function initHotkeys() {
-  document.body.addEventListener('keydown', listenKeyboard);
-}
-
-function removeHotkeys() {
-  document.body.removeEventListener('keydown', listenKeyboard);
-}
-
-export { initHotkeys, removeHotkeys };