Raphaël Bosi
2025-01-24 16:10:02 +01:00
committed by GitHub
parent 29df6e64a0
commit ff41768e8f
15 changed files with 438 additions and 221 deletions

View File

@ -1,19 +1,24 @@
import { CommandGroup } from '@/command-menu/components/CommandGroup';
import { CommandMenuDefaultSelectionEffect } from '@/command-menu/components/CommandMenuDefaultSelectionEffect';
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { ResetContextToSelectionCommandButton } from '@/command-menu/components/ResetContextToSelectionCommandButton';
import { COMMAND_MENU_SEARCH_BAR_HEIGHT } from '@/command-menu/constants/CommandMenuSearchBarHeight';
import { COMMAND_MENU_SEARCH_BAR_PADDING } from '@/command-menu/constants/CommandMenuSearchBarPadding';
import { useCommandMenuOnItemClick } from '@/command-menu/hooks/useCommandMenuOnItemClick';
import { useMatchingCommandMenuCommands } from '@/command-menu/hooks/useMatchingCommandMenuCommands';
import { useResetPreviousCommandMenuContext } from '@/command-menu/hooks/useResetPreviousCommandMenuContext';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { Command } from '@/command-menu/types/Command';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-ui';
@ -60,6 +65,8 @@ export const CommandMenu = () => {
const { t } = useLingui();
const { onItemClick } = useCommandMenuOnItemClick();
const { resetPreviousCommandMenuContext } =
useResetPreviousCommandMenuContext();
const commandMenuSearch = useRecoilValue(commandMenuSearchState);
@ -84,22 +91,33 @@ export const CommandMenu = () => {
commandMenuSearch,
});
const selectableItems = copilotCommands
.concat(matchingStandardActionRecordSelectionCommands)
.concat(matchingWorkflowRunRecordSelectionCommands)
.concat(matchingStandardActionGlobalCommands)
.concat(matchingWorkflowRunGlobalCommands)
.concat(matchingNavigateCommand)
.concat(peopleCommands)
.concat(companyCommands)
.concat(opportunityCommands)
.concat(noteCommands)
.concat(tasksCommands)
.concat(customObjectCommands)
const selectableItems: Command[] = copilotCommands
.concat(
matchingStandardActionRecordSelectionCommands,
matchingWorkflowRunRecordSelectionCommands,
matchingStandardActionGlobalCommands,
matchingWorkflowRunGlobalCommands,
matchingNavigateCommand,
peopleCommands,
companyCommands,
opportunityCommands,
noteCommands,
tasksCommands,
customObjectCommands,
)
.filter(isDefined);
const previousContextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataIdComponentState,
'command-menu-previous',
);
const selectableItemIds = selectableItems.map((item) => item.id);
if (isNonEmptyString(previousContextStoreCurrentObjectMetadataId)) {
selectableItemIds.unshift('reset-context-to-selection');
}
const commandGroups: CommandGroupConfig[] = [
{
heading: t`Copilot`,
@ -168,6 +186,11 @@ export const CommandMenu = () => {
selectableItemIdArray={selectableItemIds}
hotkeyScope={AppHotkeyScope.CommandMenu}
onEnter={(itemId) => {
if (itemId === 'reset-context-to-selection') {
resetPreviousCommandMenuContext();
return;
}
const command = selectableItems.find(
(item) => item.id === itemId,
);
@ -184,6 +207,16 @@ export const CommandMenu = () => {
}
}}
>
{isNonEmptyString(
previousContextStoreCurrentObjectMetadataId,
) && (
<CommandGroup heading={t`Context`} key={t`Context`}>
<SelectableItem itemId="reset-context-to-selection">
<ResetContextToSelectionCommandButton />
</SelectableItem>
</CommandGroup>
)}
{isNoResults && !isLoading && (
<StyledEmpty>No results found</StyledEmpty>
)}

View File

@ -1,6 +1,6 @@
import styled from '@emotion/styled';
const StyledChip = styled.div`
const StyledChip = styled.div<{ variant?: 'default' | 'small' }>`
align-items: center;
background: ${({ theme }) => theme.background.transparent.light};
border: 1px solid ${({ theme }) => theme.border.color.medium};
@ -8,7 +8,8 @@ const StyledChip = styled.div`
box-sizing: border-box;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
height: ${({ theme }) => theme.spacing(8)};
height: ${({ theme, variant }) =>
variant === 'small' ? theme.spacing(6) : theme.spacing(8)};
padding: 0 ${({ theme }) => theme.spacing(2)};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
@ -40,13 +41,15 @@ export const CommandMenuContextChip = ({
Icons,
text,
withIconBackground,
variant = 'default',
}: {
Icons: React.ReactNode[];
text?: string;
withIconBackground?: boolean;
variant?: 'default' | 'small';
}) => {
return (
<StyledChip>
<StyledChip variant={variant}>
<StyledIconsContainer>
{Icons.map((Icon, index) => (
<StyledIconWrapper

View File

@ -1,14 +1,17 @@
import { CommandMenuContextChip } from '@/command-menu/components/CommandMenuContextChip';
import { CommandMenuContextRecordChipAvatars } from '@/command-menu/components/CommandMenuContextRecordChipAvatars';
import { getSelectedRecordsContextText } from '@/command-menu/utils/getRecordContextText';
import { useFindManyRecordsSelectedInContextStore } from '@/context-store/hooks/useFindManyRecordsSelectedInContextStore';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { capitalize } from 'twenty-shared';
export const CommandMenuContextRecordChip = ({
objectMetadataItemId,
instanceId,
variant = 'default',
}: {
objectMetadataItemId: string;
instanceId?: string;
variant?: 'default' | 'small';
}) => {
const { objectMetadataItem } = useObjectMetadataItemById({
objectId: objectMetadataItemId,
@ -17,6 +20,7 @@ export const CommandMenuContextRecordChip = ({
const { records, loading, totalCount } =
useFindManyRecordsSelectedInContextStore({
limit: 3,
instanceId,
});
if (loading || !totalCount) {
@ -31,17 +35,16 @@ export const CommandMenuContextRecordChip = ({
/>
));
const text =
totalCount === 1
? getObjectRecordIdentifier({ objectMetadataItem, record: records[0] })
.name
: `${totalCount} ${capitalize(objectMetadataItem.namePlural)}`;
return (
<CommandMenuContextChip
text={text}
text={getSelectedRecordsContextText(
objectMetadataItem,
records,
totalCount,
)}
Icons={Avatars}
withIconBackground={true}
variant={variant}
/>
);
};

View File

@ -14,7 +14,10 @@ export const CommandMenuDefaultSelectionEffect = ({
const selectedItemId = useRecoilValue(selectedItemIdState);
useEffect(() => {
if (isDefined(selectedItemId)) {
if (
isDefined(selectedItemId) &&
selectableItemIds.includes(selectedItemId)
) {
return;
}

View File

@ -4,6 +4,7 @@ import { IconArrowUpRight, IconComponent, MenuItemCommand } from 'twenty-ui';
import { useCommandMenuOnItemClick } from '@/command-menu/hooks/useCommandMenuOnItemClick';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { ReactNode } from 'react';
export type CommandMenuItemProps = {
label: string;
@ -14,6 +15,7 @@ export type CommandMenuItemProps = {
firstHotKey?: string;
secondHotKey?: string;
shouldCloseCommandMenuOnClick?: boolean;
RightComponent?: ReactNode;
};
export const CommandMenuItem = ({
@ -25,6 +27,7 @@ export const CommandMenuItem = ({
firstHotKey,
secondHotKey,
shouldCloseCommandMenuOnClick,
RightComponent,
}: CommandMenuItemProps) => {
const { onItemClick } = useCommandMenuOnItemClick();
@ -49,6 +52,7 @@ export const CommandMenuItem = ({
})
}
isSelected={isSelectedItemId}
RightComponent={RightComponent}
/>
);
};

View File

@ -0,0 +1,56 @@
import { CommandMenuContextRecordChip } from '@/command-menu/components/CommandMenuContextRecordChip';
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { useResetPreviousCommandMenuContext } from '@/command-menu/hooks/useResetPreviousCommandMenuContext';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { t } from '@lingui/core/macro';
import { useRecoilValue } from 'recoil';
import { IconArrowBackUp, isDefined } from 'twenty-ui';
export const ResetContextToSelectionCommandButton = () => {
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
'command-menu-previous',
);
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataIdComponentState,
'command-menu-previous',
);
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const objectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.id === contextStoreCurrentObjectMetadataId,
);
const { resetPreviousCommandMenuContext } =
useResetPreviousCommandMenuContext();
if (
!isDefined(objectMetadataItem) ||
(contextStoreTargetedRecordsRule.mode === 'selection' &&
contextStoreTargetedRecordsRule.selectedRecordIds.length === 0)
) {
return null;
}
return (
<CommandMenuItem
id="reset-context-to-selection"
Icon={IconArrowBackUp}
label={t`Reset to`}
RightComponent={
<CommandMenuContextRecordChip
objectMetadataItemId={objectMetadataItem.id}
instanceId="command-menu-previous"
variant="small"
/>
}
onClick={resetPreviousCommandMenuContext}
/>
);
};

View File

@ -4,19 +4,18 @@ import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchS
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { isDefined } from '~/utils/isDefined';
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { CommandMenuPages } from '@/command-menu/components/CommandMenuPages';
import { useCopyContextStoreStates } from '@/command-menu/hooks/useCopyContextStoreAndActionMenuStates';
import { useResetContextStoreStates } from '@/command-menu/hooks/useResetContextStoreStates';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageTitle';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
import { emitRightDrawerCloseEvent } from '@/ui/layout/right-drawer/utils/emitRightDrawerCloseEvent';
@ -33,120 +32,22 @@ export const useCommandMenu = () => {
mainContextStoreComponentInstanceIdState,
);
const { copyContextStoreStates } = useCopyContextStoreStates();
const { resetContextStoreStates } = useResetContextStoreStates();
const openCommandMenu = useRecoilCallback(
({ snapshot, set }) =>
({ set }) =>
() => {
if (isDefined(mainContextStoreComponentInstanceId)) {
const contextStoreCurrentObjectMetadataId = snapshot
.getLoadable(
contextStoreCurrentObjectMetadataIdComponentState.atomFamily({
instanceId: mainContextStoreComponentInstanceId,
}),
)
.getValue();
set(
contextStoreCurrentObjectMetadataIdComponentState.atomFamily({
instanceId: 'command-menu',
}),
contextStoreCurrentObjectMetadataId,
);
const contextStoreTargetedRecordsRule = snapshot
.getLoadable(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: mainContextStoreComponentInstanceId,
}),
)
.getValue();
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: 'command-menu',
}),
contextStoreTargetedRecordsRule,
);
const contextStoreNumberOfSelectedRecords = snapshot
.getLoadable(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: mainContextStoreComponentInstanceId,
}),
)
.getValue();
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: 'command-menu',
}),
contextStoreNumberOfSelectedRecords,
);
const contextStoreFilters = snapshot
.getLoadable(
contextStoreFiltersComponentState.atomFamily({
instanceId: mainContextStoreComponentInstanceId,
}),
)
.getValue();
set(
contextStoreFiltersComponentState.atomFamily({
instanceId: 'command-menu',
}),
contextStoreFilters,
);
const contextStoreCurrentViewId = snapshot
.getLoadable(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: mainContextStoreComponentInstanceId,
}),
)
.getValue();
set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: 'command-menu',
}),
contextStoreCurrentViewId,
);
const contextStoreCurrentViewType = snapshot
.getLoadable(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: mainContextStoreComponentInstanceId,
}),
)
.getValue();
set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: 'command-menu',
}),
contextStoreCurrentViewType,
);
}
const actionMenuEntries = snapshot
.getLoadable(
actionMenuEntriesComponentState.atomFamily({
instanceId: mainContextStoreComponentInstanceId,
}),
)
.getValue();
set(
actionMenuEntriesComponentState.atomFamily({
instanceId: 'command-menu',
}),
actionMenuEntries,
);
copyContextStoreStates({
instanceIdToCopyFrom: mainContextStoreComponentInstanceId,
instanceIdToCopyTo: 'command-menu',
});
set(isCommandMenuOpenedState, true);
setHotkeyScopeAndMemorizePreviousScope(AppHotkeyScope.CommandMenuOpen);
},
[
copyContextStoreStates,
mainContextStoreComponentInstanceId,
setHotkeyScopeAndMemorizePreviousScope,
],
@ -160,57 +61,8 @@ export const useCommandMenu = () => {
.getValue();
if (isCommandMenuOpened) {
set(
contextStoreCurrentObjectMetadataIdComponentState.atomFamily({
instanceId: 'command-menu',
}),
null,
);
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: 'command-menu',
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: 'command-menu',
}),
0,
);
set(
contextStoreFiltersComponentState.atomFamily({
instanceId: 'command-menu',
}),
[],
);
set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: 'command-menu',
}),
null,
);
set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: 'command-menu',
}),
null,
);
set(
actionMenuEntriesComponentState.atomFamily({
instanceId: 'command-menu',
}),
new Map(),
);
resetContextStoreStates('command-menu');
resetContextStoreStates('command-menu-previous');
set(viewableRecordIdState, null);
set(commandMenuPageState, CommandMenuPages.Root);
@ -225,7 +77,7 @@ export const useCommandMenu = () => {
emitRightDrawerCloseEvent();
}
},
[goBackToPreviousHotkeyScope, resetSelectedItem],
[goBackToPreviousHotkeyScope, resetContextStoreStates, resetSelectedItem],
);
const toggleCommandMenu = useRecoilCallback(
@ -258,44 +110,59 @@ export const useCommandMenu = () => {
[openCommandMenu],
);
const setGlobalCommandMenuContext = useRecoilCallback(({ set }) => {
return () => {
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: 'command-menu',
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
const setGlobalCommandMenuContext = useRecoilCallback(
({ set }) => {
return () => {
copyContextStoreStates({
instanceIdToCopyFrom: 'command-menu',
instanceIdToCopyTo: 'command-menu-previous',
});
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: 'command-menu',
}),
0,
);
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: 'command-menu',
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: 'command-menu',
}),
null,
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: 'command-menu',
}),
0,
);
set(commandMenuPageInfoState, {
title: undefined,
Icon: undefined,
});
};
}, []);
set(
contextStoreFiltersComponentState.atomFamily({
instanceId: 'command-menu',
}),
[],
);
set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: 'command-menu',
}),
ContextStoreViewType.Table,
);
set(commandMenuPageInfoState, {
title: undefined,
Icon: undefined,
});
};
},
[copyContextStoreStates],
);
return {
openCommandMenu,
closeCommandMenu,
openRecordInCommandMenu,
toggleCommandMenu,
resetCommandMenuContext: setGlobalCommandMenuContext,
setGlobalCommandMenuContext,
};
};

View File

@ -2,15 +2,17 @@ import { CommandMenuPages } from '@/command-menu/components/CommandMenuPages';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isNonEmptyString } from '@sniptt/guards';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
export const useCommandMenuHotKeys = () => {
const { closeCommandMenu, toggleCommandMenu, resetCommandMenuContext } =
const { closeCommandMenu, toggleCommandMenu, setGlobalCommandMenuContext } =
useCommandMenu();
const commandMenuSearch = useRecoilValue(commandMenuSearchState);
@ -19,6 +21,11 @@ export const useCommandMenuHotKeys = () => {
const commandMenuPage = useRecoilValue(commandMenuPageState);
const contextStoreTargetedRecordsRuleComponent = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
'command-menu',
);
useScopedHotkeys(
'ctrl+k,meta+k',
() => {
@ -43,9 +50,14 @@ export const useCommandMenuHotKeys = () => {
() => {
if (
commandMenuPage === CommandMenuPages.Root &&
!isNonEmptyString(commandMenuSearch)
!isNonEmptyString(commandMenuSearch) &&
!(
contextStoreTargetedRecordsRuleComponent.mode === 'selection' &&
contextStoreTargetedRecordsRuleComponent.selectedRecordIds.length ===
0
)
) {
resetCommandMenuContext();
setGlobalCommandMenuContext();
}
},
AppHotkeyScope.CommandMenuOpen,

View File

@ -0,0 +1,129 @@
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useRecoilCallback } from 'recoil';
export const useCopyContextStoreStates = () => {
const copyContextStoreStates = useRecoilCallback(
({ snapshot, set }) =>
({
instanceIdToCopyFrom,
instanceIdToCopyTo,
}: {
instanceIdToCopyFrom: string;
instanceIdToCopyTo: string;
}) => {
const contextStoreCurrentObjectMetadataId = snapshot
.getLoadable(
contextStoreCurrentObjectMetadataIdComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
set(
contextStoreCurrentObjectMetadataIdComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentObjectMetadataId,
);
const contextStoreTargetedRecordsRule = snapshot
.getLoadable(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreTargetedRecordsRule,
);
const contextStoreNumberOfSelectedRecords = snapshot
.getLoadable(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreNumberOfSelectedRecords,
);
const contextStoreFilters = snapshot
.getLoadable(
contextStoreFiltersComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
set(
contextStoreFiltersComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreFilters,
);
const contextStoreCurrentViewId = snapshot
.getLoadable(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentViewId,
);
const contextStoreCurrentViewType = snapshot
.getLoadable(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreCurrentViewType,
);
const actionMenuEntries = snapshot
.getLoadable(
actionMenuEntriesComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
set(
actionMenuEntriesComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
actionMenuEntries,
);
},
[],
);
return { copyContextStoreStates };
};

View File

@ -0,0 +1,68 @@
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useRecoilCallback } from 'recoil';
export const useResetContextStoreStates = () => {
const resetContextStoreStates = useRecoilCallback(({ set }) => {
return (instanceId: string) => {
set(
contextStoreCurrentObjectMetadataIdComponentState.atomFamily({
instanceId,
}),
null,
);
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId,
}),
{
mode: 'selection',
selectedRecordIds: [],
},
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId,
}),
0,
);
set(
contextStoreFiltersComponentState.atomFamily({
instanceId,
}),
[],
);
set(
contextStoreCurrentViewIdComponentState.atomFamily({
instanceId,
}),
null,
);
set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId,
}),
null,
);
set(
actionMenuEntriesComponentState.atomFamily({
instanceId,
}),
new Map(),
);
};
}, []);
return { resetContextStoreStates };
};

View File

@ -0,0 +1,19 @@
import { useCopyContextStoreStates } from '@/command-menu/hooks/useCopyContextStoreAndActionMenuStates';
import { useResetContextStoreStates } from '@/command-menu/hooks/useResetContextStoreStates';
export const useResetPreviousCommandMenuContext = () => {
const { copyContextStoreStates } = useCopyContextStoreStates();
const { resetContextStoreStates } = useResetContextStoreStates();
const resetPreviousCommandMenuContext = () => {
copyContextStoreStates({
instanceIdToCopyFrom: 'command-menu-previous',
instanceIdToCopyTo: 'command-menu',
});
resetContextStoreStates('command-menu-previous');
};
return {
resetPreviousCommandMenuContext,
};
};

View File

@ -0,0 +1,14 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { capitalize } from 'twenty-shared';
export const getSelectedRecordsContextText = (
objectMetadataItem: ObjectMetadataItem,
records: ObjectRecord[],
totalCount: number,
) => {
return totalCount === 1
? getObjectRecordIdentifier({ objectMetadataItem, record: records[0] }).name
: `${totalCount} ${capitalize(objectMetadataItem.namePlural)}`;
};

View File

@ -53,6 +53,7 @@ export const useFindManyRecordsSelectedInContextStore = ({
contextStoreTargetedRecordsRule.mode === 'selection' &&
contextStoreTargetedRecordsRule.selectedRecordIds.length === 0,
limit,
fetchPolicy: 'cache-and-network',
});
return {

View File

@ -39,6 +39,7 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const { findManyRecordsQuery } = useFindManyRecordsQuery({
objectNameSingular,
recordGqlFields,

View File

@ -8,6 +8,7 @@ import {
import { IconComponent } from '@ui/display';
import { useIsMobile } from '@ui/utilities/responsive/hooks/useIsMobile';
import { ReactNode } from 'react';
import { MenuItemCommandHotKeys } from './MenuItemCommandHotKeys';
const StyledMenuItemLabelText = styled(StyledMenuItemLabel)`
@ -72,6 +73,7 @@ export type MenuItemCommandProps = {
className?: string;
isSelected?: boolean;
onClick?: () => void;
RightComponent?: ReactNode;
};
export const MenuItemCommand = ({
@ -82,6 +84,7 @@ export const MenuItemCommand = ({
className,
isSelected,
onClick,
RightComponent,
}: MenuItemCommandProps) => {
const theme = useTheme();
const isMobile = useIsMobile();
@ -99,6 +102,7 @@ export const MenuItemCommand = ({
</StyledBigIconContainer>
)}
<StyledMenuItemLabelText>{text}</StyledMenuItemLabelText>
{RightComponent}
</StyledMenuItemLeftContent>
{!isMobile && (
<MenuItemCommandHotKeys