Skip to content

Commit 1b0d2c8

Browse files
authored
Merge pull request #81 from ClayPulse/hotfix
hotfix when workflow first time installing apps, some apps are not in…
2 parents 8443a54 + 5a3eb89 commit 1b0d2c8

File tree

9 files changed

+132
-92
lines changed

9 files changed

+132
-92
lines changed

web/app/(extension-layout)/extension/page.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

3+
import { mfHost } from "@/components/providers/remote-module-provider";
34
import { getRemote } from "@/lib/module-federation/remote";
4-
import { loadRemote, registerRemotes } from "@module-federation/runtime";
55
import { useSearchParams } from "next/navigation";
66
import { useEffect, useState } from "react";
77

@@ -39,13 +39,12 @@ export default function ExtensionPage({}) {
3939
window.viewId = viewId;
4040

4141
if (!isRegistered) {
42-
registerRemotes(
43-
getRemote(remoteOrigin, moduleId, moduleVersion),
44-
);
42+
mfHost.registerRemotes(getRemote(remoteOrigin, moduleId, moduleVersion));
4543
setIsRegistered(true);
4644
}
4745

48-
loadRemote(`${moduleId}/main`)
46+
mfHost
47+
.loadRemote(`${moduleId}/main`)
4948
.then((module) => {
5049
console.log("Loaded remote module:", moduleId, module);
5150

web/components/app-loaders/sandbox-app-loader.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export default function SandboxAppLoader({
4747

4848
const clRef = useRef<ConnectionListener | null>(null);
4949
const [isConnected, setIsConnected] = useState<boolean>(false);
50+
const [isInitialized, setIsInitialized] = useState<boolean>(false);
5051

5152
const { resolvedTheme } = useTheme();
5253
const { platformApi } = usePlatformApi();
@@ -65,7 +66,7 @@ export default function SandboxAppLoader({
6566

6667
// Reset the connection when the view ID changes
6768
useEffect(() => {
68-
function getExtension(model: ViewModel) {
69+
function getExtension(model: ViewModel, ext: ExtensionApp) {
6970
// // File view type extension needs to match the file type.
7071
// // For example:
7172
// // .txt, .md, .js for code editor extension;
@@ -98,31 +99,34 @@ export default function SandboxAppLoader({
9899
// }
99100
// }
100101

101-
const extId = model.appConfig?.id;
102+
setCurrentExtension(ext);
103+
}
102104

105+
if (currentViewId && !isInitialized) {
103106
const ext = editorContext?.persistSettings?.extensions?.find(
104-
(extension) => extension.config.id === extId,
107+
(extension) => extension.config.id === viewModel.appConfig?.id,
105108
);
106109

107110
if (!ext) {
108-
throw new Error("Extension not found. Something went wrong.");
111+
return;
109112
}
110113

111-
setCurrentExtension(ext);
112-
}
113-
114-
if (currentViewId) {
115114
// Listen for an incoming extension connection
116115
console.log(`[${currentViewId}]: Listening for app connection...`);
116+
setIsInitialized(true);
117117
listenForExtensionConnection();
118118

119119
setIsLookingForExtension(true);
120120
setCurrentExtension(undefined);
121121
setIsMissingExtension(false);
122-
getExtension(viewModel);
122+
getExtension(viewModel, ext);
123123
setIsLookingForExtension(false);
124124
}
125-
}, [currentViewId]);
125+
}, [
126+
currentViewId,
127+
editorContext?.persistSettings?.extensions,
128+
isInitialized,
129+
]);
126130

127131
// Set is loading extension to true when current extension changes
128132
useEffect(() => {
@@ -308,7 +312,7 @@ export default function SandboxAppLoader({
308312
return (
309313
<div className="relative h-full w-full">
310314
{isLookingForExtension ? (
311-
<div className="bg-content1 absolute top-0 left-0 h-full w-full">
315+
<div className="bg-content1 h-full w-full">
312316
<Loading />
313317
</div>
314318
) : isMissingExtension ? (

web/components/interface/command-viewer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ export default function CommandViewer() {
154154
}
155155

156156
checkAndRunAction();
157-
}, [actionReadyToRun]);
157+
}, [actionReadyToRun, runActionCallback]);
158158

159159
function handleKeyDown(e: KeyboardEvent) {
160160
// Prevent default behavior for certain keys

web/components/marketplace/workflow/workflow-preview-card.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ export default function WorkflowPreviewCard({
3838
}
3939

4040
async function openWorkflow() {
41-
await createCanvasTabView({
42-
viewId: `canvas-${v4()}`,
43-
appConfigs: workflow.content.nodes.map((node) => node.data.config),
44-
initialWorkflowContent: workflow.content,
45-
});
41+
await createCanvasTabView(
42+
{
43+
viewId: `canvas-${v4()}`,
44+
appConfigs: workflow.content.nodes.map((node) => node.data.config),
45+
initialWorkflowContent: workflow.content,
46+
},
47+
false,
48+
);
4649

4750
editorContext?.setEditorStates((prev) => ({
4851
...prev,
@@ -121,7 +124,9 @@ export default function WorkflowPreviewCard({
121124
<Button
122125
className="text-medium h-12 sm:h-8 sm:text-sm"
123126
variant="light"
124-
onPress={(e) => {}}
127+
onPress={() => {
128+
openWorkflow();
129+
}}
125130
>
126131
<p className="w-full text-start">Use</p>
127132
</Button>

web/components/providers/remote-module-provider.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
"use client";
22

3-
import { init, registerRemotes } from "@module-federation/runtime";
4-
import React, { useContext, useEffect } from "react";
5-
import { ReactNode } from "react";
3+
import { getRemote } from "@/lib/module-federation/remote";
4+
import { ExtensionAgent, ExtensionApp } from "@/lib/types";
5+
import { createInstance } from "@module-federation/runtime";
6+
import React, { ReactNode, useContext, useEffect } from "react";
67
import ReactDOM from "react-dom";
78
import { EditorContext } from "./editor-context-provider";
8-
import { ExtensionApp, ExtensionAgent } from "@/lib/types";
9-
import { getRemote } from "@/lib/module-federation/remote";
109

11-
const host = init({
10+
export const mfHost = createInstance({
1211
name: "pulse_editor",
1312
remotes: [],
1413
shared: {
@@ -121,7 +120,7 @@ export default function RemoteModuleProvider({
121120
)
122121
.flat();
123122

124-
registerRemotes(remotes);
123+
mfHost.registerRemotes(remotes);
125124
console.log("Registered remotes", remotes);
126125

127126
// For each extension, load their agents

web/components/views/base/base-app-view.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,21 @@ export default function BaseAppView({
1515
config: AppViewConfig;
1616
viewId: string;
1717
}) {
18+
const editorContext = useContext(EditorContext);
1819
const imcContext = useContext(IMCContext);
1920

20-
const [noAccessToApp, setNoAccessToApp] = useState<boolean>(false);
2121
const {
2222
installExtension,
2323
loadAppFromCache,
2424
loadAppFromRegistry,
2525
loadAppFromURL,
2626
} = useExtensionManager();
27+
28+
const [noAccessToApp, setNoAccessToApp] = useState<boolean>(false);
2729
const [pulseAppViewModel, setPulseAppViewModel] = useState<
2830
ViewModel | undefined
2931
>(undefined);
32+
const [isOpened, setIsOpened] = useState<boolean>(false);
3033

3134
useEffect(() => {
3235
async function installAndOpenApp(ext: ExtensionApp) {
@@ -77,13 +80,19 @@ export default function BaseAppView({
7780
}
7881
}
7982

80-
openApp();
81-
}, [config]);
83+
if (!isOpened) {
84+
openApp().then(() => setIsOpened(true));
85+
}
86+
}, [config, installExtension, isOpened]);
8287

8388
return noAccessToApp ? (
84-
<NotAuthorized />
89+
<div className="bg-content1 h-full w-full">
90+
<NotAuthorized />
91+
</div>
8592
) : !pulseAppViewModel ? (
86-
<Loading text="Searching for app..." />
93+
<div className="bg-content1 h-full w-full">
94+
<Loading text="Searching for app..." />
95+
</div>
8796
) : (
8897
<SandboxAppLoader
8998
viewModel={pulseAppViewModel}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import PublishWorkflowModal from "@/components/modals/publish-workflow-modal";
2+
import { EditorContext } from "@/components/providers/editor-context-provider";
23
import { useRegisterMenuAction } from "@/lib/hooks/menu-actions/use-register-menu-action";
34
import { useAppInfo } from "@/lib/hooks/use-app-info";
45
import useCanvasWorkflow from "@/lib/hooks/use-canvas-workflow";
@@ -26,7 +27,7 @@ import {
2627
useViewport,
2728
} from "@xyflow/react";
2829
import "@xyflow/react/dist/style.css";
29-
import { useCallback, useEffect, useRef, useState } from "react";
30+
import { useCallback, useContext, useEffect, useRef, useState } from "react";
3031
import Icon from "../../misc/icon";
3132
import AppNode from "./nodes/app-node/app-node";
3233
import "./theme.css";
@@ -58,6 +59,8 @@ export default function CanvasView({
5859
isActive: boolean;
5960
tabName: string;
6061
}) {
62+
const editorContext = useContext(EditorContext);
63+
6164
const { openAppInfoModal } = useAppInfo();
6265

6366
const {
@@ -134,7 +137,7 @@ export default function CanvasView({
134137
async () => {
135138
await startWorkflow();
136139
},
137-
[entryPoint, isActive, tabName],
140+
[entryPoint, isActive, tabName, editorContext?.persistSettings?.extensions],
138141
isActive,
139142
);
140143
// Publish workflow

web/lib/hooks/use-extension-manager.ts

Lines changed: 66 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,93 @@
11
import { EditorContext } from "@/components/providers/editor-context-provider";
2+
import { mfHost } from "@/components/providers/remote-module-provider";
23
import { fetchAPI, getAPIUrl } from "@/lib/pulse-editor-website/backend";
3-
import { registerRemotes } from "@module-federation/runtime";
4-
import { useContext } from "react";
4+
import { useCallback, useContext } from "react";
55
import toast from "react-hot-toast";
66
import { compare } from "semver";
77
import { getRemote, getRemoteClientBaseURL } from "../module-federation/remote";
88
import {
9-
getRemoteLibVersion,
109
getHostMFVersion,
10+
getRemoteLibVersion,
1111
getRemoteMFVersion,
1212
} from "../module-federation/version";
1313
import { AppMetaData, ExtensionApp } from "../types";
1414

1515
export default function useExtensionManager() {
1616
const editorContext = useContext(EditorContext);
1717

18-
async function installExtension(
19-
remoteOrigin: string,
20-
id: string,
21-
version: string,
22-
): Promise<void> {
23-
const extension = await getExtensionInfoFromRemote(
24-
remoteOrigin,
25-
id,
26-
version,
27-
);
18+
const installExtension = useCallback(
19+
async (
20+
remoteOrigin: string,
21+
id: string,
22+
version: string,
23+
): Promise<void> => {
24+
// Don't install if already installed
25+
if (
26+
editorContext?.persistSettings?.extensions?.find(
27+
(ext) =>
28+
ext.config.id === id &&
29+
ext.config.version === version &&
30+
ext.remoteOrigin === remoteOrigin,
31+
)
32+
) {
33+
console.log(`Extension ${id} is already installed`);
34+
return;
35+
}
2836

29-
console.log("Installing remote", extension);
37+
const extension = await getExtensionInfoFromRemote(
38+
remoteOrigin,
39+
id,
40+
version,
41+
);
3042

31-
const remoteMFVersion = extension.mfVersion;
43+
console.log("Installing remote", extension);
3244

33-
const hostMFVersion = await getHostMFVersion();
45+
const remoteMFVersion = extension.mfVersion;
3446

35-
if (!remoteMFVersion) {
36-
throw new Error("Remote MF version is undefined");
37-
} else if (compare(remoteMFVersion, hostMFVersion) !== 0) {
38-
throw new Error(
39-
`Extension MF version ${remoteMFVersion} is not compatible with host MF version ${hostMFVersion}`,
40-
);
41-
}
47+
const hostMFVersion = await getHostMFVersion();
4248

43-
// TODO: Prevent CSS from being injected from the remote
44-
// Register the frontend and backend from remote
45-
registerRemotes(
46-
getRemote(
47-
extension.remoteOrigin,
48-
extension.config.id,
49-
extension.config.version,
50-
),
51-
);
49+
if (!remoteMFVersion) {
50+
throw new Error("Remote MF version is undefined");
51+
} else if (compare(remoteMFVersion, hostMFVersion) !== 0) {
52+
throw new Error(
53+
`Extension MF version ${remoteMFVersion} is not compatible with host MF version ${hostMFVersion}`,
54+
);
55+
}
5256

53-
const installedExtensions =
54-
(await editorContext?.persistSettings?.extensions) ?? [];
57+
// TODO: Prevent CSS from being injected from the remote
58+
// Register the frontend and backend from remote
59+
mfHost.registerRemotes(
60+
getRemote(
61+
extension.remoteOrigin,
62+
extension.config.id,
63+
extension.config.version,
64+
),
65+
);
5566

56-
// Check if extension is already installed
57-
if (
58-
installedExtensions.find((ext) => ext.config.id === extension.config.id)
59-
) {
60-
return;
61-
}
67+
const installedExtensions =
68+
(await editorContext?.persistSettings?.extensions) ?? [];
6269

63-
const updatedExtensions = [...installedExtensions, extension];
70+
// Check if extension is already installed
71+
if (
72+
installedExtensions.find((ext) => ext.config.id === extension.config.id)
73+
) {
74+
return;
75+
}
6476

65-
editorContext?.setPersistSettings((prev) => {
66-
return {
67-
...prev,
68-
extensions: updatedExtensions,
69-
};
70-
});
77+
const updatedExtensions = [...installedExtensions, extension];
7178

72-
// Try to set default extension for file types
73-
tryAutoSetDefault(extension);
74-
}
79+
editorContext?.setPersistSettings((prev) => {
80+
return {
81+
...prev,
82+
extensions: updatedExtensions,
83+
};
84+
});
85+
86+
// Try to set default extension for file types
87+
tryAutoSetDefault(extension);
88+
},
89+
[editorContext?.persistSettings?.extensions],
90+
);
7591

7692
async function uninstallExtension(name: string): Promise<void> {
7793
const extensions = (await editorContext?.persistSettings?.extensions) ?? [];

0 commit comments

Comments
 (0)