Skip to content

Commit

Permalink
start useFlowchartGraph rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
39bytes committed Feb 27, 2024
1 parent 9d06979 commit 3d5ae73
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 24 deletions.
6 changes: 3 additions & 3 deletions src/renderer/hooks/useCustomBlockManifest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { captain } from "@/renderer/lib/ky";
import { BlocksMetadataMap } from "@/renderer/types/blocks-metadata";
import { BlockMetadataMap } from "@/renderer/types/blocks-metadata";
import { RootNode, validateRootSchema } from "@/renderer/utils/ManifestLoader";
import { atom, useAtom, useSetAtom } from "jotai";
import { useCallback } from "react";
Expand All @@ -8,7 +8,7 @@ import { manifestChangedAtom } from "./useManifest";

// undefined = loading state
const customBlockManifestAtom = atom<RootNode | undefined | null>(null);
const customBlocksMetadataMapAtom = atom<BlocksMetadataMap | undefined | null>(
const customBlocksMetadataMapAtom = atom<BlockMetadataMap | undefined | null>(
null,
);

Expand Down Expand Up @@ -54,7 +54,7 @@ export const useCustomSections = () => {
},
})
.json();
setCustomBlocksMetadata(res2 as BlocksMetadataMap);
setCustomBlocksMetadata(res2 as BlockMetadataMap);
setManifestChanged(true);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
Expand Down
12 changes: 0 additions & 12 deletions src/renderer/hooks/useFlowChartGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,6 @@ export const useFlowChartGraph = () => {
setNodes(updatedNodes);
};

const removeCtrlInputDataForNode = (nodeId: string, paramId: string) => {
setNodes((nodes) => {
const node = nodes.find((e) => e.id === nodeId);
if (node) {
node.data.ctrls = node.data.ctrls || {};
delete node.data.ctrls[paramId];
}
});
sendEventToMix("Control Input Data Removed", { nodeId, paramId });
};

const handleNodeChanges = (
cb: (nodes: Node<BlockData>[]) => Node<BlockData>[],
) => {
Expand All @@ -165,7 +154,6 @@ export const useFlowChartGraph = () => {
selectedNode,
unSelectedNodes,
updateCtrlInputDataForNode,
removeCtrlInputDataForNode,
updateInitCtrlInputDataForNode,
loadFlowExportObject,
handleTitleChange,
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/hooks/useManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
getManifest,
getBlocksMetadata,
} from "@/renderer/services/FlowChartServices";
import { BlocksMetadataMap } from "@/renderer/types/blocks-metadata";
import { BlockMetadataMap } from "@/renderer/types/blocks-metadata";
import { RootNode } from "@/renderer/utils/ManifestLoader";
import { atom, useAtomValue, useSetAtom } from "jotai";
import { useCallback, useMemo } from "react";
Expand All @@ -24,7 +24,7 @@ export const useFetchManifest = () => {

export const useManifest = () => useAtomValue(manifestAtom);

const nodesMetadataMapAtom = atom<BlocksMetadataMap | undefined | null>(null);
const nodesMetadataMapAtom = atom<BlockMetadataMap | undefined | null>(null);

export const useFetchNodesMetadata = () => {
const setNodesMetadata = useSetAtom(nodesMetadataMapAtom);
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/lib/sync.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BlockData } from "@/renderer/types";
import { BlocksMetadataMap } from "@/renderer/types/blocks-metadata";
import { BlockMetadataMap } from "@/renderer/types/blocks-metadata";
import { Leaf, RootNode, TreeNode } from "@/renderer/utils/ManifestLoader";
import { Edge, Node } from "reactflow";
import { CtrlData } from "@/renderer/types/node";
Expand All @@ -11,7 +11,7 @@ export function syncFlowchartWithManifest(
nodes: Node<BlockData>[],
edges: Edge[],
blockManifest: RootNode,
blockMetadata: BlocksMetadataMap,
blockMetadata: BlockMetadataMap,
): [Node<BlockData>[], Edge[]] {
const blocks = flattenManifest(blockManifest);

Expand Down
4 changes: 2 additions & 2 deletions src/renderer/services/FlowChartServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { captain } from "@/renderer/lib/ky";
import { HTTPError } from "ky";
import { RootNode, validateRootSchema } from "@/renderer/utils/ManifestLoader";
import { toast } from "sonner";
import { BlocksMetadataMap } from "@/renderer/types/blocks-metadata";
import { BlockMetadataMap } from "@/renderer/types/blocks-metadata";
import { EnvVar } from "../types/envVar";

export const postEnvironmentVariable = async (
Expand Down Expand Up @@ -122,7 +122,7 @@ export const getManifest = async () => {
export const getBlocksMetadata = async () => {
try {
const res = await captain.get("blocks/metadata").json();
return res as BlocksMetadataMap;
return res as BlockMetadataMap;
} catch (err: unknown) {
if (err instanceof HTTPError) {
toast.message("Failed to generate blocks metadata", {
Expand Down
212 changes: 212 additions & 0 deletions src/renderer/stores/project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import { Node, Edge } from "reactflow";
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { BlockData } from "../types/";
import { BlockParameterValue, TextData } from "../types/node";

import * as galleryItems from "../data/apps";
import { ExampleProjects } from "../data/docs-example-apps";
import * as RECIPES from "../data/RECIPES";
import { BlockManifest } from "../utils/ManifestLoader";
import { BlockMetadataMap } from "../types/blocks-metadata";
import { Project } from "../types/project";
import { syncFlowchartWithManifest } from "../lib/sync";
import { sendEventToMix } from "../services/MixpanelServices";
import { Err, Ok, Result } from "@/types/result";

type State = {
name: string | undefined;
nodes: Node<BlockData>[]; // TODO: Turn this into a record for fast lookup
edges: Edge[];
textNodes: Node<TextData>[];
};

type Actions = {
loadProject: (
project: Project,
manifest: BlockManifest,
metadata: BlockMetadataMap,
) => void;
updateBlockParameter: (
blockId: string,
paramName: string,
value: BlockParameterValue,
) => Result<void>;
updateBlockInitParameter: (
blockId: string,
paramName: string,
value: BlockParameterValue,
) => Result<void>;
updateBlockName: (blockId: string, name: string) => Result<void>;

handleNodeChanges: (
cb: (nodes: Node<BlockData>[]) => Node<BlockData>[],
) => void;

handleEdgeChanges: (cb: (nodes: Edge[]) => Edge[]) => void;
};

const defaultProjectData =
resolveProjectReference(resolveDefaultProjectReference()) ??
RECIPES.NOISY_SINE;
const initialNodes: Node<BlockData>[] = defaultProjectData.nodes;
const initialEdges: Edge[] = defaultProjectData.edges;

export const useProjectStore = create<State & Actions>()(
immer((set) => ({
name: undefined,
nodes: initialNodes,
edges: initialEdges,
textNodes: [],

handleNodeChanges: (
cb: (nodes: Node<BlockData>[]) => Node<BlockData>[],
) => {
set((state) => {
cb(state.nodes);
});
},
handleEdgeChanges: (cb: (edges: Edge[]) => Edge[]) => {
set((state) => {
cb(state.edges);
});
},

loadProject: (
project: Project,
manifest: BlockManifest,
metadata: BlockMetadataMap,
) => {
const {
rfInstance: { nodes, edges },
textNodes,
} = project;
const [syncedNodes, syncedEdges] = syncFlowchartWithManifest(
nodes,
edges,
manifest,
metadata,
);
set({
nodes: syncedNodes,
edges: syncedEdges,
textNodes: textNodes ?? [],
});

// toast("Synced blocks with manifest.");

sendEventToMix("Flow Export Object Loaded");
},
updateBlockParameter: (
blockId: string,
paramName: string,
value: BlockParameterValue,
) => {
try {
set((state) => {
const block = state.nodes.find((e) => e.id === blockId);
if (!block) {
return Err(new Error("Block not found"));
}

block.data.ctrls[paramName].value = value;
if (block.data.func === "CONSTANT" && paramName === "constant") {
block.data.label = value?.toString() ?? "CONSTANT";
}
});
} catch (e) {
return Err(e as Error);
}

sendEventToMix("Control Input Data Updated", {
blockId,
paramName,
value,
});

return Ok(undefined);
},

updateBlockInitParameter: (
blockId: string,
paramName: string,
value: BlockParameterValue,
) => {
try {
set((state) => {
const block = state.nodes.find((e) => e.id === blockId);
if (!block) {
throw new Error("Block not found");
}

if (!block.data.initCtrls) {
throw new Error("Block has no init parameters");
}

block.data.initCtrls[paramName].value = value;
});
} catch (e) {
return Err(e as Error);
}

sendEventToMix("Control Input Data Updated", {
blockId,
paramName,
value,
});
return Ok(undefined);
},

updateBlockName: (blockId: string, name: string) => {
try {
set((state) => {
const node = state.nodes.find((n) => n.data.id === blockId);
if (node === undefined) {
throw new Error("Block not found");
}

if (name === node?.data.label) {
return;
}

const isDuplicate = state.nodes.find(
(n) => n.data.label === name && n.data.id !== blockId,
);
if (isDuplicate) {
throw new Error(
`There is another node with the same label: ${name}`,
);
}
node.data.label = name;
});
} catch (e) {
return Err(e as Error);
}

sendEventToMix("Block Name Changed", { blockId, name });
return Ok(undefined);
},
})),
);

function resolveDefaultProjectReference() {
if (typeof window !== "undefined") {
const query = new URLSearchParams(window.location.search);
return query.get("project"); // TODO: set these env through electron API as process is not accessible at this level
}
return undefined;
}

function resolveProjectReference(project: string | null | undefined) {
if (!project) {
return null;
}

if (RECIPES[project]) {
return RECIPES[project];
} else if (galleryItems[project]) {
return galleryItems[project].rfInstance;
} else if (ExampleProjects[project]) {
return ExampleProjects[project].rfInstance;
}
}
2 changes: 1 addition & 1 deletion src/renderer/types/blocks-metadata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type BlocksMetadataMap = {
export type BlockMetadataMap = {
[node: string]: {
metadata: string;
path: string;
Expand Down
7 changes: 5 additions & 2 deletions src/renderer/types/node.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { NodeProps } from "reactflow";
import { Nullish } from "./util";

export type BlockParameterValue = Nullish<string | number | boolean>;

type BlockDefinition = {
name: string;
Expand All @@ -22,7 +25,7 @@ type BlockDefinition = {
string,
{
type: string;
default?: string | number | boolean | null | undefined;
default?: BlockParameterValue;
options?: Array<string>;
desc: string | null;
overload: Record<string, string[]> | null;
Expand All @@ -41,7 +44,7 @@ export type CtrlData = Record<
? U & {
functionName: string;
param: string;
value: string | boolean | number | undefined | null;
value: BlockParameterValue;
}
: never
: never
Expand Down
8 changes: 8 additions & 0 deletions src/renderer/types/project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ReactFlowJsonObject, Node } from "reactflow";
import { TextData, BlockData } from "./node";

export type Project = {
name?: string;
rfInstance: ReactFlowJsonObject<BlockData>;
textNodes?: Node<TextData>[];
};
1 change: 1 addition & 0 deletions src/renderer/types/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Nullish<T> = T | null | undefined;
1 change: 1 addition & 0 deletions src/renderer/utils/ManifestLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const rootSchema = z.object({
});

export type RootNode = z.infer<typeof rootSchema>;
export type BlockManifest = RootNode;
export type RootChild = z.infer<typeof rootSchema>["children"][0];

export const validateRootSchema = (schema: RootNode) => {
Expand Down
8 changes: 8 additions & 0 deletions src/types/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ export function Ok<T, E>(value: T): Result<T, E> {
export function Err<T, E>(error: E): Result<T, E> {
return { ok: false, error };
}

export function tryCatch<T>(fn: () => T): Result<T> {
try {
return Ok(fn());
} catch (e) {
return Err(e as Error);
}
}

0 comments on commit 3d5ae73

Please sign in to comment.