mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-30 04:12:28 +00:00 
			
		
		
		
	refactoring, bug fixes and create CommandMenuCommandsEffect
This commit is contained in:
		| @@ -13,6 +13,10 @@ import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchS | ||||
| import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; | ||||
| import { Command, CommandType } from '@/command-menu/types/Command'; | ||||
| import { Company } from '@/companies/types/Company'; | ||||
| import { | ||||
|   ContextStoreTargetedRecordsRule, | ||||
|   contextStoreTargetedRecordsRuleComponentState, | ||||
| } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; | ||||
| import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId'; | ||||
| import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu'; | ||||
| import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; | ||||
| @@ -31,7 +35,12 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; | ||||
| import styled from '@emotion/styled'; | ||||
| import { isNonEmptyString } from '@sniptt/guards'; | ||||
| import { useMemo, useRef } from 'react'; | ||||
| import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; | ||||
| import { | ||||
|   useRecoilCallback, | ||||
|   useRecoilState, | ||||
|   useRecoilValue, | ||||
|   useSetRecoilState, | ||||
| } from 'recoil'; | ||||
| import { Key } from 'ts-key-enum'; | ||||
| import { Avatar, IconNotes, IconSparkles, isDefined } from 'twenty-ui'; | ||||
| import { useDebounce } from 'use-debounce'; | ||||
| @@ -100,6 +109,25 @@ export const CommandMenu = () => { | ||||
|   const commandMenuCommands = useRecoilValue(commandMenuCommandsState); | ||||
|   const { closeKeyboardShortcutMenu } = useKeyboardShortcutMenu(); | ||||
|  | ||||
|   const mainContextStoreComponentInstanceId = useRecoilValue( | ||||
|     mainContextStoreComponentInstanceIdState, | ||||
|   ); | ||||
|  | ||||
|   const setContextStoreTargetedRecordsRule = useRecoilCallback( | ||||
|     ({ set }) => | ||||
|       (rule: ContextStoreTargetedRecordsRule) => { | ||||
|         if (isDefined(mainContextStoreComponentInstanceId)) { | ||||
|           set( | ||||
|             contextStoreTargetedRecordsRuleComponentState.atomFamily({ | ||||
|               instanceId: mainContextStoreComponentInstanceId, | ||||
|             }), | ||||
|             rule, | ||||
|           ); | ||||
|         } | ||||
|       }, | ||||
|     [mainContextStoreComponentInstanceId], | ||||
|   ); | ||||
|  | ||||
|   const isMobile = useIsMobile(); | ||||
|  | ||||
|   useScopedHotkeys( | ||||
| @@ -121,6 +149,23 @@ export const CommandMenu = () => { | ||||
|     [closeCommandMenu], | ||||
|   ); | ||||
|  | ||||
|   useScopedHotkeys( | ||||
|     [Key.Backspace, Key.Delete], | ||||
|     () => { | ||||
|       if (!isNonEmptyString(commandMenuSearch)) { | ||||
|         setContextStoreTargetedRecordsRule({ | ||||
|           mode: 'selection', | ||||
|           selectedRecordIds: [], | ||||
|         }); | ||||
|       } | ||||
|     }, | ||||
|     AppHotkeyScope.CommandMenu, | ||||
|     [closeCommandMenu], | ||||
|     { | ||||
|       preventDefault: false, | ||||
|     }, | ||||
|   ); | ||||
|  | ||||
|   const { loading: isPeopleLoading, records: people } = | ||||
|     useSearchRecords<Person>({ | ||||
|       skip: !isCommandMenuOpened, | ||||
| @@ -304,10 +349,6 @@ export const CommandMenu = () => { | ||||
|     isOpportunitiesLoading || | ||||
|     isCompaniesLoading; | ||||
|  | ||||
|   const mainContextStoreComponentInstanceId = useRecoilValue( | ||||
|     mainContextStoreComponentInstanceIdState, | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       {isCommandMenuOpened && ( | ||||
|   | ||||
| @@ -0,0 +1,29 @@ | ||||
| import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; | ||||
| import { commandMenuCommandsState } from '@/command-menu/states/commandMenuCommandsState'; | ||||
| import { computeCommandMenuCommands } from '@/command-menu/utils/computeCommandMenuCommands'; | ||||
| import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId'; | ||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||
| import { useEffect } from 'react'; | ||||
| import { useRecoilValue, useSetRecoilState } from 'recoil'; | ||||
| import { isDefined } from 'twenty-ui'; | ||||
|  | ||||
| export const CommandMenuCommandsEffect = () => { | ||||
|   const mainContextStoreComponentInstanceId = useRecoilValue( | ||||
|     mainContextStoreComponentInstanceIdState, | ||||
|   ); | ||||
|  | ||||
|   const actionMenuEntries = useRecoilComponentValueV2( | ||||
|     actionMenuEntriesComponentSelector, | ||||
|     mainContextStoreComponentInstanceId, | ||||
|   ); | ||||
|  | ||||
|   const setCommands = useSetRecoilState(commandMenuCommandsState); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (isDefined(mainContextStoreComponentInstanceId)) { | ||||
|       setCommands(computeCommandMenuCommands(actionMenuEntries)); | ||||
|     } | ||||
|   }, [mainContextStoreComponentInstanceId, actionMenuEntries, setCommands]); | ||||
|  | ||||
|   return null; | ||||
| }; | ||||
| @@ -31,7 +31,7 @@ const StyledAvatarWrapper = styled.div` | ||||
|   border-radius: ${({ theme }) => theme.border.radius.sm}; | ||||
|   padding: ${({ theme }) => theme.spacing(0.5)}; | ||||
|   border: 1px solid ${({ theme }) => theme.border.color.medium}; | ||||
|   &:not(:first-child) { | ||||
|   &:not(:first-of-type) { | ||||
|     margin-left: -${({ theme }) => theme.spacing(1)}; | ||||
|   } | ||||
| `; | ||||
| @@ -75,7 +75,7 @@ export const CommandMenuContextRecordChip = () => { | ||||
|  | ||||
|   const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2( | ||||
|     contextStoreCurrentObjectMetadataIdComponentState, | ||||
|     mainContextStoreComponentInstanceId ?? undefined, | ||||
|     mainContextStoreComponentInstanceId, | ||||
|   ); | ||||
|  | ||||
|   const { objectMetadataItem } = useObjectMetadataItemById({ | ||||
| @@ -83,7 +83,7 @@ export const CommandMenuContextRecordChip = () => { | ||||
|   }); | ||||
|  | ||||
|   const { records, loading, totalCount } = useContextStoreSelectedRecords( | ||||
|     mainContextStoreComponentInstanceId ?? undefined, | ||||
|     mainContextStoreComponentInstanceId, | ||||
|   ); | ||||
|  | ||||
|   if (loading || !totalCount) { | ||||
| @@ -96,6 +96,7 @@ export const CommandMenuContextRecordChip = () => { | ||||
|         {records.map((record) => ( | ||||
|           <CommandMenuContextRecordChipAvatars | ||||
|             objectMetadataItem={objectMetadataItem} | ||||
|             key={record.id} | ||||
|             record={record} | ||||
|           /> | ||||
|         ))} | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import { isDefined } from '~/utils/isDefined'; | ||||
|  | ||||
| import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; | ||||
| import { COMMAND_MENU_COMMANDS } from '@/command-menu/constants/CommandMenuCommands'; | ||||
| import { computeCommandMenuCommands } from '@/command-menu/utils/computeCommandMenuCommands'; | ||||
| import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId'; | ||||
| import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; | ||||
| import { ALL_ICONS } from '@ui/display/icon/providers/internal/AllIcons'; | ||||
| @@ -37,39 +38,17 @@ export const useCommandMenu = () => { | ||||
|     ({ snapshot }) => | ||||
|       () => { | ||||
|         if (isDefined(mainContextStoreComponentInstanceId)) { | ||||
|           const actionMenuEntries = snapshot.getLoadable( | ||||
|           const actionMenuEntries = snapshot | ||||
|             .getLoadable( | ||||
|               actionMenuEntriesComponentSelector.selectorFamily({ | ||||
|                 instanceId: mainContextStoreComponentInstanceId, | ||||
|               }), | ||||
|           ); | ||||
|  | ||||
|           const commands = Object.values(COMMAND_MENU_COMMANDS); | ||||
|  | ||||
|           const actionCommands = actionMenuEntries | ||||
|             .getValue() | ||||
|             ?.filter((actionMenuEntry) => actionMenuEntry.type === 'standard') | ||||
|             ?.map((actionMenuEntry) => ({ | ||||
|               id: actionMenuEntry.key, | ||||
|               label: actionMenuEntry.label, | ||||
|               Icon: actionMenuEntry.Icon, | ||||
|               onCommandClick: actionMenuEntry.onClick, | ||||
|               type: CommandType.StandardAction, | ||||
|             })); | ||||
|  | ||||
|           const workflowRunCommands = actionMenuEntries | ||||
|             .getValue() | ||||
|             ?.filter( | ||||
|               (actionMenuEntry) => actionMenuEntry.type === 'workflow-run', | ||||
|             ) | ||||
|             ?.map((actionMenuEntry) => ({ | ||||
|               id: actionMenuEntry.key, | ||||
|               label: actionMenuEntry.label, | ||||
|               Icon: actionMenuEntry.Icon, | ||||
|               onCommandClick: actionMenuEntry.onClick, | ||||
|               type: CommandType.WorkflowRun, | ||||
|             })); | ||||
|             .getValue(); | ||||
|  | ||||
|           setCommands([...commands, ...actionCommands, ...workflowRunCommands]); | ||||
|           const commands = computeCommandMenuCommands(actionMenuEntries); | ||||
|  | ||||
|           setCommands(commands); | ||||
|         } | ||||
|  | ||||
|         setIsCommandMenuOpened(true); | ||||
|   | ||||
| @@ -0,0 +1,31 @@ | ||||
| import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry'; | ||||
| import { COMMAND_MENU_COMMANDS } from '@/command-menu/constants/CommandMenuCommands'; | ||||
| import { CommandType } from '@/command-menu/types/Command'; | ||||
|  | ||||
| export const computeCommandMenuCommands = ( | ||||
|   actionMenuEntries: ActionMenuEntry[], | ||||
| ) => { | ||||
|   const commands = Object.values(COMMAND_MENU_COMMANDS); | ||||
|  | ||||
|   const actionCommands = actionMenuEntries | ||||
|     ?.filter((actionMenuEntry) => actionMenuEntry.type === 'standard') | ||||
|     ?.map((actionMenuEntry) => ({ | ||||
|       id: actionMenuEntry.key, | ||||
|       label: actionMenuEntry.label, | ||||
|       Icon: actionMenuEntry.Icon, | ||||
|       onCommandClick: actionMenuEntry.onClick, | ||||
|       type: CommandType.StandardAction, | ||||
|     })); | ||||
|  | ||||
|   const workflowRunCommands = actionMenuEntries | ||||
|     ?.filter((actionMenuEntry) => actionMenuEntry.type === 'workflow-run') | ||||
|     ?.map((actionMenuEntry) => ({ | ||||
|       id: actionMenuEntry.key, | ||||
|       label: actionMenuEntry.label, | ||||
|       Icon: actionMenuEntry.Icon, | ||||
|       onCommandClick: actionMenuEntry.onClick, | ||||
|       type: CommandType.WorkflowRun, | ||||
|     })); | ||||
|  | ||||
|   return [...commands, ...actionCommands, ...workflowRunCommands]; | ||||
| }; | ||||
| @@ -11,10 +11,10 @@ export const MainContextStoreComponentInstanceIdSetterEffect = () => { | ||||
|   const context = useContext(ContextStoreComponentInstanceContext); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     setMainContextStoreComponentInstanceId(context?.instanceId ?? null); | ||||
|     setMainContextStoreComponentInstanceId(context?.instanceId ?? 'app'); | ||||
|  | ||||
|     return () => { | ||||
|       setMainContextStoreComponentInstanceId(null); | ||||
|       setMainContextStoreComponentInstanceId('app'); | ||||
|     }; | ||||
|   }, [context, setMainContextStoreComponentInstanceId]); | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,6 @@ | ||||
| import { createState } from 'twenty-ui'; | ||||
|  | ||||
| export const mainContextStoreComponentInstanceIdState = createState< | ||||
|   string | null | ||||
| >({ | ||||
| export const mainContextStoreComponentInstanceIdState = createState<string>({ | ||||
|   key: 'mainContextStoreComponentInstanceIdState', | ||||
|   defaultValue: null, | ||||
|   defaultValue: 'app', | ||||
| }); | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import { AuthModal } from '@/auth/components/AuthModal'; | ||||
| import { CommandMenu } from '@/command-menu/components/CommandMenu'; | ||||
| import { CommandMenuCommandsEffect } from '@/command-menu/components/CommandMenuCommandsEffect'; | ||||
| import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary'; | ||||
| import { KeyboardShortcutMenu } from '@/keyboard-shortcut-menu/components/KeyboardShortcutMenu'; | ||||
| import { AppNavigationDrawer } from '@/navigation/components/AppNavigationDrawer'; | ||||
| @@ -80,6 +81,7 @@ export const DefaultLayout = () => { | ||||
|         `} | ||||
|       /> | ||||
|       <StyledLayout> | ||||
|         <CommandMenuCommandsEffect /> | ||||
|         <CommandMenu /> | ||||
|         <KeyboardShortcutMenu /> | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bosiraphael
					bosiraphael