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;
initialFocus: false,
onDeactivate: () => setJumpAnchor(undefined),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Menu variant="Surface">
IconButton,
} from 'folds';
import FocusTrap from 'focus-trap-react';
+import { stopPropagation } from '../utils/keyboard';
export type UIAFlowOverlayProps = {
currentStep: number;
}: 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}
import { CustomEditor, useEditor } from './Editor';
import { Toolbar } from './Toolbar';
+import { stopPropagation } from '../../utils/keyboard';
export function EditorPreview() {
const [open, setOpen] = useState(false);
initialFocus: false,
onDeactivate: () => setOpen(false),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Modal size="500">
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 (
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 }}>
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;
allowOutsideClick: true,
isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt),
+ escapeDeactivates: stopPropagation,
}}
>
<Menu className={css.AutocompleteMenu}>
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';
!editableActiveElement() && isKeyHotkey(['arrowdown', 'arrowright'], evt),
isKeyBackward: (evt: KeyboardEvent) =>
!editableActiveElement() && isKeyHotkey(['arrowup', 'arrowleft'], evt),
+ escapeDeactivates: stopPropagation,
}}
>
<EmojiBoardLayout
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;
initialFocus: false,
onDeactivate: onCancel,
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Dialog variant="Surface">
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;
initialFocus: false,
onDeactivate: onCancel,
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Dialog variant="Surface">
mimeTypeToExt,
} from '../../../utils/mimeTypes';
import * as css from './style.css';
+import { stopPropagation } from '../../../utils/keyboard';
const renderErrorButton = (retry: () => void, text: string) => (
<TooltipProvider
initialFocus: false,
onDeactivate: () => setTextViewer(false),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Modal
initialFocus: false,
onDeactivate: () => setPdfViewer(false),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Modal
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;
initialFocus: false,
onDeactivate: () => setViewer(false),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Modal
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';
initialFocus: false,
clickOutsideDeactivates: true,
onDeactivate: closeError,
+ escapeDeactivates: stopPropagation,
}}
>
<Dialog variant="Surface">
initialFocus: false,
clickOutsideDeactivates: true,
onDeactivate: closeTopic,
+ escapeDeactivates: stopPropagation,
}}
>
{renderTopicViewer(roomName, roomTopic, closeTopic)}
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;
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<Menu style={{ maxWidth: toRem(150), width: '100vw' }}>
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;
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<LobbyMenu
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();
initialFocus: false,
clickOutsideDeactivates: true,
onDeactivate: () => setViewTopic(false),
+ escapeDeactivates: stopPropagation,
}}
>
<RoomTopicViewer
} 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';
initialFocus: false,
clickOutsideDeactivates: true,
onDeactivate: () => setView(false),
+ escapeDeactivates: stopPropagation,
}}
>
<RoomTopicViewer
import { ErrorCode } from '../../cs-errorcode';
import { useDraggableItem } from './DnD';
import { openCreateRoom, openSpaceAddExisting } from '../../../client/action/navigation';
+import { stopPropagation } from '../../utils/keyboard';
function SpaceProfileLoading() {
return (
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<Menu style={{ padding: config.space.S100 }}>
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<Menu style={{ padding: config.space.S100 }}>
} from '../../hooks/useAsyncSearch';
import { DebounceOptions, useDebounce } from '../../hooks/useDebounce';
import { VirtualTile } from '../../components/virtualizer';
+import { stopPropagation } from '../../utils/keyboard';
type OrderButtonProps = {
order?: string;
initialFocus: false,
onDeactivate: () => setMenuAnchor(undefined),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Menu variant="Surface">
initialFocus: false,
onDeactivate: () => setMenuAnchor(undefined),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Menu variant="Surface" style={{ width: toRem(250) }}>
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;
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<RoomNavItemMenu
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,
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<Menu style={{ padding: config.space.S100 }}>
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<Menu style={{ padding: config.space.S100 }}>
-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';
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();
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">
-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';
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();
? 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 />
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;
initialFocus: false,
onDeactivate: () => setOpen(false),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Modal variant="Surface" size="300">
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;
initialFocus: false,
clickOutsideDeactivates: true,
onDeactivate: () => setViewTopic(false),
+ escapeDeactivates: stopPropagation,
}}
>
<RoomTopicViewer
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<RoomMenu
} 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;
returnFocusOnDeactivate: false,
onDeactivate: () => handleClose(),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Modal variant="Surface" size="300">
initialFocus: false,
onDeactivate: handleClose,
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Modal variant="Surface" size="300">
initialFocus: false,
onDeactivate: handleClose,
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Modal variant="Surface" size="500">
initialFocus: false,
onDeactivate: handleClose,
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Dialog variant="Surface">
initialFocus: false,
onDeactivate: handleClose,
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Dialog variant="Surface">
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<Menu>
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<Menu {...props} ref={ref}>
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;
returnFocusOnDeactivate: false,
onDeactivate: () => setViewer(false),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Modal variant="Surface" size="300">
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect, useRef, useCallback } from 'react';
import { useAtomValue } from 'jotai';
import './Search.scss';
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);
}
}, [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];
import FocusTrap from 'focus-trap-react';
import { useDebounce } from '../../hooks/useDebounce';
+import { stopPropagation } from '../../utils/keyboard';
export function ServerPicker({
server,
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<Menu>
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>();
initialFocus: false,
onDeactivate: () => setAnchor(undefined),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Menu>
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';
useEffect(() => {
const handleStart = () => {
- initHotkeys();
setLoading(false);
};
initMatrix.once('init_loading_finished', handleStart);
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;
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<DirectMenu requestClose={() => setMenuAnchor(undefined)} />
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();
initialFocus: false,
clickOutsideDeactivates: true,
onDeactivate: () => setDialog(false),
+ escapeDeactivates: stopPropagation,
}}
>
<Dialog variant="Surface">
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(
initialFocus: false,
onDeactivate: () => setMenuAnchor(undefined),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Menu variant="Surface">
initialFocus: false,
onDeactivate: () => setMenuAnchor(undefined),
clickOutsideDeactivates: true,
+ escapeDeactivates: stopPropagation,
}}
>
<Menu variant="Surface">
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;
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<HomeMenu requestClose={() => setMenuAnchor(undefined)} />
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';
initialFocus: false,
clickOutsideDeactivates: true,
onDeactivate: closeTopic,
+ escapeDeactivates: stopPropagation,
}}
>
<RoomTopicViewer
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;
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<DirectMenu requestClose={() => setMenuAnchor(undefined)} />
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;
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<HomeMenu requestClose={() => setMenuAnchor(undefined)} />
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;
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<SpaceMenu
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;
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
}}
>
<SpaceMenu room={space} requestClose={() => setMenuAnchor(undefined)} />
callback();
}
};
+
+export const stopPropagation = (evt: KeyboardEvent): boolean => {
+ evt.stopPropagation();
+ return true;
+};
+++ /dev/null
-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 };