Make hotkeys work again (#1819)
This commit is contained in:
parent
c52c4f7d32
commit
c4abe39375
40 changed files with 182 additions and 39 deletions
|
@ -26,6 +26,7 @@ import * as css from './PdfViewer.css';
|
||||||
import { AsyncStatus } from '../../hooks/useAsyncCallback';
|
import { AsyncStatus } from '../../hooks/useAsyncCallback';
|
||||||
import { useZoom } from '../../hooks/useZoom';
|
import { useZoom } from '../../hooks/useZoom';
|
||||||
import { createPage, usePdfDocumentLoader, usePdfJSLoader } from '../../plugins/pdfjs-dist';
|
import { createPage, usePdfDocumentLoader, usePdfJSLoader } from '../../plugins/pdfjs-dist';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
export type PdfViewerProps = {
|
export type PdfViewerProps = {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -201,6 +202,7 @@ export const PdfViewer = as<'div', PdfViewerProps>(
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setJumpAnchor(undefined),
|
onDeactivate: () => setJumpAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu variant="Surface">
|
<Menu variant="Surface">
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
IconButton,
|
IconButton,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
import { stopPropagation } from '../utils/keyboard';
|
||||||
|
|
||||||
export type UIAFlowOverlayProps = {
|
export type UIAFlowOverlayProps = {
|
||||||
currentStep: number;
|
currentStep: number;
|
||||||
|
@ -28,7 +29,7 @@ export function UIAFlowOverlay({
|
||||||
}: UIAFlowOverlayProps) {
|
}: UIAFlowOverlayProps) {
|
||||||
return (
|
return (
|
||||||
<Overlay open backdrop={<OverlayBackdrop />}>
|
<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 style={{ height: '100%' }} direction="Column" grow="Yes" gap="400">
|
||||||
<Box grow="Yes" direction="Column" alignItems="Center" justifyContent="Center">
|
<Box grow="Yes" direction="Column" alignItems="Center" justifyContent="Center">
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
|
|
||||||
import { CustomEditor, useEditor } from './Editor';
|
import { CustomEditor, useEditor } from './Editor';
|
||||||
import { Toolbar } from './Toolbar';
|
import { Toolbar } from './Toolbar';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
export function EditorPreview() {
|
export function EditorPreview() {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
@ -32,6 +33,7 @@ export function EditorPreview() {
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setOpen(false),
|
onDeactivate: () => setOpen(false),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal size="500">
|
<Modal size="500">
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { isMacOS } from '../../utils/user-agent';
|
||||||
import { KeySymbol } from '../../utils/key-symbol';
|
import { KeySymbol } from '../../utils/key-symbol';
|
||||||
import { useSetting } from '../../state/hooks/settings';
|
import { useSetting } from '../../state/hooks/settings';
|
||||||
import { settingsAtom } from '../../state/settings';
|
import { settingsAtom } from '../../state/settings';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
function BtnTooltip({ text, shortCode }: { text: string; shortCode?: string }) {
|
function BtnTooltip({ text, shortCode }: { text: string; shortCode?: string }) {
|
||||||
return (
|
return (
|
||||||
|
@ -151,6 +152,7 @@ export function HeadingBlockButton() {
|
||||||
isKeyForward: (evt: KeyboardEvent) =>
|
isKeyForward: (evt: KeyboardEvent) =>
|
||||||
evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
|
evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu style={{ padding: config.space.S100 }}>
|
<Menu style={{ padding: config.space.S100 }}>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { isKeyHotkey } from 'is-hotkey';
|
||||||
import { Header, Menu, Scroll, config } from 'folds';
|
import { Header, Menu, Scroll, config } from 'folds';
|
||||||
|
|
||||||
import * as css from './AutocompleteMenu.css';
|
import * as css from './AutocompleteMenu.css';
|
||||||
import { preventScrollWithArrowKey } from '../../../utils/keyboard';
|
import { preventScrollWithArrowKey, stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
type AutocompleteMenuProps = {
|
type AutocompleteMenuProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
|
@ -24,6 +24,7 @@ export function AutocompleteMenu({ headerContent, requestClose, children }: Auto
|
||||||
allowOutsideClick: true,
|
allowOutsideClick: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
|
isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
|
||||||
isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt),
|
isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt),
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu className={css.AutocompleteMenu}>
|
<Menu className={css.AutocompleteMenu}>
|
||||||
|
|
|
@ -37,7 +37,7 @@ import * as css from './EmojiBoard.css';
|
||||||
import { EmojiGroupId, IEmoji, IEmojiGroup, emojiGroups, emojis } from '../../plugins/emoji';
|
import { EmojiGroupId, IEmoji, IEmojiGroup, emojiGroups, emojis } from '../../plugins/emoji';
|
||||||
import { IEmojiGroupLabels, useEmojiGroupLabels } from './useEmojiGroupLabels';
|
import { IEmojiGroupLabels, useEmojiGroupLabels } from './useEmojiGroupLabels';
|
||||||
import { IEmojiGroupIcons, useEmojiGroupIcons } from './useEmojiGroupIcons';
|
import { IEmojiGroupIcons, useEmojiGroupIcons } from './useEmojiGroupIcons';
|
||||||
import { preventScrollWithArrowKey } from '../../utils/keyboard';
|
import { preventScrollWithArrowKey, stopPropagation } from '../../utils/keyboard';
|
||||||
import { useRelevantImagePacks } from '../../hooks/useImagePacks';
|
import { useRelevantImagePacks } from '../../hooks/useImagePacks';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { useRecentEmoji } from '../../hooks/useRecentEmoji';
|
import { useRecentEmoji } from '../../hooks/useRecentEmoji';
|
||||||
|
@ -775,6 +775,7 @@ export function EmojiBoard({
|
||||||
!editableActiveElement() && isKeyHotkey(['arrowdown', 'arrowright'], evt),
|
!editableActiveElement() && isKeyHotkey(['arrowdown', 'arrowright'], evt),
|
||||||
isKeyBackward: (evt: KeyboardEvent) =>
|
isKeyBackward: (evt: KeyboardEvent) =>
|
||||||
!editableActiveElement() && isKeyHotkey(['arrowup', 'arrowleft'], evt),
|
!editableActiveElement() && isKeyHotkey(['arrowup', 'arrowleft'], evt),
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<EmojiBoardLayout
|
<EmojiBoardLayout
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
import { MatrixError } from 'matrix-js-sdk';
|
import { MatrixError } from 'matrix-js-sdk';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
type LeaveRoomPromptProps = {
|
type LeaveRoomPromptProps = {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
|
@ -52,6 +53,7 @@ export function LeaveRoomPrompt({ roomId, onDone, onCancel }: LeaveRoomPromptPro
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: onCancel,
|
onDeactivate: onCancel,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dialog variant="Surface">
|
<Dialog variant="Surface">
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
import { MatrixError } from 'matrix-js-sdk';
|
import { MatrixError } from 'matrix-js-sdk';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
type LeaveSpacePromptProps = {
|
type LeaveSpacePromptProps = {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
|
@ -52,6 +53,7 @@ export function LeaveSpacePrompt({ roomId, onDone, onCancel }: LeaveSpacePromptP
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: onCancel,
|
onDeactivate: onCancel,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dialog variant="Surface">
|
<Dialog variant="Surface">
|
||||||
|
|
|
@ -29,6 +29,7 @@ import {
|
||||||
mimeTypeToExt,
|
mimeTypeToExt,
|
||||||
} from '../../../utils/mimeTypes';
|
} from '../../../utils/mimeTypes';
|
||||||
import * as css from './style.css';
|
import * as css from './style.css';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
const renderErrorButton = (retry: () => void, text: string) => (
|
const renderErrorButton = (retry: () => void, text: string) => (
|
||||||
<TooltipProvider
|
<TooltipProvider
|
||||||
|
@ -101,6 +102,7 @@ export function ReadTextFile({ body, mimeType, url, encInfo, renderViewer }: Rea
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setTextViewer(false),
|
onDeactivate: () => setTextViewer(false),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -184,6 +186,7 @@ export function ReadPdfFile({ body, mimeType, url, encInfo, renderViewer }: Read
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setPdfViewer(false),
|
onDeactivate: () => setPdfViewer(false),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal
|
<Modal
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { getFileSrcUrl } from './util';
|
||||||
import * as css from './style.css';
|
import * as css from './style.css';
|
||||||
import { bytesToSize } from '../../../utils/common';
|
import { bytesToSize } from '../../../utils/common';
|
||||||
import { FALLBACK_MIMETYPE } from '../../../utils/mimeTypes';
|
import { FALLBACK_MIMETYPE } from '../../../utils/mimeTypes';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
type RenderViewerProps = {
|
type RenderViewerProps = {
|
||||||
src: string;
|
src: string;
|
||||||
|
@ -108,6 +109,7 @@ export const ImageContent = as<'div', ImageContentProps>(
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setViewer(false),
|
onDeactivate: () => setViewer(false),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal
|
<Modal
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { nameInitials } from '../../utils/common';
|
||||||
import { millify } from '../../plugins/millify';
|
import { millify } from '../../plugins/millify';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
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 { RoomType, StateEvent } from '../../../types/matrix/room';
|
||||||
import { useJoinedRoomId } from '../../hooks/useJoinedRoomId';
|
import { useJoinedRoomId } from '../../hooks/useJoinedRoomId';
|
||||||
import { useElementSizeObserver } from '../../hooks/useElementSizeObserver';
|
import { useElementSizeObserver } from '../../hooks/useElementSizeObserver';
|
||||||
|
@ -107,6 +107,7 @@ function ErrorDialog({
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
onDeactivate: closeError,
|
onDeactivate: closeError,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dialog variant="Surface">
|
<Dialog variant="Surface">
|
||||||
|
@ -236,6 +237,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
onDeactivate: closeTopic,
|
onDeactivate: closeTopic,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{renderTopicViewer(roomName, roomTopic, closeTopic)}
|
{renderTopicViewer(roomName, roomTopic, closeTopic)}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||||
import { UseStateProvider } from '../../components/UseStateProvider';
|
import { UseStateProvider } from '../../components/UseStateProvider';
|
||||||
import { LeaveSpacePrompt } from '../../components/leave-space-prompt';
|
import { LeaveSpacePrompt } from '../../components/leave-space-prompt';
|
||||||
import { LeaveRoomPrompt } from '../../components/leave-room-prompt';
|
import { LeaveRoomPrompt } from '../../components/leave-room-prompt';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
type HierarchyItemWithParent = HierarchyItem & {
|
type HierarchyItemWithParent = HierarchyItem & {
|
||||||
parentId: string;
|
parentId: string;
|
||||||
|
@ -227,6 +228,7 @@ export function HierarchyItemMenu({
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu style={{ maxWidth: toRem(150), width: '100vw' }}>
|
<Menu style={{ maxWidth: toRem(150), width: '100vw' }}>
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { openInviteUser, openSpaceSettings } from '../../../client/action/naviga
|
||||||
import { IPowerLevels, usePowerLevelsAPI } from '../../hooks/usePowerLevels';
|
import { IPowerLevels, usePowerLevelsAPI } from '../../hooks/usePowerLevels';
|
||||||
import { UseStateProvider } from '../../components/UseStateProvider';
|
import { UseStateProvider } from '../../components/UseStateProvider';
|
||||||
import { LeaveSpacePrompt } from '../../components/leave-space-prompt';
|
import { LeaveSpacePrompt } from '../../components/leave-space-prompt';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
type LobbyMenuProps = {
|
type LobbyMenuProps = {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
|
@ -197,6 +198,7 @@ export function LobbyHeader({ showProfile, powerLevels }: LobbyHeaderProps) {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LobbyMenu
|
<LobbyMenu
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { UseStateProvider } from '../../components/UseStateProvider';
|
||||||
import { RoomTopicViewer } from '../../components/room-topic-viewer';
|
import { RoomTopicViewer } from '../../components/room-topic-viewer';
|
||||||
import * as css from './LobbyHero.css';
|
import * as css from './LobbyHero.css';
|
||||||
import { PageHero } from '../../components/page';
|
import { PageHero } from '../../components/page';
|
||||||
import { onEnterOrSpace } from '../../utils/keyboard';
|
import { onEnterOrSpace, stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
export function LobbyHero() {
|
export function LobbyHero() {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
@ -46,6 +46,7 @@ export function LobbyHero() {
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
onDeactivate: () => setViewTopic(false),
|
onDeactivate: () => setViewTopic(false),
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RoomTopicViewer
|
<RoomTopicViewer
|
||||||
|
|
|
@ -31,7 +31,7 @@ import {
|
||||||
} from '../../components/RoomSummaryLoader';
|
} from '../../components/RoomSummaryLoader';
|
||||||
import { UseStateProvider } from '../../components/UseStateProvider';
|
import { UseStateProvider } from '../../components/UseStateProvider';
|
||||||
import { RoomTopicViewer } from '../../components/room-topic-viewer';
|
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 { Membership, RoomType } from '../../../types/matrix/room';
|
||||||
import * as css from './RoomItem.css';
|
import * as css from './RoomItem.css';
|
||||||
import * as styleCss from './style.css';
|
import * as styleCss from './style.css';
|
||||||
|
@ -264,6 +264,7 @@ function RoomProfile({
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
onDeactivate: () => setView(false),
|
onDeactivate: () => setView(false),
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RoomTopicViewer
|
<RoomTopicViewer
|
||||||
|
|
|
@ -34,6 +34,7 @@ import * as styleCss from './style.css';
|
||||||
import { ErrorCode } from '../../cs-errorcode';
|
import { ErrorCode } from '../../cs-errorcode';
|
||||||
import { useDraggableItem } from './DnD';
|
import { useDraggableItem } from './DnD';
|
||||||
import { openCreateRoom, openSpaceAddExisting } from '../../../client/action/navigation';
|
import { openCreateRoom, openSpaceAddExisting } from '../../../client/action/navigation';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
function SpaceProfileLoading() {
|
function SpaceProfileLoading() {
|
||||||
return (
|
return (
|
||||||
|
@ -277,6 +278,7 @@ function AddRoomButton({ item }: { item: HierarchyItem }) {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu style={{ padding: config.space.S100 }}>
|
<Menu style={{ padding: config.space.S100 }}>
|
||||||
|
@ -338,6 +340,7 @@ function AddSpaceButton({ item }: { item: HierarchyItem }) {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu style={{ padding: config.space.S100 }}>
|
<Menu style={{ padding: config.space.S100 }}>
|
||||||
|
|
|
@ -38,6 +38,7 @@ import {
|
||||||
} from '../../hooks/useAsyncSearch';
|
} from '../../hooks/useAsyncSearch';
|
||||||
import { DebounceOptions, useDebounce } from '../../hooks/useDebounce';
|
import { DebounceOptions, useDebounce } from '../../hooks/useDebounce';
|
||||||
import { VirtualTile } from '../../components/virtualizer';
|
import { VirtualTile } from '../../components/virtualizer';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
type OrderButtonProps = {
|
type OrderButtonProps = {
|
||||||
order?: string;
|
order?: string;
|
||||||
|
@ -66,6 +67,7 @@ function OrderButton({ order, onChange }: OrderButtonProps) {
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setMenuAnchor(undefined),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu variant="Surface">
|
<Menu variant="Surface">
|
||||||
|
@ -202,6 +204,7 @@ function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButto
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setMenuAnchor(undefined),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu variant="Surface" style={{ width: toRem(250) }}>
|
<Menu variant="Surface" style={{ width: toRem(250) }}>
|
||||||
|
|
|
@ -36,6 +36,7 @@ import { LeaveRoomPrompt } from '../../components/leave-room-prompt';
|
||||||
import { useClientConfig } from '../../hooks/useClientConfig';
|
import { useClientConfig } from '../../hooks/useClientConfig';
|
||||||
import { useRoomTypingMember } from '../../hooks/useRoomTypingMembers';
|
import { useRoomTypingMember } from '../../hooks/useRoomTypingMembers';
|
||||||
import { TypingIndicator } from '../../components/typing-indicator';
|
import { TypingIndicator } from '../../components/typing-indicator';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
type RoomNavItemMenuProps = {
|
type RoomNavItemMenuProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -269,6 +270,7 @@ export function RoomNavItem({
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RoomNavItemMenu
|
<RoomNavItemMenu
|
||||||
|
|
|
@ -55,6 +55,7 @@ import { millify } from '../../plugins/millify';
|
||||||
import { ScrollTopContainer } from '../../components/scroll-top-container';
|
import { ScrollTopContainer } from '../../components/scroll-top-container';
|
||||||
import { UserAvatar } from '../../components/user-avatar';
|
import { UserAvatar } from '../../components/user-avatar';
|
||||||
import { useRoomTypingMember } from '../../hooks/useRoomTypingMembers';
|
import { useRoomTypingMember } from '../../hooks/useRoomTypingMembers';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
export const MembershipFilters = {
|
export const MembershipFilters = {
|
||||||
filterJoined: (m: RoomMember) => m.membership === Membership.Join,
|
filterJoined: (m: RoomMember) => m.membership === Membership.Join,
|
||||||
|
@ -300,6 +301,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu style={{ padding: config.space.S100 }}>
|
<Menu style={{ padding: config.space.S100 }}>
|
||||||
|
@ -358,6 +360,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu style={{ padding: config.space.S100 }}>
|
<Menu style={{ padding: config.space.S100 }}>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Box, Line } from 'folds';
|
import { Box, Line } from 'folds';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { isKeyHotkey } from 'is-hotkey';
|
||||||
import { RoomView } from './RoomView';
|
import { RoomView } from './RoomView';
|
||||||
import { MembersDrawer } from './MembersDrawer';
|
import { MembersDrawer } from './MembersDrawer';
|
||||||
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
|
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
|
||||||
|
@ -8,6 +9,8 @@ import { useSetting } from '../../state/hooks/settings';
|
||||||
import { settingsAtom } from '../../state/settings';
|
import { settingsAtom } from '../../state/settings';
|
||||||
import { PowerLevelsContextProvider, usePowerLevels } from '../../hooks/usePowerLevels';
|
import { PowerLevelsContextProvider, usePowerLevels } from '../../hooks/usePowerLevels';
|
||||||
import { useRoom } from '../../hooks/useRoom';
|
import { useRoom } from '../../hooks/useRoom';
|
||||||
|
import { useKeyDown } from '../../hooks/useKeyDown';
|
||||||
|
import { markAsRead } from '../../../client/action/notifications';
|
||||||
|
|
||||||
export function Room() {
|
export function Room() {
|
||||||
const { eventId } = useParams();
|
const { eventId } = useParams();
|
||||||
|
@ -17,6 +20,18 @@ export function Room() {
|
||||||
const screenSize = useScreenSizeContext();
|
const screenSize = useScreenSizeContext();
|
||||||
const powerLevels = usePowerLevels(room);
|
const powerLevels = usePowerLevels(room);
|
||||||
|
|
||||||
|
useKeyDown(
|
||||||
|
window,
|
||||||
|
useCallback(
|
||||||
|
(evt) => {
|
||||||
|
if (isKeyHotkey('escape', evt)) {
|
||||||
|
markAsRead(room.roomId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[room.roomId]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PowerLevelsContextProvider value={powerLevels}>
|
<PowerLevelsContextProvider value={powerLevels}>
|
||||||
<Box grow="Yes">
|
<Box grow="Yes">
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React, { useRef } from 'react';
|
import React, { useCallback, useRef } from 'react';
|
||||||
import { Box, Text, config } from 'folds';
|
import { Box, Text, config } from 'folds';
|
||||||
import { EventType, Room } from 'matrix-js-sdk';
|
import { EventType, Room } from 'matrix-js-sdk';
|
||||||
|
import { ReactEditor } from 'slate-react';
|
||||||
|
import { isKeyHotkey } from 'is-hotkey';
|
||||||
import { useStateEvent } from '../../hooks/useStateEvent';
|
import { useStateEvent } from '../../hooks/useStateEvent';
|
||||||
import { StateEvent } from '../../../types/matrix/room';
|
import { StateEvent } from '../../../types/matrix/room';
|
||||||
import { usePowerLevelsAPI, usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
import { usePowerLevelsAPI, usePowerLevelsContext } from '../../hooks/usePowerLevels';
|
||||||
|
@ -15,10 +16,42 @@ import { RoomInput } from './RoomInput';
|
||||||
import { RoomViewFollowing } from './RoomViewFollowing';
|
import { RoomViewFollowing } from './RoomViewFollowing';
|
||||||
import { Page } from '../../components/page';
|
import { Page } from '../../components/page';
|
||||||
import { RoomViewHeader } from './RoomViewHeader';
|
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 }) {
|
export function RoomView({ room, eventId }: { room: Room; eventId?: string }) {
|
||||||
const roomInputRef = useRef(null);
|
const roomInputRef = useRef<HTMLDivElement>(null);
|
||||||
const roomViewRef = useRef(null);
|
const roomViewRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const { roomId } = room;
|
const { roomId } = room;
|
||||||
const editor = useEditor();
|
const editor = useEditor();
|
||||||
|
@ -33,6 +66,25 @@ export function RoomView({ room, eventId }: { room: Room; eventId?: string }) {
|
||||||
? canSendEvent(EventType.RoomMessage, getPowerLevel(myUserId))
|
? canSendEvent(EventType.RoomMessage, getPowerLevel(myUserId))
|
||||||
: false;
|
: 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 (
|
return (
|
||||||
<Page ref={roomViewRef}>
|
<Page ref={roomViewRef}>
|
||||||
<RoomViewHeader />
|
<RoomViewHeader />
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { useRoomLatestRenderedEvent } from '../../hooks/useRoomLatestRenderedEvent';
|
import { useRoomLatestRenderedEvent } from '../../hooks/useRoomLatestRenderedEvent';
|
||||||
import { useRoomEventReaders } from '../../hooks/useRoomEventReaders';
|
import { useRoomEventReaders } from '../../hooks/useRoomEventReaders';
|
||||||
import { EventReaders } from '../../components/event-readers';
|
import { EventReaders } from '../../components/event-readers';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
export type RoomViewFollowingProps = {
|
export type RoomViewFollowingProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -50,6 +51,7 @@ export const RoomViewFollowing = as<'div', RoomViewFollowingProps>(
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setOpen(false),
|
onDeactivate: () => setOpen(false),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal variant="Surface" size="300">
|
<Modal variant="Surface" size="300">
|
||||||
|
|
|
@ -57,6 +57,7 @@ import { useRoomAvatar, useRoomName, useRoomTopic } from '../../hooks/useRoomMet
|
||||||
import { mDirectAtom } from '../../state/mDirectList';
|
import { mDirectAtom } from '../../state/mDirectList';
|
||||||
import { useClientConfig } from '../../hooks/useClientConfig';
|
import { useClientConfig } from '../../hooks/useClientConfig';
|
||||||
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
|
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
type RoomMenuProps = {
|
type RoomMenuProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -240,6 +241,7 @@ export function RoomViewHeader() {
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
onDeactivate: () => setViewTopic(false),
|
onDeactivate: () => setViewTopic(false),
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RoomTopicViewer
|
<RoomTopicViewer
|
||||||
|
@ -331,6 +333,7 @@ export function RoomViewHeader() {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RoomMenu
|
<RoomMenu
|
||||||
|
|
|
@ -74,6 +74,7 @@ import {
|
||||||
} from '../../../pages/pathUtils';
|
} from '../../../pages/pathUtils';
|
||||||
import { copyToClipboard } from '../../../utils/dom';
|
import { copyToClipboard } from '../../../utils/dom';
|
||||||
import { useClientConfig } from '../../../hooks/useClientConfig';
|
import { useClientConfig } from '../../../hooks/useClientConfig';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
export type ReactionHandler = (keyOrMxc: string, shortcode: string) => void;
|
export type ReactionHandler = (keyOrMxc: string, shortcode: string) => void;
|
||||||
|
|
||||||
|
@ -148,6 +149,7 @@ export const MessageAllReactionItem = as<
|
||||||
returnFocusOnDeactivate: false,
|
returnFocusOnDeactivate: false,
|
||||||
onDeactivate: () => handleClose(),
|
onDeactivate: () => handleClose(),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal variant="Surface" size="300">
|
<Modal variant="Surface" size="300">
|
||||||
|
@ -201,6 +203,7 @@ export const MessageReadReceiptItem = as<
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: handleClose,
|
onDeactivate: handleClose,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal variant="Surface" size="300">
|
<Modal variant="Surface" size="300">
|
||||||
|
@ -278,6 +281,7 @@ export const MessageSourceCodeItem = as<
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: handleClose,
|
onDeactivate: handleClose,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal variant="Surface" size="500">
|
<Modal variant="Surface" size="500">
|
||||||
|
@ -401,6 +405,7 @@ export const MessageDeleteItem = as<
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: handleClose,
|
onDeactivate: handleClose,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dialog variant="Surface">
|
<Dialog variant="Surface">
|
||||||
|
@ -530,6 +535,7 @@ export const MessageReportItem = as<
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: handleClose,
|
onDeactivate: handleClose,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dialog variant="Surface">
|
<Dialog variant="Surface">
|
||||||
|
@ -875,6 +881,7 @@ export const Message = as<'div', MessageProps>(
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu>
|
<Menu>
|
||||||
|
@ -1089,6 +1096,7 @@ export const Event = as<'div', EventProps>(
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu {...props} ref={ref}>
|
<Menu {...props} ref={ref}>
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { Reaction, ReactionTooltipMsg } from '../../../components/message';
|
||||||
import { useRelations } from '../../../hooks/useRelations';
|
import { useRelations } from '../../../hooks/useRelations';
|
||||||
import * as css from './styles.css';
|
import * as css from './styles.css';
|
||||||
import { ReactionViewer } from '../reaction-viewer';
|
import { ReactionViewer } from '../reaction-viewer';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
export type ReactionsProps = {
|
export type ReactionsProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -105,6 +106,7 @@ export const Reactions = as<'div', ReactionsProps>(
|
||||||
returnFocusOnDeactivate: false,
|
returnFocusOnDeactivate: false,
|
||||||
onDeactivate: () => setViewer(false),
|
onDeactivate: () => setViewer(false),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal variant="Surface" size="300">
|
<Modal variant="Surface" size="300">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import './Search.scss';
|
import './Search.scss';
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ import { roomToUnreadAtom } from '../../state/room/roomToUnread';
|
||||||
import { roomToParentsAtom } from '../../state/room/roomToParents';
|
import { roomToParentsAtom } from '../../state/room/roomToParents';
|
||||||
import { allRoomsAtom } from '../../state/room-list/roomList';
|
import { allRoomsAtom } from '../../state/room-list/roomList';
|
||||||
import { mDirectAtom } from '../../state/mDirectList';
|
import { mDirectAtom } from '../../state/mDirectList';
|
||||||
|
import { useKeyDown } from '../../hooks/useKeyDown';
|
||||||
|
import { openSearch } from '../../../client/action/navigation';
|
||||||
|
|
||||||
function useVisiblityToggle(setResult) {
|
function useVisiblityToggle(setResult) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
@ -49,6 +51,27 @@ function useVisiblityToggle(setResult) {
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [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);
|
const requestClose = () => setIsOpen(false);
|
||||||
|
|
||||||
return [isOpen, requestClose];
|
return [isOpen, requestClose];
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
|
||||||
import { useDebounce } from '../../hooks/useDebounce';
|
import { useDebounce } from '../../hooks/useDebounce';
|
||||||
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
|
||||||
export function ServerPicker({
|
export function ServerPicker({
|
||||||
server,
|
server,
|
||||||
|
@ -103,6 +104,7 @@ export function ServerPicker({
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu>
|
<Menu>
|
||||||
|
|
|
@ -36,6 +36,7 @@ import {
|
||||||
import { PasswordInput } from '../../../components/password-input/PasswordInput';
|
import { PasswordInput } from '../../../components/password-input/PasswordInput';
|
||||||
import { FieldError } from '../FiledError';
|
import { FieldError } from '../FiledError';
|
||||||
import { getResetPasswordPath } from '../../pathUtils';
|
import { getResetPasswordPath } from '../../pathUtils';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
function UsernameHint({ server }: { server: string }) {
|
function UsernameHint({ server }: { server: string }) {
|
||||||
const [anchor, setAnchor] = useState<RectCords>();
|
const [anchor, setAnchor] = useState<RectCords>();
|
||||||
|
@ -54,6 +55,7 @@ function UsernameHint({ server }: { server: string }) {
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setAnchor(undefined),
|
onDeactivate: () => setAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu>
|
<Menu>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Box, Spinner, Text } from 'folds';
|
import { Box, Spinner, Text } from 'folds';
|
||||||
import React, { ReactNode, useEffect, useState } from 'react';
|
import React, { ReactNode, useEffect, useState } from 'react';
|
||||||
import initMatrix from '../../../client/initMatrix';
|
import initMatrix from '../../../client/initMatrix';
|
||||||
import { initHotkeys } from '../../../client/event/hotkeys';
|
|
||||||
import { getSecret } from '../../../client/state/auth';
|
import { getSecret } from '../../../client/state/auth';
|
||||||
import { SplashScreen } from '../../components/splash-screen';
|
import { SplashScreen } from '../../components/splash-screen';
|
||||||
import { CapabilitiesAndMediaConfigLoader } from '../../components/CapabilitiesAndMediaConfigLoader';
|
import { CapabilitiesAndMediaConfigLoader } from '../../components/CapabilitiesAndMediaConfigLoader';
|
||||||
|
@ -47,7 +46,6 @@ export function ClientRoot({ children }: ClientRootProps) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleStart = () => {
|
const handleStart = () => {
|
||||||
initHotkeys();
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
initMatrix.once('init_loading_finished', handleStart);
|
initMatrix.once('init_loading_finished', handleStart);
|
||||||
|
|
|
@ -44,6 +44,7 @@ import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page
|
||||||
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
|
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
|
||||||
import { useRoomsUnread } from '../../../state/hooks/unread';
|
import { useRoomsUnread } from '../../../state/hooks/unread';
|
||||||
import { markAsRead } from '../../../../client/action/notifications';
|
import { markAsRead } from '../../../../client/action/notifications';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
type DirectMenuProps = {
|
type DirectMenuProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
|
@ -118,6 +119,7 @@ function DirectHeader() {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DirectMenu requestClose={() => setMenuAnchor(undefined)} />
|
<DirectMenu requestClose={() => setMenuAnchor(undefined)} />
|
||||||
|
|
|
@ -36,6 +36,7 @@ import { getMxIdServer } from '../../../utils/matrix';
|
||||||
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||||
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
|
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
|
||||||
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
|
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
export function AddServer() {
|
export function AddServer() {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
@ -80,6 +81,7 @@ export function AddServer() {
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
onDeactivate: () => setDialog(false),
|
onDeactivate: () => setDialog(false),
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dialog variant="Surface">
|
<Dialog variant="Surface">
|
||||||
|
|
|
@ -41,6 +41,7 @@ import * as css from './style.css';
|
||||||
import { allRoomsAtom } from '../../../state/room-list/roomList';
|
import { allRoomsAtom } from '../../../state/room-list/roomList';
|
||||||
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
|
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
|
||||||
import { getMxIdServer } from '../../../utils/matrix';
|
import { getMxIdServer } from '../../../utils/matrix';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
const useServerSearchParams = (searchParams: URLSearchParams): ExploreServerPathSearchParams =>
|
const useServerSearchParams = (searchParams: URLSearchParams): ExploreServerPathSearchParams =>
|
||||||
useMemo(
|
useMemo(
|
||||||
|
@ -182,6 +183,7 @@ function ThirdPartyProtocolsSelector({
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setMenuAnchor(undefined),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu variant="Surface">
|
<Menu variant="Surface">
|
||||||
|
@ -277,6 +279,7 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setMenuAnchor(undefined),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Menu variant="Surface">
|
<Menu variant="Surface">
|
||||||
|
|
|
@ -47,6 +47,7 @@ import { PageNav, PageNavHeader, PageNavContent } from '../../../components/page
|
||||||
import { useRoomsUnread } from '../../../state/hooks/unread';
|
import { useRoomsUnread } from '../../../state/hooks/unread';
|
||||||
import { markAsRead } from '../../../../client/action/notifications';
|
import { markAsRead } from '../../../../client/action/notifications';
|
||||||
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
|
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
type HomeMenuProps = {
|
type HomeMenuProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
|
@ -121,6 +122,7 @@ function HomeHeader() {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<HomeMenu requestClose={() => setMenuAnchor(undefined)} />
|
<HomeMenu requestClose={() => setMenuAnchor(undefined)} />
|
||||||
|
|
|
@ -34,7 +34,7 @@ import { RoomAvatar } from '../../../components/room-avatar';
|
||||||
import { addRoomIdToMDirect, getMxIdLocalPart, guessDmRoomUserId } from '../../../utils/matrix';
|
import { addRoomIdToMDirect, getMxIdLocalPart, guessDmRoomUserId } from '../../../utils/matrix';
|
||||||
import { Time } from '../../../components/message';
|
import { Time } from '../../../components/message';
|
||||||
import { useElementSizeObserver } from '../../../hooks/useElementSizeObserver';
|
import { useElementSizeObserver } from '../../../hooks/useElementSizeObserver';
|
||||||
import { onEnterOrSpace } from '../../../utils/keyboard';
|
import { onEnterOrSpace, stopPropagation } from '../../../utils/keyboard';
|
||||||
import { RoomTopicViewer } from '../../../components/room-topic-viewer';
|
import { RoomTopicViewer } from '../../../components/room-topic-viewer';
|
||||||
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||||
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
|
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
|
||||||
|
@ -140,6 +140,7 @@ function InviteCard({ room, userId, direct, compact, onNavigate }: InviteCardPro
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
onDeactivate: closeTopic,
|
onDeactivate: closeTopic,
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RoomTopicViewer
|
<RoomTopicViewer
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
|
||||||
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
|
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
|
||||||
import { useDirectRooms } from '../direct/useDirectRooms';
|
import { useDirectRooms } from '../direct/useDirectRooms';
|
||||||
import { markAsRead } from '../../../../client/action/notifications';
|
import { markAsRead } from '../../../../client/action/notifications';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
type DirectMenuProps = {
|
type DirectMenuProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
|
@ -120,6 +121,7 @@ export function DirectTab() {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DirectMenu requestClose={() => setMenuAnchor(undefined)} />
|
<DirectMenu requestClose={() => setMenuAnchor(undefined)} />
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
|
||||||
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
|
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
|
||||||
import { useHomeRooms } from '../home/useHomeRooms';
|
import { useHomeRooms } from '../home/useHomeRooms';
|
||||||
import { markAsRead } from '../../../../client/action/notifications';
|
import { markAsRead } from '../../../../client/action/notifications';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
type HomeMenuProps = {
|
type HomeMenuProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
|
@ -122,6 +123,7 @@ export function HomeTab() {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<HomeMenu requestClose={() => setMenuAnchor(undefined)} />
|
<HomeMenu requestClose={() => setMenuAnchor(undefined)} />
|
||||||
|
|
|
@ -90,6 +90,7 @@ import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
|
||||||
import { markAsRead } from '../../../../client/action/notifications';
|
import { markAsRead } from '../../../../client/action/notifications';
|
||||||
import { copyToClipboard } from '../../../utils/dom';
|
import { copyToClipboard } from '../../../utils/dom';
|
||||||
import { openInviteUser, openSpaceSettings } from '../../../../client/action/navigation';
|
import { openInviteUser, openSpaceSettings } from '../../../../client/action/navigation';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
type SpaceMenuProps = {
|
type SpaceMenuProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -463,6 +464,7 @@ function SpaceTab({
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SpaceMenu
|
<SpaceMenu
|
||||||
|
|
|
@ -73,6 +73,7 @@ import { useClientConfig } from '../../../hooks/useClientConfig';
|
||||||
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
|
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
|
||||||
import { useStateEvent } from '../../../hooks/useStateEvent';
|
import { useStateEvent } from '../../../hooks/useStateEvent';
|
||||||
import { StateEvent } from '../../../../types/matrix/room';
|
import { StateEvent } from '../../../../types/matrix/room';
|
||||||
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
type SpaceMenuProps = {
|
type SpaceMenuProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -248,6 +249,7 @@ function SpaceHeader() {
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SpaceMenu room={space} requestClose={() => setMenuAnchor(undefined)} />
|
<SpaceMenu room={space} requestClose={() => setMenuAnchor(undefined)} />
|
||||||
|
|
|
@ -30,3 +30,8 @@ export const onEnterOrSpace = (callback: () => void) => (evt: KeyboardEventLike)
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const stopPropagation = (evt: KeyboardEvent): boolean => {
|
||||||
|
evt.stopPropagation();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
|
@ -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 };
|
|
Loading…
Reference in a new issue