Skip to content

Commit

Permalink
Disable keyboard shortcuts when dialog is opened
Browse files Browse the repository at this point in the history
- Centralise whether dialog is opened in store
- Refactor suppressing of overlapping dialogs
  • Loading branch information
microbit-grace committed Dec 3, 2024
1 parent 13f69b1 commit 9f6f16e
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 159 deletions.
77 changes: 33 additions & 44 deletions src/components/ActionBar/ActionBarItemsRight.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { HStack, MenuDivider, useDisclosure } from "@chakra-ui/react";
import { HStack, MenuDivider } from "@chakra-ui/react";
import { ReactNode, useMemo } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { useIntl } from "react-intl";
import { useLocation } from "react-router";
import {
globalShortcutConfig,
keyboardShortcuts,
} from "../../keyboard-shortcuts";
useKeyboardShortcut,
} from "../../keyboard-shortcut-hooks";
import { useStore } from "../../store";
import AboutDialog from "../AboutDialog";
import ConnectFirstDialog from "../ConnectFirstDialog";
Expand All @@ -27,11 +26,17 @@ interface ItemsRightProps {

const ItemsRight = ({ menuItems, toolbarItems }: ItemsRightProps) => {
const intl = useIntl();
const languageDisclosure = useDisclosure();
const settingsDisclosure = useDisclosure();
const aboutDialogDisclosure = useDisclosure();
const feedbackDisclosure = useDisclosure();
const connectFirstDisclosure = useDisclosure();
const closeDialog = useStore((s) => s.closeDialog);
const languageDialogOnOpen = useStore((s) => s.languageDialogOnOpen);
const isLanguageDialogOpen = useStore((s) => s.isLanguageDialogOpen);
const settingsDialogOnOpen = useStore((s) => s.settingsDialogOnOpen);
const isSettingsDialogOpen = useStore((s) => s.isSettingsDialogOpen);
const aboutDialogOnOpen = useStore((s) => s.aboutDialogOnOpen);
const isAboutDialogOpen = useStore((s) => s.isAboutDialogOpen);
const feedbackOnOpen = useStore((s) => s.feedbackFormOnOpen);
const isFeedbackOpen = useStore((s) => s.isFeedbackFormOpen);
const connectFirstDialogOnOpen = useStore((s) => s.connectFirstDialogOnOpen);
const isConnectFirstDialogOpen = useStore((s) => s.isConnectFirstDialogOpen);
const setPostConnectTourTrigger = useStore(
(s) => s.setPostConnectTourTrigger
);
Expand All @@ -52,48 +57,32 @@ const ItemsRight = ({ menuItems, toolbarItems }: ItemsRightProps) => {
}
}
}, [tourTriggerName]);
useHotkeys(
keyboardShortcuts.settings,
settingsDisclosure.onOpen,
globalShortcutConfig
);
useKeyboardShortcut(keyboardShortcuts.settings, settingsDialogOnOpen);
return (
<>
<LanguageDialog
isOpen={languageDisclosure.isOpen}
onClose={languageDisclosure.onClose}
/>
<SettingsDialog
isOpen={settingsDisclosure.isOpen}
onClose={settingsDisclosure.onClose}
/>
<LanguageDialog isOpen={isLanguageDialogOpen} onClose={closeDialog} />
<SettingsDialog isOpen={isSettingsDialogOpen} onClose={closeDialog} />
<ConnectFirstDialog
isOpen={connectFirstDisclosure.isOpen}
onClose={connectFirstDisclosure.onClose}
isOpen={isConnectFirstDialogOpen}
onClose={closeDialog}
onChooseConnect={() => setPostConnectTourTrigger(tourTrigger)}
explanationTextId="connect-to-tour-body"
options={{ postConnectTourTrigger: tourTrigger }}
/>
<AboutDialog
isOpen={aboutDialogDisclosure.isOpen}
onClose={aboutDialogDisclosure.onClose}
/>
<FeedbackForm
isOpen={feedbackDisclosure.isOpen}
onClose={feedbackDisclosure.onClose}
/>
<AboutDialog isOpen={isAboutDialogOpen} onClose={closeDialog} />
<FeedbackForm isOpen={isFeedbackOpen} onClose={closeDialog} />
<HStack spacing={3} display={{ base: "none", lg: "flex" }}>
{toolbarItems}
<SettingsMenu
onLanguageDialogOpen={languageDisclosure.onOpen}
onSettingsDialogOpen={settingsDisclosure.onOpen}
onLanguageDialogOpen={languageDialogOnOpen}
onSettingsDialogOpen={settingsDialogOnOpen}
/>
</HStack>
<HelpMenu
display={{ base: "none", md: "block", lg: "block" }}
onAboutDialogOpen={aboutDialogDisclosure.onOpen}
onConnectFirstDialogOpen={connectFirstDisclosure.onOpen}
onFeedbackOpen={feedbackDisclosure.onOpen}
onAboutDialogOpen={aboutDialogOnOpen}
onConnectFirstDialogOpen={connectFirstDialogOnOpen}
onFeedbackOpen={feedbackOnOpen}
tourTrigger={tourTrigger}
/>
<ToolbarMenu
Expand All @@ -102,8 +91,8 @@ const ItemsRight = ({ menuItems, toolbarItems }: ItemsRightProps) => {
label={intl.formatMessage({ id: "main-menu" })}
>
{menuItems}
<LanguageMenuItem onOpen={languageDisclosure.onOpen} />
<SettingsMenuItem onOpen={settingsDisclosure.onOpen} />
<LanguageMenuItem onOpen={languageDialogOnOpen} />
<SettingsMenuItem onOpen={settingsDialogOnOpen} />
</ToolbarMenu>
{/* Toolbar items when sm window size. */}
<ToolbarMenu
Expand All @@ -112,13 +101,13 @@ const ItemsRight = ({ menuItems, toolbarItems }: ItemsRightProps) => {
label={intl.formatMessage({ id: "main-menu" })}
>
{menuItems}
<LanguageMenuItem onOpen={languageDisclosure.onOpen} />
<SettingsMenuItem onOpen={settingsDisclosure.onOpen} />
<LanguageMenuItem onOpen={languageDialogOnOpen} />
<SettingsMenuItem onOpen={settingsDialogOnOpen} />
<MenuDivider />
<HelpMenuItems
onAboutDialogOpen={aboutDialogDisclosure.onOpen}
onConnectFirstDialogOpen={connectFirstDisclosure.onOpen}
onFeedbackOpen={feedbackDisclosure.onOpen}
onAboutDialogOpen={aboutDialogOnOpen}
onConnectFirstDialogOpen={connectFirstDialogOnOpen}
onFeedbackOpen={feedbackOnOpen}
tourTrigger={tourTrigger}
/>
</ToolbarMenu>
Expand Down
43 changes: 18 additions & 25 deletions src/components/DataSamplesMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
MenuList,
Portal,
Text,
useDisclosure,
} from "@chakra-ui/react";
import { useCallback } from "react";
import { MdMoreVert } from "react-icons/md";
Expand All @@ -18,10 +17,6 @@ import {
RiUpload2Line,
} from "react-icons/ri";
import { FormattedMessage, useIntl } from "react-intl";
import {
ConnectionFlowStep,
useConnectionStage,
} from "../connection-stage-hooks";
import { useLogging } from "../logging/logging-hooks";
import { useStore } from "../store";
import { getTotalNumSamples } from "../utils/actions";
Expand All @@ -36,9 +31,15 @@ const DataSamplesMenu = () => {
const logging = useLogging();
const actions = useStore((s) => s.actions);
const downloadDataset = useStore((s) => s.downloadDataset);
const { stage } = useConnectionStage();
const deleteConfirmDisclosure = useDisclosure();
const nameProjectDialogDisclosure = useDisclosure();
const isDeleteAllActionsDialogOpen = useStore(
(s) => s.isDeleteAllActionsDialogOpen
);
const deleteAllActionsDialogOnOpen = useStore(
(s) => s.deleteAllActionsDialogOnOpen
);
const closeDialog = useStore((s) => s.closeDialog);
const isNameProjectDialogOpen = useStore((s) => s.isNameProjectDialogOpen);
const nameProjectDialogOnOpen = useStore((s) => s.nameProjectDialogOnOpen);
const isUntitled = useProjectIsUntitled();
const setProjectName = useStore((s) => s.setProjectName);

Expand All @@ -58,43 +59,35 @@ const DataSamplesMenu = () => {
type: "dataset-delete",
});
deleteAllActions();
deleteConfirmDisclosure.onClose();
}, [deleteAllActions, deleteConfirmDisclosure, logging]);
}, [deleteAllActions, logging]);

const handleSave = useCallback(
(newName?: string) => {
if (newName) {
setProjectName(newName);
}
download();
nameProjectDialogDisclosure.onClose();
},
[download, nameProjectDialogDisclosure, setProjectName]
[download, setProjectName]
);

const handleDownloadDataset = useCallback(() => {
if (isUntitled) {
nameProjectDialogDisclosure.onOpen();
nameProjectDialogOnOpen();
} else {
download();
}
}, [download, isUntitled, nameProjectDialogDisclosure]);
}, [download, isUntitled, nameProjectDialogOnOpen]);

return (
<>
<NameProjectDialog
isOpen={
stage.flowStep === ConnectionFlowStep.None &&
nameProjectDialogDisclosure.isOpen
}
onClose={nameProjectDialogDisclosure.onClose}
isOpen={isNameProjectDialogOpen}
onClose={closeDialog}
onSave={handleSave}
/>
<ConfirmDialog
isOpen={
deleteConfirmDisclosure.isOpen &&
stage.flowStep === ConnectionFlowStep.None
}
isOpen={isDeleteAllActionsDialogOpen}
heading={intl.formatMessage({
id: "delete-data-samples-confirm-heading",
})}
Expand All @@ -104,7 +97,7 @@ const DataSamplesMenu = () => {
</Text>
}
onConfirm={handleDeleteAllActions}
onCancel={deleteConfirmDisclosure.onClose}
onCancel={closeDialog}
/>
<Menu>
<MenuButton
Expand All @@ -130,7 +123,7 @@ const DataSamplesMenu = () => {
</MenuItem>
<MenuItem
icon={<RiDeleteBin2Line />}
onClick={deleteConfirmDisclosure.onOpen}
onClick={deleteAllActionsDialogOnOpen}
>
<FormattedMessage id="delete-data-samples-action" />
</MenuItem>
Expand Down
45 changes: 10 additions & 35 deletions src/components/DefaultPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,17 @@ import {
VStack,
} from "@chakra-ui/react";
import { ReactNode, useCallback, useEffect } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { RiDownload2Line, RiHome2Line } from "react-icons/ri";
import { FormattedMessage, useIntl } from "react-intl";
import { useNavigate } from "react-router";
import {
ConnectionFlowStep,
useConnectionStage,
} from "../connection-stage-hooks";
import { useDeployment } from "../deployment";
import { flags } from "../flags";
import { useProject } from "../hooks/project-hooks";
import { globalShortcutConfig, keyboardShortcuts } from "../keyboard-shortcuts";
import {
PostImportDialogState,
SaveStep,
TrainModelDialogStage,
} from "../model";
keyboardShortcuts,
useKeyboardShortcut,
} from "../keyboard-shortcut-hooks";
import { PostImportDialogState } from "../model";
import Tour from "../pages/Tour";
import { useStore } from "../store";
import { createHomePageUrl } from "../urls";
Expand Down Expand Up @@ -58,17 +52,10 @@ const DefaultPageLayout = ({
showPageTitle = false,
}: DefaultPageLayoutProps) => {
const intl = useIntl();
const isEditorOpen = useStore((s) => s.isEditorOpen);
const isTourClosed = useStore((s) => s.tourState === undefined);
const isTrainDialogClosed = useStore(
(s) => s.trainModelDialogStage === TrainModelDialogStage.Closed
);
const isMakeCodeErrorDialogClosed = useStore(
(s) => !s.isEditorTimedOutDialogOpen
const isConnectionDialogOpen = useStore((s) => s.isConnectionDialogOpen);
const isNonConnectionDialogOpen = useStore((s) =>
s.isNonConnectionDialogOpen()
);
const { stage } = useConnectionStage();
const isConnectionDialogClosed = stage.flowStep === ConnectionFlowStep.None;
const isSaveDialogClosed = useStore((s) => s.save.step === SaveStep.None);
const { appNameFull } = useDeployment();

useEffect(() => {
Expand All @@ -86,14 +73,7 @@ const DefaultPageLayout = ({
return (
<>
{/* Suppress dialogs to prevent overlapping dialogs */}
{!isEditorOpen &&
isTrainDialogClosed &&
isTourClosed &&
isSaveDialogClosed &&
isMakeCodeErrorDialogClosed &&
postImportDialogState === PostImportDialogState.None && (
<ConnectionDialogs />
)}
{!isNonConnectionDialogOpen && <ConnectionDialogs />}
<Tour />
<SaveDialogs />
<NotCreateAiHexImportDialog
Expand All @@ -106,12 +86,7 @@ const DefaultPageLayout = ({
/>
<MakeCodeLoadErrorDialog />
<ProjectDropTarget
isEnabled={
isTrainDialogClosed &&
isTourClosed &&
isConnectionDialogClosed &&
isSaveDialogClosed
}
isEnabled={!isNonConnectionDialogOpen && !isConnectionDialogOpen}
>
<VStack
minH="100vh"
Expand Down Expand Up @@ -169,7 +144,7 @@ export const ProjectToolbarItems = () => {
const handleSave = useCallback(() => {
void saveHex();
}, [saveHex]);
useHotkeys(keyboardShortcuts.saveSession, handleSave, globalShortcutConfig);
useKeyboardShortcut(keyboardShortcuts.saveSession, handleSave);

return (
<>
Expand Down
21 changes: 9 additions & 12 deletions src/components/LiveGraphPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,18 @@ import {
import { useCallback, useRef } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { ConnectionStatus } from "../connect-status-hooks";
import {
ConnectionFlowStep,
useConnectionStage,
} from "../connection-stage-hooks";
import { useConnectionStage } from "../connection-stage-hooks";
import microbitImage from "../images/stylised-microbit-black.svg";
import {
keyboardShortcuts,
useKeyboardShortcut,
} from "../keyboard-shortcut-hooks";
import { useLogging } from "../logging/logging-hooks";
import { tourElClassname } from "../tours";
import AlertIcon from "./AlertIcon";
import InfoToolTip from "./InfoToolTip";
import LiveGraph from "./LiveGraph";
import PredictedAction from "./PredictedAction";
import { useHotkeys } from "react-hotkeys-hook";
import { globalShortcutConfig, keyboardShortcuts } from "../keyboard-shortcuts";

interface LiveGraphPanelProps {
showPredictedAction?: boolean;
Expand All @@ -36,7 +35,7 @@ const LiveGraphPanel = ({
showPredictedAction,
disconnectedTextId,
}: LiveGraphPanelProps) => {
const { actions, status, isConnected, stage } = useConnectionStage();
const { actions, status, isConnected } = useConnectionStage();
const parentPortalRef = useRef(null);
const logging = useLogging();
const isReconnecting =
Expand All @@ -62,8 +61,7 @@ const LiveGraphPanel = ({
void actions.reconnect();
}
}, [status, actions, logging]);
useHotkeys(keyboardShortcuts.connect, handleConnectOrReconnect, {
...globalShortcutConfig,
useKeyboardShortcut(keyboardShortcuts.connect, handleConnectOrReconnect, {
enabled: isDisconnected,
});
const handleDisconnect = useCallback(() => {
Expand All @@ -72,9 +70,8 @@ const LiveGraphPanel = ({
});
void actions.disconnect();
}, [actions, logging]);
useHotkeys(keyboardShortcuts.disconnect, handleDisconnect, {
...globalShortcutConfig,
enabled: isConnected && stage.flowStep === ConnectionFlowStep.None,
useKeyboardShortcut(keyboardShortcuts.disconnect, handleDisconnect, {
enabled: isConnected,
});
const intl = useIntl();
return (
Expand Down
Loading

0 comments on commit 9f6f16e

Please sign in to comment.