Fix standard actions overriding navigate and create commands when opening the command menu. Before fix: <img width="493" alt="Capture d’écran 2024-10-31 à 18 08 56" src="https://github.com/user-attachments/assets/015bd798-baa4-4f84-8886-e355c0ef1455"> After fix: <img width="499" alt="Capture d’écran 2024-10-31 à 18 08 34" src="https://github.com/user-attachments/assets/02ba7fc4-ec90-4c13-9830-d884c0da37d9"> --------- Co-authored-by: Charles Bochet <charles@twenty.com>
167 lines
5.2 KiB
TypeScript
167 lines
5.2 KiB
TypeScript
import { isNonEmptyString } from '@sniptt/guards';
|
|
import { useCallback } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
|
|
|
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
|
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 { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector';
|
|
import { COMMAND_MENU_COMMANDS } from '@/command-menu/constants/CommandMenuCommands';
|
|
import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId';
|
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
|
import { ALL_ICONS } from '@ui/display/icon/providers/internal/AllIcons';
|
|
import { sortByProperty } from '~/utils/array/sortByProperty';
|
|
import { commandMenuCommandsState } from '../states/commandMenuCommandsState';
|
|
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
|
|
import { Command, CommandType } from '../types/Command';
|
|
|
|
export const useCommandMenu = () => {
|
|
const navigate = useNavigate();
|
|
const setIsCommandMenuOpened = useSetRecoilState(isCommandMenuOpenedState);
|
|
const setCommands = useSetRecoilState(commandMenuCommandsState);
|
|
const { resetSelectedItem } = useSelectableList('command-menu-list');
|
|
const {
|
|
setHotkeyScopeAndMemorizePreviousScope,
|
|
goBackToPreviousHotkeyScope,
|
|
} = usePreviousHotkeyScope();
|
|
|
|
const mainContextStoreComponentInstanceId = useRecoilValue(
|
|
mainContextStoreComponentInstanceIdState,
|
|
);
|
|
|
|
const openCommandMenu = useRecoilCallback(
|
|
({ snapshot }) =>
|
|
() => {
|
|
if (isDefined(mainContextStoreComponentInstanceId)) {
|
|
const actionMenuEntries = snapshot.getLoadable(
|
|
actionMenuEntriesComponentSelector.selectorFamily({
|
|
instanceId: mainContextStoreComponentInstanceId,
|
|
}),
|
|
);
|
|
|
|
const commands = Object.values(COMMAND_MENU_COMMANDS);
|
|
|
|
const actionCommands = actionMenuEntries
|
|
.getValue()
|
|
?.map((actionMenuEntry) => ({
|
|
id: actionMenuEntry.key,
|
|
label: actionMenuEntry.label,
|
|
Icon: actionMenuEntry.Icon,
|
|
onCommandClick: actionMenuEntry.onClick,
|
|
type: CommandType.Action,
|
|
}));
|
|
|
|
setCommands([...commands, ...actionCommands]);
|
|
}
|
|
|
|
setIsCommandMenuOpened(true);
|
|
setHotkeyScopeAndMemorizePreviousScope(AppHotkeyScope.CommandMenuOpen);
|
|
},
|
|
[
|
|
mainContextStoreComponentInstanceId,
|
|
setCommands,
|
|
setHotkeyScopeAndMemorizePreviousScope,
|
|
setIsCommandMenuOpened,
|
|
],
|
|
);
|
|
|
|
const closeCommandMenu = useRecoilCallback(
|
|
({ snapshot }) =>
|
|
() => {
|
|
const isCommandMenuOpened = snapshot
|
|
.getLoadable(isCommandMenuOpenedState)
|
|
.getValue();
|
|
|
|
if (isCommandMenuOpened) {
|
|
setIsCommandMenuOpened(false);
|
|
setCommands([]);
|
|
resetSelectedItem();
|
|
goBackToPreviousHotkeyScope();
|
|
}
|
|
},
|
|
[
|
|
goBackToPreviousHotkeyScope,
|
|
resetSelectedItem,
|
|
setCommands,
|
|
setIsCommandMenuOpened,
|
|
],
|
|
);
|
|
|
|
const toggleCommandMenu = useRecoilCallback(
|
|
({ snapshot, set }) =>
|
|
async () => {
|
|
const isCommandMenuOpened = snapshot
|
|
.getLoadable(isCommandMenuOpenedState)
|
|
.getValue();
|
|
|
|
set(commandMenuSearchState, '');
|
|
|
|
if (isCommandMenuOpened) {
|
|
closeCommandMenu();
|
|
} else {
|
|
openCommandMenu();
|
|
}
|
|
},
|
|
[closeCommandMenu, openCommandMenu],
|
|
);
|
|
|
|
const addToCommandMenu = useCallback(
|
|
(addCommand: Command[]) => {
|
|
setCommands((prev) => [...prev, ...addCommand]);
|
|
},
|
|
[setCommands],
|
|
);
|
|
|
|
const setObjectsInCommandMenu = (menuItems: ObjectMetadataItem[]) => {
|
|
const formattedItems = [
|
|
...[
|
|
...menuItems.map(
|
|
(item) =>
|
|
({
|
|
id: item.id,
|
|
to: `/objects/${item.namePlural}`,
|
|
label: `Go to ${item.labelPlural}`,
|
|
type: CommandType.Navigate,
|
|
firstHotKey: item.shortcut ? 'G' : undefined,
|
|
secondHotKey: item.shortcut,
|
|
Icon: ALL_ICONS[
|
|
(item?.icon as keyof typeof ALL_ICONS) ?? 'IconArrowUpRight'
|
|
],
|
|
}) as Command,
|
|
),
|
|
].sort(sortByProperty('label', 'asc')),
|
|
COMMAND_MENU_COMMANDS.settings,
|
|
];
|
|
setCommands(formattedItems);
|
|
};
|
|
|
|
const onItemClick = useCallback(
|
|
(onClick?: () => void, to?: string) => {
|
|
toggleCommandMenu();
|
|
|
|
if (isDefined(onClick)) {
|
|
onClick();
|
|
return;
|
|
}
|
|
if (isNonEmptyString(to)) {
|
|
navigate(to);
|
|
return;
|
|
}
|
|
},
|
|
[navigate, toggleCommandMenu],
|
|
);
|
|
|
|
return {
|
|
openCommandMenu,
|
|
closeCommandMenu,
|
|
toggleCommandMenu,
|
|
addToCommandMenu,
|
|
onItemClick,
|
|
setObjectsInCommandMenu,
|
|
};
|
|
};
|