abdab2fb7e
## Description - Introduces a new engine command execution model that replaces the previous approach of mapping `EngineComponentKey` to React components. Instead, engine commands are now mounted headlessly via `HeadlessEngineCommandMountRoot`, with their execution context populated synchronously before mounting. - Creates new headless command components - Moves error handling from the SDK layer to the host app by wrapping all mounted commands with a new `CommandMenuItemErrorBoundary` The new flow works as follows: - When a command menu item with an `engineComponentKey` is clicked, `useCommandMenuItemFrontComponentCommands` calls `useMountEngineCommand`, which synchronously reads the current context store (object metadata, selected records, filters, view ID, etc.) and writes a `MountedEngineCommandContext` into `mountedEngineCommandsState`. - The command is then mounted into `mountedEngineCommandsState`, which triggers `HeadlessEngineCommandMountRoot` to render the corresponding headless component from `ENGINE_COMPONENT_KEY_HEADLESS_COMPONENT_MAP`, wrapped in `CommandMenuItemErrorBoundary`, `ContextStoreComponentInstanceContext.Provider`, and `EngineCommandComponentInstanceContext.Provider`. - Each command component reads its execution context and delegates to one of the 4 execution patterns: `HeadlessEngineCommandWrapperEffect` (simple actions), `HeadlessConfirmationModalEngineCommandEffect` (destructive actions needing confirmation), `HeadlessNavigateEngineCommand` (GO_TO_* commands), or `HeadlessOpenSidePanelPageEngineCommand` (SEARCH_RECORDS, ASK_AI, VIEW_PREVIOUS_AI_CHATS). - After execution, the command self-unmounts via `useUnmountEngineCommand`, which removes the entry from `mountedEngineCommandsState` and stops rendering the component.
38 lines
1.2 KiB
TypeScript
38 lines
1.2 KiB
TypeScript
import { HeadlessEngineCommandWrapperEffect } from '@/command-menu-item/engine-command/components/HeadlessEngineCommandWrapperEffect';
|
|
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
|
|
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
|
|
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
|
import { type MessageDescriptor } from '@lingui/core';
|
|
import { t } from '@lingui/core/macro';
|
|
import { type SidePanelPages } from 'twenty-shared/types';
|
|
import { type IconComponent } from 'twenty-ui/display';
|
|
|
|
export const HeadlessOpenSidePanelPageEngineCommand = ({
|
|
page,
|
|
pageTitle,
|
|
pageIcon,
|
|
shouldResetSearchState = false,
|
|
}: {
|
|
page: SidePanelPages;
|
|
pageTitle: MessageDescriptor;
|
|
pageIcon: IconComponent;
|
|
shouldResetSearchState?: boolean;
|
|
}) => {
|
|
const { navigateSidePanel } = useNavigateSidePanel();
|
|
const setSidePanelSearch = useSetAtomState(sidePanelSearchState);
|
|
|
|
const onExecute = () => {
|
|
navigateSidePanel({
|
|
page,
|
|
pageTitle: t(pageTitle),
|
|
pageIcon,
|
|
});
|
|
|
|
if (shouldResetSearchState) {
|
|
setSidePanelSearch('');
|
|
}
|
|
};
|
|
|
|
return <HeadlessEngineCommandWrapperEffect execute={onExecute} />;
|
|
};
|