From 963aca900ffa8f9885cc34c052757f37e3644480 Mon Sep 17 00:00:00 2001 From: Hebi Li Date: Thu, 27 Apr 2023 13:38:21 -0700 Subject: [PATCH] refactor: node type CODE, SCOPE, RICH in front-end --- ui/src/components/Canvas.tsx | 17 +++++------ ui/src/lib/fetch.tsx | 50 ++++++++++++++++++++++--------- ui/src/lib/nodes.tsx | 3 +- ui/src/lib/store/canvasSlice.tsx | 20 ++++++------- ui/src/lib/store/index.tsx | 2 +- ui/src/lib/store/runtimeSlice.tsx | 2 +- ui/src/lib/utils.tsx | 33 -------------------- 7 files changed, 57 insertions(+), 70 deletions(-) diff --git a/ui/src/components/Canvas.tsx b/ui/src/components/Canvas.tsx index defe00e2..d2810838 100644 --- a/ui/src/components/Canvas.tsx +++ b/ui/src/components/Canvas.tsx @@ -33,7 +33,6 @@ import { lowercase, numbers } from "nanoid-dictionary"; import { useStore } from "zustand"; import { RepoContext } from "../lib/store"; -import { dbtype2nodetype, nodetype2dbtype } from "../lib/utils"; import { useEdgesYjsObserver, useYjsObserver } from "../lib/nodes"; import { useApolloClient } from "@apollo/client"; @@ -46,7 +45,7 @@ import { YMap } from "yjs/dist/src/types/YMap"; import FloatingEdge from "./nodes/FloatingEdge"; import CustomConnectionLine from "./nodes/CustomConnectionLine"; -const nodeTypes = { scope: ScopeNode, code: CodeNode, rich: RichNode }; +const nodeTypes = { SCOPE: ScopeNode, CODE: CodeNode, RICH: RichNode }; const edgeTypes = { floating: FloatingEdge, }; @@ -62,7 +61,7 @@ function store2nodes(id: string, { getId2children, getPod }) { if (id !== "ROOT") { res.push({ id: id, - type: dbtype2nodetype(pod.type), + type: pod.type, data: { // label: `ID: ${id}, parent: ${pods[id].parent}, pos: ${pods[id].x}, ${pods[id].y}`, label: id, @@ -535,7 +534,7 @@ function CanvasImpl() { }; const onNodeContextMenu = (event, node) => { - if (node?.type !== "scope") return; + if (node?.type !== "SCOPE") return; event.preventDefault(); setShowContextMenu(true); @@ -651,8 +650,8 @@ function CanvasImpl() { { if (n.style?.borderColor) return n.style.borderColor; - if (n.type === "code") return "#d6dee6"; - if (n.type === "scope") return "#f4f6f8"; + if (n.type === "CODE") return "#d6dee6"; + if (n.type === "SCOPE") return "#f4f6f8"; return "#d6dee6"; }} @@ -673,17 +672,17 @@ function CanvasImpl() { x={points.x} y={points.y} addCode={() => - addNode("code", project({ x: client.x, y: client.y }), parentNode) + addNode("CODE", project({ x: client.x, y: client.y }), parentNode) } addScope={() => addNode( - "scope", + "SCOPE", project({ x: client.x, y: client.y }), parentNode ) } addRich={() => - addNode("rich", project({ x: client.x, y: client.y }), parentNode) + addNode("RICH", project({ x: client.x, y: client.y }), parentNode) } onShareClick={() => { setShareOpen(true); diff --git a/ui/src/lib/fetch.tsx b/ui/src/lib/fetch.tsx index 2f2e3ba5..e5a29b22 100644 --- a/ui/src/lib/fetch.tsx +++ b/ui/src/lib/fetch.tsx @@ -98,6 +98,37 @@ export async function doRemoteLoadRepo(client: ApolloClient, id: string) { } } +/** + * For historical reason, the backend DB schema pod.type are "CODE", "DECK", + * "WYSIWYG", while the node types in front-end are "CODE", "SCOPE", "RICH". + */ + +function dbtype2nodetype(dbtype: string) { + switch (dbtype) { + case "CODE": + return "CODE"; + case "DECK": + return "SCOPE"; + case "WYSIWYG": + return "RICH"; + default: + throw new Error(`unknown dbtype ${dbtype}`); + } +} + +function nodetype2dbtype(nodetype: string) { + switch (nodetype) { + case "CODE": + return "CODE"; + case "SCOPE": + return "DECK"; + case "RICH": + return "WYSIWYG"; + default: + throw new Error(`unknown nodetype ${nodetype}`); + } +} + export function normalize(pods) { const res: { [key: string]: Pod } = { ROOT: { @@ -108,7 +139,7 @@ export function normalize(pods) { // Adding this to avoid errors // XXX should I save these to db? lang: "python", - type: "DECK", + type: "SCOPE", content: "", x: 0, y: 0, @@ -141,19 +172,10 @@ export function normalize(pods) { type: res[id].type, })) : []; - // change children.id format - // UDPATE Or, I just put {id,type} in the children array - // - // pod.children = pod.children.map(({ id }) => id); - // - // sort according to index - // pod.children.sort((a, b) => res[a.id].index - res[b.id].index); - // if (pod.type === "WYSIWYG" || pod.type === "CODE") { - // pod.content = JSON.parse(pod.content); - // } pod.content = JSON.parse(pod.content); pod.staged = JSON.parse(pod.staged); pod.githead = JSON.parse(pod.githead); + pod.type = dbtype2nodetype(pod.type); if (pod.result) { pod.result = JSON.parse(pod.result); } @@ -161,9 +183,9 @@ export function normalize(pods) { pod.error = JSON.parse(pod.error); } // DEBUG the deck's content seems to be a long string of escaped \ - if (pod.type === "DECK" && pod.content) { + if (pod.type === "SCOPE" && pod.content) { console.log( - `warning: deck ${pod.id} content is not null, setting to null:`, + `warning: SCOPE ${pod.id} content is not null, setting to null:`, pod.content ); pod.content = null; @@ -195,7 +217,7 @@ function serializePodInput(pod) { height, }) => ({ id, - type, + type: nodetype2dbtype(type), column, lang, // stdout, diff --git a/ui/src/lib/nodes.tsx b/ui/src/lib/nodes.tsx index 6f4d7bdd..9b3ec74c 100644 --- a/ui/src/lib/nodes.tsx +++ b/ui/src/lib/nodes.tsx @@ -1,7 +1,6 @@ import { useCallback, useEffect, useState, useContext } from "react"; import { applyNodeChanges, Edge, Node } from "reactflow"; import { RepoContext } from "./store"; -import { nodetype2dbtype } from "./utils"; import { useStore } from "zustand"; import { useApolloClient } from "@apollo/client"; import { Transaction, YEvent } from "yjs"; @@ -30,7 +29,7 @@ export function useYjsObserver() { id: node.id, children: [], parent: "ROOT", - type: nodetype2dbtype(node.type || ""), + type: node.type as "CODE" | "SCOPE" | "RICH", lang: "python", x: node.position.x, y: node.position.y, diff --git a/ui/src/lib/store/canvasSlice.tsx b/ui/src/lib/store/canvasSlice.tsx index 12f65c3d..974dd3c8 100644 --- a/ui/src/lib/store/canvasSlice.tsx +++ b/ui/src/lib/store/canvasSlice.tsx @@ -15,7 +15,7 @@ import { Transaction, YEvent } from "yjs"; import { match, P } from "ts-pattern"; -import { myNanoId, nodetype2dbtype, dbtype2nodetype } from "../utils"; +import { myNanoId } from "../utils"; import { Connection, @@ -69,14 +69,14 @@ function createTemporaryNode(pod, position, parent = "ROOT", level = 0): any { width: pod.width, }; - if (pod.type === "DECK") { + if (pod.type === "SCOPE") { style["height"] = pod.height!; style["backgroundColor"] = level2color[level] || level2color["default"]; } const newNode = { id, - type: dbtype2nodetype(pod.type), + type: pod.type, position, data: { label: id, @@ -108,13 +108,13 @@ function createTemporaryNode(pod, position, parent = "ROOT", level = 0): any { /** * The new reactflow nodes for context-menu's addXXX items. */ -function createNewNode(type: "scope" | "code" | "rich", position): Node { +function createNewNode(type: "SCOPE" | "CODE" | "RICH", position): Node { let id = myNanoId(); const newNode = { id, type, position, - ...(type === "scope" + ...(type === "SCOPE" ? { width: 600, height: 600, @@ -154,7 +154,7 @@ function getScopeAt( const scope = nodes.findLast((node) => { let [x1, y1] = getAbsPos(node, nodesMap); return ( - node.type === "scope" && + node.type === "SCOPE" && x >= x1 && !excludes.includes(node.id) && x <= x1 + node.width && @@ -244,7 +244,7 @@ export interface CanvasSlice { setPaneBlur: () => void; addNode: ( - type: "code" | "scope" | "rich", + type: "CODE" | "SCOPE" | "RICH", position: XYPosition, parent: string ) => void; @@ -358,7 +358,7 @@ export const createCanvasSlice: StateCreator = ( style: { ...node.style, backgroundColor: - node.type === "scope" ? level2color[node.data.level] : undefined, + node.type === "SCOPE" ? level2color[node.data.level] : undefined, }, selected: selectedPods.has(node.id), // className: get().dragHighlight === node.id ? "active" : "", @@ -392,7 +392,7 @@ export const createCanvasSlice: StateCreator = ( id: node.id, children: [], parent: "ROOT", - type: nodetype2dbtype(node.type || ""), + type: node.type as "CODE" | "SCOPE" | "RICH", lang: "python", x: node.position.x, y: node.position.y, @@ -910,7 +910,7 @@ function fitChildren( node2children, nodesMap ): null | { x: number; y: number; width: number; height: number } { - if (node.type !== "scope") return null; + if (node.type !== "SCOPE") return null; // This is a scope node. Get all its children and calculate the x,y,width,height to tightly fit its children. let children = node2children.get(node.id); // If no children, nothing is changed. diff --git a/ui/src/lib/store/index.tsx b/ui/src/lib/store/index.tsx index c75221e1..8c04432b 100644 --- a/ui/src/lib/store/index.tsx +++ b/ui/src/lib/store/index.tsx @@ -17,7 +17,7 @@ enableMapSet(); export type Pod = { id: string; name?: string; - type: string; + type: "CODE" | "SCOPE" | "RICH"; content?: string; dirty?: boolean; isSyncing?: boolean; diff --git a/ui/src/lib/store/runtimeSlice.tsx b/ui/src/lib/store/runtimeSlice.tsx index 3a8a85dd..10a0d80a 100644 --- a/ui/src/lib/store/runtimeSlice.tsx +++ b/ui/src/lib/store/runtimeSlice.tsx @@ -321,7 +321,7 @@ export const createRuntimeSlice: StateCreator = ( get().clearResults(id); get().setRunning(id); set({ chain: [...get().chain, id] }); - } else if (get().pods[id].type === "DECK") { + } else if (get().pods[id].type === "SCOPE") { // If this pod is a scope, run all pods inside a scope by geographical order. // get the pods in the scope let children = get().node2children.get(id); diff --git a/ui/src/lib/utils.tsx b/ui/src/lib/utils.tsx index 6aad24b8..2d698522 100644 --- a/ui/src/lib/utils.tsx +++ b/ui/src/lib/utils.tsx @@ -36,37 +36,4 @@ export function getUpTime(startedAt: string) { return prettyTime; } -/** - * For historical reason, the state.pod.type and DB schema pod.type are "CODE", - * "DECK", "WYSIWYG", while the node types in react-flow are "code", "scope", - * "rich". These two functions document this and handle the conversion. - * @param dbtype - * @returns - */ -export function dbtype2nodetype(dbtype: string) { - switch (dbtype) { - case "CODE": - return "code"; - case "DECK": - return "scope"; - case "WYSIWYG": - return "rich"; - default: - throw new Error(`unknown dbtype ${dbtype}`); - } -} - -export function nodetype2dbtype(nodetype: string) { - switch (nodetype) { - case "code": - return "CODE"; - case "scope": - return "DECK"; - case "rich": - return "WYSIWYG"; - default: - throw new Error(`unknown nodetype ${nodetype}`); - } -} - export const myNanoId = customAlphabet(lowercase + numbers, 20);