Skip to content

Commit 34eb0bb

Browse files
committed
Add app object in action args
1 parent dffd5f6 commit 34eb0bb

File tree

9 files changed

+376
-250
lines changed

9 files changed

+376
-250
lines changed

npm-packages/shared-utils/src/types/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ export type TypedVariableType =
208208
| "number"
209209
| "boolean"
210210
| "any"
211+
// An app instance is a reference to another app.
212+
// This instance could be possessed by the owner,
213+
// or it can be initialized by the caller.
214+
| "app-instance"
211215
| TypedVariableObjectType
212216
| TypedVariableArrayType;
213217

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

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import BaseAppLoader from "@/components/app-loaders/base-app-loader";
22
import Loading from "@/components/interface/status-screens/loading";
33
import { EditorContext } from "@/components/providers/editor-context-provider";
44
import { IMCContext } from "@/components/providers/imc-provider";
5-
import { PlatformEnum } from "@/lib/enums";
5+
import { DragEventTypeEnum, PlatformEnum } from "@/lib/enums";
66
import { usePlatformApi } from "@/lib/hooks/use-platform-api";
77
import { getPlatform } from "@/lib/platform-api/platform-checker";
8-
import { ExtensionApp } from "@/lib/types";
8+
import { ExtensionApp, FileDragData } from "@/lib/types";
9+
import { addToast } from "@heroui/react";
910
import {
1011
AppTypeEnum,
1112
ConnectionListener,
@@ -310,7 +311,56 @@ export default function SandboxAppLoader({
310311
}
311312

312313
return (
313-
<div className="relative h-full w-full">
314+
<div
315+
className="relative h-full w-full"
316+
onDragOver={(e) => {
317+
e.stopPropagation();
318+
const types = e.dataTransfer.types;
319+
if (
320+
types.includes(`application/${DragEventTypeEnum.File.toLowerCase()}`)
321+
) {
322+
e.preventDefault(); // allow drop
323+
e.dataTransfer.dropEffect = "move";
324+
} else {
325+
e.dataTransfer.dropEffect = "none";
326+
}
327+
}}
328+
onDrop={async (e) => {
329+
const dataText = e.dataTransfer.getData(
330+
`application/${DragEventTypeEnum.File.toLowerCase()}`,
331+
);
332+
if (!dataText) {
333+
return;
334+
}
335+
console.log("Dropped item:", dataText);
336+
try {
337+
const data = JSON.parse(dataText) as FileDragData;
338+
339+
e.preventDefault();
340+
const uri = data.uri;
341+
342+
// Send uri to app view
343+
await imcContext?.polyIMC?.sendMessage(
344+
viewModel.viewId,
345+
IMCMessageTypeEnum.EditorAppReceiveFileUri,
346+
{
347+
uri,
348+
},
349+
);
350+
} catch (error) {
351+
addToast({
352+
title: "Failed to open file",
353+
description: "The dropped file data is invalid.",
354+
color: "danger",
355+
});
356+
} finally {
357+
editorContext?.setEditorStates((prev) => ({
358+
...prev,
359+
isDraggingOverCanvas: false,
360+
}));
361+
}
362+
}}
363+
>
314364
{isLookingForExtension ? (
315365
<div className="bg-content1 h-full w-full">
316366
<Loading />
@@ -324,7 +374,12 @@ export default function SandboxAppLoader({
324374
</p>
325375
</div>
326376
) : (
327-
<div className="relative h-full w-full">
377+
<div
378+
className="relative h-full w-full data-[is-dragging-file=true]:pointer-events-none"
379+
data-is-dragging-file={
380+
editorContext?.editorStates.isDraggingOverCanvas ? "true" : "false"
381+
}
382+
>
328383
{isLoadingExtension && (
329384
<div className="bg-content1 absolute top-0 left-0 h-full w-full">
330385
<Loading />

web/components/interface/status-screens/loading.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { Progress } from "@heroui/react";
22

33
export default function Loading({ text = "Loading..." }: { text?: string }) {
44
return (
5-
<div className="flex h-full w-full flex-col items-center justify-center dark:bg-[#27272A]">
5+
<div className="flex h-full w-full flex-col items-center justify-center">
66
<Progress
77
isIndeterminate={true}
8-
className="w-1/2 text-black dark:text-white"
8+
className="w-1/2 text-default-foreground"
99
color="default"
1010
size="md"
1111
label={text}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Loading from "@/components/interface/status-screens/loading";
22
import NotAuthorized from "@/components/interface/status-screens/not-authorized";
3-
import { EditorContext } from "@/components/providers/editor-context-provider";
43
import { IMCContext } from "@/components/providers/imc-provider";
54
import useExtensionManager from "@/lib/hooks/use-extension-manager";
65
import { AppViewConfig, ExtensionApp } from "@/lib/types";
@@ -15,7 +14,6 @@ export default function BaseAppView({
1514
config: AppViewConfig;
1615
viewId: string;
1716
}) {
18-
const editorContext = useContext(EditorContext);
1917
const imcContext = useContext(IMCContext);
2018

2119
const {
@@ -86,7 +84,7 @@ export default function BaseAppView({
8684
}, [config, installExtension, isOpened]);
8785

8886
return noAccessToApp ? (
89-
<div className="bg-content1 h-full w-full">
87+
<div className="bg-content3 h-full w-full">
9088
<NotAuthorized />
9189
</div>
9290
) : !pulseAppViewModel ? (

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

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import PublishWorkflowModal from "@/components/modals/publish-workflow-modal";
22
import { EditorContext } from "@/components/providers/editor-context-provider";
3+
import { DragEventTypeEnum } from "@/lib/enums";
34
import { useRegisterMenuAction } from "@/lib/hooks/menu-actions/use-register-menu-action";
45
import { useAppInfo } from "@/lib/hooks/use-app-info";
56
import useCanvasWorkflow from "@/lib/hooks/use-canvas-workflow";
67
import { useTabViewManager } from "@/lib/hooks/use-tab-view-manager";
78
import {
9+
AppDragData,
810
AppInfoModalContent,
911
AppNodeData,
1012
AppViewConfig,
1113
CanvasViewConfig,
14+
ExtensionApp,
1215
} from "@/lib/types";
13-
import { Button } from "@heroui/react";
16+
import { addToast, Button } from "@heroui/react";
1417
import {
1518
addEdge,
1619
applyEdgeChanges,
@@ -28,7 +31,16 @@ import {
2831
useViewport,
2932
} from "@xyflow/react";
3033
import "@xyflow/react/dist/style.css";
31-
import { useCallback, useContext, useEffect, useRef, useState } from "react";
34+
import { useTheme } from "next-themes";
35+
import {
36+
memo,
37+
useCallback,
38+
useContext,
39+
useEffect,
40+
useRef,
41+
useState,
42+
} from "react";
43+
import { v4 } from "uuid";
3244
import Icon from "../../misc/icon";
3345
import AppNode from "./nodes/app-node/app-node";
3446
import "./theme.css";
@@ -63,7 +75,7 @@ export default function CanvasView({
6375
const editorContext = useContext(EditorContext);
6476

6577
const { openAppInfoModal } = useAppInfo();
66-
78+
const { resolvedTheme } = useTheme();
6779
const {
6880
localEdges,
6981
localNodes,
@@ -76,7 +88,8 @@ export default function CanvasView({
7688
} = useCanvasWorkflow(config.initialWorkflowContent);
7789
const viewport = useViewport();
7890
const { screenToFlowPosition } = useReactFlow();
79-
const { deleteAppViewInCanvasView } = useTabViewManager();
91+
const { deleteAppViewInCanvasView, createAppViewInCanvasView } =
92+
useTabViewManager();
8093

8194
const [isPublishModalOpen, setIsPublishModalOpen] = useState(false);
8295

@@ -270,6 +283,50 @@ export default function CanvasView({
270283
ref={containerRef}
271284
className="bg-content3 text-content3-foreground relative h-full w-full"
272285
id={config.viewId}
286+
onDragOver={(e) => {
287+
const types = e.dataTransfer.types;
288+
if (
289+
types.includes(`application/${DragEventTypeEnum.App.toLowerCase()}`)
290+
) {
291+
e.preventDefault(); // allow drop
292+
e.dataTransfer.dropEffect = "copy";
293+
} else {
294+
e.dataTransfer.dropEffect = "none";
295+
}
296+
}}
297+
onDrop={(e) => {
298+
const dataText = e.dataTransfer.getData(
299+
`application/${DragEventTypeEnum.App.toLowerCase()}`,
300+
);
301+
if (!dataText) {
302+
return;
303+
}
304+
console.log("Dropped item:", dataText);
305+
try {
306+
const data = JSON.parse(dataText) as AppDragData;
307+
e.preventDefault();
308+
309+
const app: ExtensionApp = data.app;
310+
const config: AppViewConfig = {
311+
app: app.config.id,
312+
viewId: `${app.config.id}-${v4()}`,
313+
recommendedHeight: app.config.recommendedHeight,
314+
recommendedWidth: app.config.recommendedWidth,
315+
};
316+
createAppViewInCanvasView(config);
317+
} catch (error) {
318+
addToast({
319+
title: "Failed to open app",
320+
description: "The dropped app data is invalid.",
321+
color: "danger",
322+
});
323+
} finally {
324+
editorContext?.setEditorStates((prev) => ({
325+
...prev,
326+
isDraggingOverCanvas: false,
327+
}));
328+
}
329+
}}
273330
>
274331
<ReactFlow
275332
nodes={localNodes ?? []}
@@ -297,6 +354,7 @@ export default function CanvasView({
297354
}}
298355
maxZoom={4}
299356
minZoom={0.1}
357+
colorMode={resolvedTheme === "dark" ? "dark" : "light"}
300358
>
301359
<Background id={config.viewId} variant={BackgroundVariant.Dots} />
302360
</ReactFlow>
@@ -323,3 +381,16 @@ export default function CanvasView({
323381
</div>
324382
);
325383
}
384+
385+
export const MemoizedCanvasView = memo(
386+
({
387+
config,
388+
isActive,
389+
tabName,
390+
}: {
391+
config: CanvasViewConfig;
392+
isActive: boolean;
393+
tabName: string;
394+
}) => <CanvasView config={config} isActive={isActive} tabName={tabName} />,
395+
);
396+
MemoizedCanvasView.displayName = "MemoizedCanvasView";

0 commit comments

Comments
 (0)