[Command menu items] Create engine commands (#18681)
## 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.
This commit is contained in:
+7
-3
@@ -1,6 +1,7 @@
|
||||
import { ThreadWebWorker, release, retain } from '@quilted/threads';
|
||||
import { RemoteReceiver } from '@remote-dom/core/receivers';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { type ConfirmationModalCaller } from 'twenty-shared/types';
|
||||
import { type CommandConfirmationModalResult } from '../../../sdk/front-component-api/globals/frontComponentHostCommunicationApi';
|
||||
import { type FrontComponentHostCommunicationApi } from '../../types/FrontComponentHostCommunicationApi';
|
||||
import { type WorkerExports } from '../../types/WorkerExports';
|
||||
@@ -11,7 +12,7 @@ const COMMAND_MENU_ITEM_CONFIRMATION_MODAL_RESULT_BROWSER_EVENT_NAME =
|
||||
'command-menu-item-confirmation-modal-result';
|
||||
|
||||
type CommandMenuItemConfirmationModalResultBrowserEventDetail = {
|
||||
frontComponentId: string;
|
||||
caller: ConfirmationModalCaller;
|
||||
confirmationResult: CommandConfirmationModalResult;
|
||||
};
|
||||
|
||||
@@ -73,9 +74,12 @@ export const FrontComponentWorkerEffect = ({
|
||||
const commandMenuItemConfirmationModalResultBrowserEventDetail =
|
||||
event.detail;
|
||||
|
||||
const caller =
|
||||
commandMenuItemConfirmationModalResultBrowserEventDetail.caller;
|
||||
|
||||
if (
|
||||
commandMenuItemConfirmationModalResultBrowserEventDetail.frontComponentId !==
|
||||
frontComponentId
|
||||
caller.type !== 'frontComponent' ||
|
||||
caller.frontComponentId !== frontComponentId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user