From 0084946b76c3531da92f76d0581176d95177da58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Mon, 24 Mar 2025 15:34:09 +0100 Subject: [PATCH] 608 fix hotkey scope and dropdown issues in the command menu (#11121) Closes https://github.com/twentyhq/core-team-issues/issues/608 - Introduces a new hotkey scope `CommandMenuFocused` - Fixes hotkey scopes issues in the side panel --- .../CommandMenuActionMenuDropdown.tsx | 20 ++++------------ .../RecordShowRightDrawerOpenRecordButton.tsx | 24 ++++++++++++------- .../components/ActivityRichTextEditor.tsx | 4 ++-- .../components/CommandMenuContainer.tsx | 4 ++-- .../CommandMenuContextChipGroups.tsx | 2 +- .../hooks/useCommandMenuHotKeys.ts | 5 ++-- .../hooks/useNavigateCommandMenu.ts | 9 +++++-- .../types/CommandMenuHotkeyScope.ts | 3 +++ .../ui/layout/dropdown/hooks/useDropdownV2.ts | 4 ++-- .../hotkey/hooks/useSetHotkeyScope.ts | 6 ++++- 10 files changed, 45 insertions(+), 36 deletions(-) create mode 100644 packages/twenty-front/src/modules/command-menu/types/CommandMenuHotkeyScope.ts diff --git a/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx index 0e706bacf..6dda3ef6f 100644 --- a/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx @@ -12,7 +12,6 @@ import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/com import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useTheme } from '@emotion/react'; import { i18n } from '@lingui/core'; -import { Key } from 'ts-key-enum'; import { Button, getOsControlSymbol, MenuItem } from 'twenty-ui'; export const CommandMenuActionMenuDropdown = () => { @@ -24,25 +23,14 @@ export const CommandMenuActionMenuDropdown = () => { ActionMenuComponentInstanceContext, ); - const { closeDropdown, openDropdown } = useDropdownV2(); + const { toggleDropdown } = useDropdownV2(); const theme = useTheme(); - useScopedHotkeys( - [Key.Escape, 'ctrl+o,meta+o'], - () => { - closeDropdown( - getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId), - ); - }, - CommandMenuActionMenuDropdownHotkeyScope.CommandMenuActionMenuDropdown, - [closeDropdown], - ); - useScopedHotkeys( ['ctrl+o,meta+o'], () => { - openDropdown( + toggleDropdown( getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId), { scope: @@ -51,7 +39,7 @@ export const CommandMenuActionMenuDropdown = () => { ); }, AppHotkeyScope.CommandMenuOpen, - [openDropdown], + [toggleDropdown], ); return ( @@ -81,7 +69,7 @@ export const CommandMenuActionMenuDropdown = () => { key={index} LeftIcon={actionMenuEntry.Icon} onClick={() => { - closeDropdown( + toggleDropdown( getRightDrawerActionMenuDropdownIdFromActionMenuId( actionMenuId, ), diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerOpenRecordButton.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerOpenRecordButton.tsx index f4ff71984..f20bdc61c 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerOpenRecordButton.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerOpenRecordButton.tsx @@ -1,4 +1,5 @@ -import { CommandMenuActionMenuDropdownHotkeyScope } from '@/action-menu/types/CommandMenuActionMenuDropdownHotkeyScope'; +import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; +import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; @@ -6,10 +7,12 @@ import { getLinkToShowPage } from '@/object-metadata/utils/getLinkToShowPage'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { AppPath } from '@/types/AppPath'; +import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId'; import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; @@ -63,6 +66,12 @@ export const RecordShowRightDrawerOpenRecordButton = ({ const navigate = useNavigateApp(); + const actionMenuId = useAvailableComponentInstanceIdOrThrow( + ActionMenuComponentInstanceContext, + ); + + const { closeDropdown } = useDropdownV2(); + const handleOpenRecord = useCallback(() => { const tabIdToOpen = activeTabIdInRightDrawer === 'home' @@ -79,10 +88,16 @@ export const RecordShowRightDrawerOpenRecordButton = ({ objectRecordId: recordId, }); + closeDropdown( + getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId), + ); + closeCommandMenu(); }, [ + actionMenuId, activeTabIdInRightDrawer, closeCommandMenu, + closeDropdown, navigate, objectNameSingular, recordId, @@ -96,13 +111,6 @@ export const RecordShowRightDrawerOpenRecordButton = ({ [closeCommandMenu, navigate, objectNameSingular, recordId], ); - useScopedHotkeys( - ['ctrl+Enter,meta+Enter'], - handleOpenRecord, - CommandMenuActionMenuDropdownHotkeyScope.CommandMenuActionMenuDropdown, - [closeCommandMenu, navigate, objectNameSingular, recordId], - ); - if (!isDefined(record)) { return null; } diff --git a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx index b7039a4f0..e2add60d1 100644 --- a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx +++ b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx @@ -17,7 +17,6 @@ import { recordStoreFamilyState } from '@/object-record/record-store/states/reco import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; -import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritingKey'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { Key } from 'ts-key-enum'; @@ -26,6 +25,7 @@ import { useDebouncedCallback } from 'use-debounce'; import { ActivityRichTextEditorChangeOnActivityIdEffect } from '@/activities/components/ActivityRichTextEditorChangeOnActivityIdEffect'; import { Note } from '@/activities/types/Note'; import { Task } from '@/activities/types/Task'; +import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeyScope'; import { BlockEditor } from '@/ui/input/editor/components/BlockEditor'; import { PartialBlock } from '@blocknote/core'; import '@blocknote/core/fonts/inter.css'; @@ -298,7 +298,7 @@ export const ActivityRichTextEditor = ({ editor.setTextCursorPosition(newBlockId, 'end'); editor.focus(); }, - AppHotkeyScope.CommandMenuOpen, + CommandMenuHotkeyScope.CommandMenuFocused, [], { preventDefault: false, diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx index 8a8a7f734..d0109c129 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx @@ -14,6 +14,7 @@ import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKey import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { CommandMenuAnimationVariant } from '@/command-menu/types/CommandMenuAnimationVariant'; +import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeyScope'; import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; @@ -23,7 +24,6 @@ import { RecordFiltersComponentInstanceContext } from '@/object-record/record-fi import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext'; import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId'; import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; -import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; @@ -74,7 +74,7 @@ export const CommandMenuContainer = ({ .getLoadable(currentHotkeyScopeState) .getValue(); - if (hotkeyScope.scope === AppHotkeyScope.CommandMenuOpen) { + if (hotkeyScope?.scope === CommandMenuHotkeyScope.CommandMenuFocused) { closeCommandMenu(); } }, diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroups.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroups.tsx index bd6a305c5..f769e6f54 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroups.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroups.tsx @@ -3,12 +3,12 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; +import { isDefined } from 'twenty-shared/utils'; import { MenuItem } from 'twenty-ui'; import { CommandMenuContextChip, CommandMenuContextChipProps, } from './CommandMenuContextChip'; -import { isDefined } from 'twenty-shared/utils'; export const CommandMenuContextChipGroups = ({ contextChips, diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts index 6ff649965..574a55096 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts @@ -5,6 +5,7 @@ import { useOpenRecordsSearchPageInCommandMenu } from '@/command-menu/hooks/useO import { useSetGlobalCommandMenuContext } from '@/command-menu/hooks/useSetGlobalCommandMenuContext'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; +import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeyScope'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu'; @@ -62,7 +63,7 @@ export const useCommandMenuHotKeys = () => { () => { goBackFromCommandMenu(); }, - AppHotkeyScope.CommandMenuOpen, + CommandMenuHotkeyScope.CommandMenuFocused, [goBackFromCommandMenu], ); @@ -87,7 +88,7 @@ export const useCommandMenuHotKeys = () => { goBackFromCommandMenu(); } }, - AppHotkeyScope.CommandMenuOpen, + CommandMenuHotkeyScope.CommandMenuFocused, [ commandMenuPage, commandMenuSearch, diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts index c82c84f43..554e2e2f3 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts @@ -9,11 +9,11 @@ import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState'; import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; +import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeyScope'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId'; import { isDragSelectionStartEnabledState } from '@/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; -import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { useRecoilCallback } from 'recoil'; import { IconComponent } from 'twenty-ui'; import { v4 } from 'uuid'; @@ -49,7 +49,12 @@ export const useNavigateCommandMenu = () => { commandMenuCloseAnimationCompleteCleanup(); } - setHotkeyScopeAndMemorizePreviousScope(AppHotkeyScope.CommandMenuOpen); + setHotkeyScopeAndMemorizePreviousScope( + CommandMenuHotkeyScope.CommandMenuFocused, + { + commandMenuOpen: true, + }, + ); if (isCommandMenuOpened) { return; diff --git a/packages/twenty-front/src/modules/command-menu/types/CommandMenuHotkeyScope.ts b/packages/twenty-front/src/modules/command-menu/types/CommandMenuHotkeyScope.ts new file mode 100644 index 000000000..55f742a98 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/types/CommandMenuHotkeyScope.ts @@ -0,0 +1,3 @@ +export enum CommandMenuHotkeyScope { + CommandMenuFocused = 'command-menu-focused', +} diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdownV2.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdownV2.ts index ae563c4fc..be0764caf 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdownV2.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdownV2.ts @@ -62,7 +62,7 @@ export const useDropdownV2 = () => { const toggleDropdown = useRecoilCallback( ({ snapshot }) => - (specificComponentId: string) => { + (specificComponentId: string, customHotkeyScope?: HotkeyScope) => { const scopeId = getScopeIdFromComponentId(specificComponentId); const isDropdownOpen = snapshot .getLoadable(isDropdownOpenComponentState({ scopeId })) @@ -71,7 +71,7 @@ export const useDropdownV2 = () => { if (isDropdownOpen) { closeDropdown(specificComponentId); } else { - openDropdown(specificComponentId); + openDropdown(specificComponentId, customHotkeyScope); } }, [closeDropdown, openDropdown], diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useSetHotkeyScope.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useSetHotkeyScope.ts index 2ca2ea03e..543909293 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useSetHotkeyScope.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/hooks/useSetHotkeyScope.ts @@ -3,13 +3,13 @@ import { useRecoilCallback } from 'recoil'; import { DEBUG_HOTKEY_SCOPE } from '@/ui/utilities/hotkey/hooks/useScopedHotkeyCallback'; import { logDebug } from '~/utils/logDebug'; +import { isDefined } from 'twenty-shared/utils'; import { DEFAULT_HOTKEYS_SCOPE_CUSTOM_SCOPES } from '../constants/DefaultHotkeysScopeCustomScopes'; import { currentHotkeyScopeState } from '../states/internal/currentHotkeyScopeState'; import { internalHotkeysEnabledScopesState } from '../states/internal/internalHotkeysEnabledScopesState'; import { AppHotkeyScope } from '../types/AppHotkeyScope'; import { CustomHotkeyScopes } from '../types/CustomHotkeyScope'; import { HotkeyScope } from '../types/HotkeyScope'; -import { isDefined } from 'twenty-shared/utils'; const isCustomScopesEqual = ( customScopesA: CustomHotkeyScopes | undefined, @@ -69,6 +69,10 @@ export const useSetHotkeyScope = () => scopesToSet.push(AppHotkeyScope.CommandMenu); } + if (newHotkeyScope.customScopes?.commandMenuOpen === true) { + scopesToSet.push(AppHotkeyScope.CommandMenuOpen); + } + if (newHotkeyScope?.customScopes?.goto === true) { scopesToSet.push(AppHotkeyScope.Goto); }