Skip to content

Commit 3afe3e4

Browse files
committed
Simplify action register hooks
1 parent 00f3e60 commit 3afe3e4

File tree

8 files changed

+232
-201
lines changed

8 files changed

+232
-201
lines changed

npm-packages/react-api/src/hooks/editor/use-register-action.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
ReceiverHandler,
66
TypedVariable,
77
} from "@pulse-editor/shared-utils";
8-
import { useEffect, useRef, useState } from "react";
8+
import { DependencyList, useEffect, useRef, useState } from "react";
99
import useIMC from "../../lib/use-imc";
1010

1111
/**
@@ -17,7 +17,8 @@ import useIMC from "../../lib/use-imc";
1717
* @param parameters Parameters of the command.
1818
* @param returns Return values of the command.
1919
* @param callbackHandler Callback handler function to handle the command.
20-
* @param isExtReady Whether the extension is ready to receive commands.
20+
* @param deps Dependency list to re-register the action when changed.
21+
* @param isExtReady Whether the extension is ready to receive commands.
2122
* Useful for actions that need to wait for some certain app state to be ready.
2223
*
2324
*/
@@ -28,7 +29,8 @@ export default function useRegisterAction(
2829
parameters?: Record<string, TypedVariable>;
2930
returns?: Record<string, TypedVariable>;
3031
},
31-
callbackHandler?: (args: any) => Promise<string | void>,
32+
callbackHandler: (args: any) => Promise<string | void>,
33+
deps: DependencyList,
3234
isExtReady: boolean = true
3335
) {
3436
const { isReady, imc } = useIMC(getReceiverHandlerMap());
@@ -85,8 +87,9 @@ export default function useRegisterAction(
8587
description: actionInfo.description,
8688
parameters: actionInfo.parameters ?? {},
8789
returns: actionInfo.returns ?? {},
90+
handler: callbackHandler,
8891
}));
89-
}, [callbackHandler]);
92+
}, [...deps]);
9093

9194
async function executeAction(args: any) {
9295
if (!action.handler) return;
Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,51 @@
11
import { useMenuActions } from "@/lib/hooks/use-menu-actions";
2-
import NavMenuDropdown from "../nav-menu-dropdown";
3-
import { MenuAction } from "@/lib/types";
4-
import { useEffect } from "react";
2+
import { useRegisterMenuAction } from "@/lib/hooks/use-register-menu-action";
53
import { useTabViewManager } from "@/lib/hooks/use-tab-view-manager";
64
import { ViewModeEnum } from "@pulse-editor/shared-utils";
5+
import { useEffect, useState } from "react";
76
import { v4 } from "uuid";
7+
import NavMenuDropdown from "../nav-menu-dropdown";
88

99
export default function FileMenuDropDown() {
10-
const { menuActions, registerMenuAction } = useMenuActions("file");
11-
const { createTabView } = useTabViewManager();
10+
const { menuActions } = useMenuActions("file");
11+
const { createTabView, activeTabView, closeTabView } = useTabViewManager();
1212

13-
const defaultMenuActions: MenuAction[] = [
13+
useRegisterMenuAction(
1414
{
1515
name: "New Workflow",
16-
actionFunc: async () => {
17-
// Trigger new Workflow creation logic
18-
await createTabView(ViewModeEnum.Canvas, { viewId: v4() });
19-
},
2016
menuCategory: "file",
2117
shortcut: "Ctrl+N",
2218
icon: "add",
2319
description: "Create a new Workflow",
2420
},
25-
];
21+
async () => {
22+
// Trigger new Workflow creation logic
23+
await createTabView(ViewModeEnum.Canvas, { viewId: v4() });
24+
},
25+
[],
26+
);
27+
28+
const [isCloseWorkflowEnabled, setIsCloseWorkflowEnabled] = useState(false);
29+
useRegisterMenuAction(
30+
{
31+
name: "Close Workflow",
32+
menuCategory: "file",
33+
shortcut: "Ctrl+C",
34+
icon: "close",
35+
description: "Close the current workflow",
36+
},
37+
async () => {
38+
if (activeTabView) {
39+
closeTabView(activeTabView);
40+
}
41+
},
42+
[activeTabView],
43+
isCloseWorkflowEnabled,
44+
);
2645

27-
// Register default menu actions if not already registered
2846
useEffect(() => {
29-
defaultMenuActions.forEach((action) => {
30-
registerMenuAction(action);
31-
});
32-
}, []);
47+
setIsCloseWorkflowEnabled(activeTabView !== undefined);
48+
}, [activeTabView]);
3349

3450
return <NavMenuDropdown category="File" menuActions={menuActions} />;
3551
}

web/components/interface/navigation/menu-dropdown/view-menu.tsx

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,57 @@
1+
import { EditorContext } from "@/components/providers/editor-context-provider";
12
import { useMenuActions } from "@/lib/hooks/use-menu-actions";
3+
import { useRegisterMenuAction } from "@/lib/hooks/use-register-menu-action";
4+
import { useContext, useEffect, useState } from "react";
25
import NavMenuDropdown from "../nav-menu-dropdown";
3-
import { MenuAction } from "@/lib/types";
4-
import { useContext, useEffect } from "react";
5-
import { EditorContext } from "@/components/providers/editor-context-provider";
66

77
export default function ViewMenuDropDown() {
88
const editorContext = useContext(EditorContext);
9-
const { menuActions, registerMenuAction, unregisterMenuAction } =
10-
useMenuActions("view");
9+
const { menuActions } = useMenuActions("view");
1110

12-
const defaultMenuActions: MenuAction[] = [];
13-
14-
// Register default menu actions if not already registered
15-
useEffect(() => {
16-
console.log("Registering default menu actions");
17-
defaultMenuActions.forEach((action) => {
18-
registerMenuAction(action);
19-
});
20-
}, []);
11+
const [isCommandViewerOpen, setIsCommandViewerOpen] = useState(false);
2112

22-
useEffect(() => {
23-
const closeAction: MenuAction = {
13+
useRegisterMenuAction(
14+
{
2415
name: "Close Command Viewer",
25-
actionFunc: async () => {
26-
console.log("Closing command viewer");
27-
editorContext?.setEditorStates((prev) => ({
28-
...prev,
29-
isCommandViewerOpen: false,
30-
}));
31-
},
3216
menuCategory: "view",
3317
shortcut: "F1",
3418
icon: "terminal",
3519
description: "Close the command viewer",
36-
};
20+
},
21+
async () => {
22+
console.log("Closing command viewer");
23+
editorContext?.setEditorStates((prev) => ({
24+
...prev,
25+
isCommandViewerOpen: false,
26+
}));
27+
},
28+
[],
29+
isCommandViewerOpen,
30+
);
3731

38-
const openAction: MenuAction = {
32+
useRegisterMenuAction(
33+
{
3934
name: "View Command Viewer",
40-
actionFunc: async () => {
41-
console.log("Opening command viewer");
42-
editorContext?.setEditorStates((prev) => ({
43-
...prev,
44-
isCommandViewerOpen: true,
45-
}));
46-
},
4735
menuCategory: "view",
4836
shortcut: "F1",
4937
icon: "terminal",
5038
description: "View all commands and shortcuts",
51-
};
52-
if (editorContext?.editorStates.isCommandViewerOpen) {
53-
// Register the close action and unregister the open action
54-
unregisterMenuAction(openAction);
55-
registerMenuAction(closeAction);
56-
} else {
57-
// Register the open action and unregister the close action
58-
unregisterMenuAction(closeAction);
59-
registerMenuAction(openAction);
60-
}
39+
},
40+
async () => {
41+
console.log("Opening command viewer");
42+
editorContext?.setEditorStates((prev) => ({
43+
...prev,
44+
isCommandViewerOpen: true,
45+
}));
46+
},
47+
[],
48+
!isCommandViewerOpen,
49+
);
50+
51+
useEffect(() => {
52+
setIsCommandViewerOpen(
53+
editorContext?.editorStates.isCommandViewerOpen ?? false,
54+
);
6155
}, [editorContext?.editorStates.isCommandViewerOpen]);
6256

6357
return <NavMenuDropdown category="View" menuActions={menuActions} />;

web/components/views/canvas/canvas-view.tsx

Lines changed: 55 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useAppInfo } from "@/lib/hooks/use-app-info";
2-
import { useMenuActions } from "@/lib/hooks/use-menu-actions";
2+
import { useRegisterMenuAction } from "@/lib/hooks/use-register-menu-action";
33
import useWorkflow from "@/lib/hooks/use-workflow";
44
import {
55
AppInfoModalContent,
@@ -53,7 +53,6 @@ Pulse Editor is a modular, cross-platform, AI-powered creativity platform with f
5353

5454
export default function CanvasView({ config }: { config?: CanvasViewConfig }) {
5555
const { openAppInfoModal } = useAppInfo();
56-
const { registerMenuAction, unregisterMenuAction } = useMenuActions();
5756

5857
const [workflow, setWorkflow] = useState<Workflow | undefined>(undefined);
5958
const [entryPoint, setEntryPoint] = useState<
@@ -123,79 +122,68 @@ export default function CanvasView({ config }: { config?: CanvasViewConfig }) {
123122
}, []);
124123

125124
// Register menu actions
126-
useEffect(() => {
127-
console.log("CanvasView rendered: registering menu actions");
128-
129-
const menuActions: MenuAction[] = [
130-
{
131-
name: "Export Workflow",
132-
menuCategory: "file",
133-
description: "Export the current workflow as a JSON file",
134-
shortcut: "Ctrl+Alt+E",
135-
actionFunc: async () => {
136-
await exportWorkflow();
137-
},
138-
icon: "download",
139-
},
140-
{
141-
name: "Import Workflow",
142-
menuCategory: "file",
143-
description: "Import a workflow from a JSON file",
144-
shortcut: "Ctrl+Alt+I",
145-
actionFunc: async () => {
146-
const input = document.createElement("input");
147-
input.type = "file";
148-
input.accept = "application/json";
149-
input.onchange = (e: any) => {
150-
const file = e.target.files[0];
151-
const reader = new FileReader();
152-
reader.onload = (event) => {
153-
try {
154-
const workflow = JSON.parse(event.target?.result as string);
155-
if (workflow) {
156-
setWorkflow(workflow);
157-
} else {
158-
alert("Invalid workflow file");
159-
}
160-
} catch (err) {
161-
alert("Error reading workflow file");
162-
}
163-
};
164-
reader.readAsText(file);
165-
};
166-
input.click();
167-
},
168-
icon: "upload",
169-
},
170-
];
171-
172-
menuActions.forEach((action) => {
173-
registerMenuAction(action);
174-
});
125+
useRegisterMenuAction(
126+
{
127+
name: "Export Workflow",
128+
menuCategory: "file",
129+
description: "Export the current workflow as a JSON file",
130+
shortcut: "Ctrl+Alt+E",
175131

176-
return () => {
177-
// Unregister menu actions on unmount
178-
menuActions.forEach((action) => {
179-
unregisterMenuAction(action);
180-
});
181-
};
182-
}, []);
132+
icon: "download",
133+
},
183134

184-
useEffect(() => {
185-
const action: MenuAction = {
135+
async () => {
136+
await exportWorkflow();
137+
},
138+
[workflow],
139+
);
140+
useRegisterMenuAction(
141+
{
142+
name: "Import Workflow",
143+
menuCategory: "file",
144+
description: "Import a workflow from a JSON file",
145+
shortcut: "Ctrl+Alt+I",
146+
icon: "upload",
147+
},
148+
async () => {
149+
const input = document.createElement("input");
150+
input.type = "file";
151+
input.accept = "application/json";
152+
input.onchange = (e: any) => {
153+
const file = e.target.files[0];
154+
const reader = new FileReader();
155+
reader.onload = (event) => {
156+
try {
157+
const workflow = JSON.parse(event.target?.result as string);
158+
if (workflow) {
159+
setWorkflow(workflow);
160+
} else {
161+
alert("Invalid workflow file");
162+
}
163+
} catch (err) {
164+
alert("Error reading workflow file");
165+
}
166+
};
167+
reader.readAsText(file);
168+
};
169+
input.click();
170+
},
171+
[],
172+
);
173+
useRegisterMenuAction(
174+
{
186175
name: "Run Workflow",
187176
menuCategory: "view",
188177
description:
189178
"Run the current workflow from the selected or default entry point",
190179
shortcut: "Ctrl+Alt+R",
191-
actionFunc: async () => {
192-
await startWorkflow();
193-
},
194180
icon: "play_arrow",
195-
};
196-
197-
registerMenuAction(action, true);
198-
}, [entryPoint]);
181+
},
182+
async () => {
183+
await startWorkflow();
184+
},
185+
[entryPoint],
186+
);
199187

200188
// Add or remove nodes when config changes
201189
useEffect(() => {

web/components/views/canvas/nodes/backend-node/backend-node.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import CanvasNodeViewLayout from "../app-node/layout";
1212
const BackendNode = memo((props: any) => {
1313
const nodeProps = props as Node<AppNodeData>;
1414

15-
const { config, selectedAction, setSelectedAction }: AppNodeData =
15+
const { config, selectedAction, setSelectedAction, isRunning }: AppNodeData =
1616
nodeProps.data;
1717
const viewId = config.viewId;
1818

@@ -40,6 +40,7 @@ const BackendNode = memo((props: any) => {
4040
deleteAppViewInCanvasView(viewId);
4141
},
4242
}}
43+
isRunning={isRunning}
4344
>
4445
<BaseAppView viewId={viewId} config={config} />
4546
</CanvasNodeViewLayout>

0 commit comments

Comments
 (0)