From 8635dc6755a77f37513598bf5d16ce5bb55367fb Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Sat, 28 Jan 2023 15:24:23 +0300 Subject: [PATCH] Switch to normalized props on server (#883) Ref https://github.com/webstudio-is/webstudio-designer/issues/696 Now all props is migrated on client side, we can drop legacy store and switch to new format server side. --- apps/designer/app/canvas/shared/instance.ts | 17 ++---- .../props-panel/props-panel.stories.tsx | 43 ++++++--------- .../features/props-panel/props-panel.tsx | 53 +++++------------- apps/designer/app/shared/db/canvas.server.ts | 4 +- apps/designer/app/shared/sync/sync-stores.ts | 5 +- packages/project/src/db/props.ts | 55 +++---------------- packages/react-sdk/src/db/index.ts | 1 - packages/react-sdk/src/db/types.ts | 6 -- packages/react-sdk/src/index.ts | 2 - packages/react-sdk/src/tree/root.ts | 2 - .../src/user-props/all-user-props.ts | 8 --- 11 files changed, 50 insertions(+), 146 deletions(-) delete mode 100644 packages/react-sdk/src/db/index.ts delete mode 100644 packages/react-sdk/src/db/types.ts delete mode 100644 packages/react-sdk/src/user-props/all-user-props.ts diff --git a/apps/designer/app/canvas/shared/instance.ts b/apps/designer/app/canvas/shared/instance.ts index a85dc09f73..f2c7a40e57 100644 --- a/apps/designer/app/canvas/shared/instance.ts +++ b/apps/designer/app/canvas/shared/instance.ts @@ -1,13 +1,9 @@ import { useEffect } from "react"; -import type { Instance, PropsItem } from "@webstudio-is/project-build"; -import { - getComponentMeta, - allUserPropsContainer, -} from "@webstudio-is/react-sdk"; - -import { useSubscribe } from "~/shared/pubsub"; -import { utils, type InstanceInsertionSpec } from "@webstudio-is/project"; import store from "immerhin"; +import type { Instance, PropsItem } from "@webstudio-is/project-build"; +import { getComponentMeta } from "@webstudio-is/react-sdk"; +import { utils, type InstanceInsertionSpec } from "@webstudio-is/project"; +import { useSubscribe } from "~/shared/pubsub"; import { propsStore, rootInstanceContainer, @@ -64,8 +60,8 @@ export const useInsertInstance = () => { ({ instance, dropTarget, props: insertedProps }) => { const selectedInstanceId = selectedInstanceIdStore.get(); store.createTransaction( - [rootInstanceContainer, allUserPropsContainer, propsStore], - (rootInstance, allUserProps, props) => { + [rootInstanceContainer, propsStore], + (rootInstance, props) => { if (rootInstance === undefined) { return; } @@ -78,7 +74,6 @@ export const useInsertInstance = () => { selectedInstanceIdStore.set(instance.id); } if (insertedProps !== undefined) { - allUserProps[instance.id] = insertedProps; props.push(...insertedProps); } } diff --git a/apps/designer/app/designer/features/props-panel/props-panel.stories.tsx b/apps/designer/app/designer/features/props-panel/props-panel.stories.tsx index cf70adc2b8..978751412a 100644 --- a/apps/designer/app/designer/features/props-panel/props-panel.stories.tsx +++ b/apps/designer/app/designer/features/props-panel/props-panel.stories.tsx @@ -1,9 +1,7 @@ import type { ComponentStory } from "@storybook/react"; import type { PropsItem } from "@webstudio-is/project-build"; -import { - allUserPropsContainer, - getComponentMetaProps, -} from "@webstudio-is/react-sdk"; +import { getComponentMetaProps } from "@webstudio-is/react-sdk"; +import { propsStore } from "~/shared/nano-states"; import { PropsPanel } from "./props-panel"; export default { @@ -12,17 +10,15 @@ export default { }; export const NoProps: ComponentStory = () => { - allUserPropsContainer.set({ - "1": [ - { - id: "disabled", - instanceId: "instanceId", - name: "disabled", - type: "boolean", - value: true, - }, - ], - }); + propsStore.set([ + { + id: "disabled", + instanceId: "1", + name: "disabled", + type: "boolean", + value: true, + }, + ]); return ( = () => { }; export const RequiredProps: ComponentStory = () => { - allUserPropsContainer.set({ - "1": [], - }); + propsStore.set([]); return ( = () => { }; export const DefaultProps: ComponentStory = () => { - allUserPropsContainer.set({ - "1": [], - }); + propsStore.set([]); return ( = () => { const meta = getComponentMetaProps("Button") ?? {}; export const AllProps: ComponentStory = () => { - allUserPropsContainer.set({ - "3": Object.entries(meta).map(([name, value]) => { + propsStore.set( + Object.entries(meta).map(([name, value]) => { return { id: name, + instanceId: "3", name, value: value?.defaultValue ?? "", } as PropsItem; - }), - }); + }) + ); return ( { selectedInstance, updateProps: (update) => { - store.createTransaction( - [allUserPropsContainer, propsStore], - (allUserProps, props) => { - let instanceProps = allUserProps[instanceId]; - if (instanceProps === undefined) { - instanceProps = []; - allUserProps[instanceId] = instanceProps; - } - replaceByOrAppendMutable( - instanceProps, - update, - (item) => item.id === update.id - ); - replaceByOrAppendMutable( - props, - update, - (item) => item.id === update.id - ); - } - ); + store.createTransaction([propsStore], (props) => { + replaceByOrAppendMutable( + props, + update, + (item) => item.id === update.id + ); + }); }, deleteProp: (id) => { - store.createTransaction( - [allUserPropsContainer, propsStore], - (allUserProps, props) => { - const instanceProps = allUserProps[instanceId]; - if (instanceProps) { - removeByMutable(instanceProps, (prop) => prop.id === id); - } - removeByMutable(props, (prop) => prop.id === id); - } - ); + store.createTransaction([propsStore], (props) => { + removeByMutable(props, (prop) => prop.id === id); + }); }, }); diff --git a/apps/designer/app/shared/db/canvas.server.ts b/apps/designer/app/shared/db/canvas.server.ts index 096657cc08..ee0f50d5d9 100644 --- a/apps/designer/app/shared/db/canvas.server.ts +++ b/apps/designer/app/shared/db/canvas.server.ts @@ -24,9 +24,8 @@ export const loadCanvasData = async ( throw new Error(`Page ${pageIdOrPath} not found`); } - const [tree, props, breakpoints, designTokens, assets] = await Promise.all([ + const [tree, breakpoints, designTokens, assets] = await Promise.all([ projectDb.tree.loadById(page.treeId), - projectDb.props.loadByTreeId(page.treeId), projectDb.breakpoints.load(build.id), designTokensDb.load(build.id), loadByProject(project.id), @@ -42,7 +41,6 @@ export const loadCanvasData = async ( return { tree, - props, breakpoints: breakpoints.values, designTokens, buildId: build.id, diff --git a/apps/designer/app/shared/sync/sync-stores.ts b/apps/designer/app/shared/sync/sync-stores.ts index 9d8cfef589..3c2bc015ba 100644 --- a/apps/designer/app/shared/sync/sync-stores.ts +++ b/apps/designer/app/shared/sync/sync-stores.ts @@ -1,7 +1,6 @@ import store, { type Change } from "immerhin"; import type { WritableAtom } from "nanostores"; import { useEffect } from "react"; -import { allUserPropsContainer } from "@webstudio-is/react-sdk"; import { type Publish, subscribe } from "~/shared/pubsub"; import { rootInstanceContainer, @@ -44,8 +43,7 @@ export const registerContainers = () => { store.register("breakpoints", breakpointsContainer); store.register("root", rootInstanceContainer); store.register("styles", stylesContainer); - store.register("props", allUserPropsContainer); - store.register("props-modern", propsStore); + store.register("props", propsStore); store.register("designTokens", designTokensContainer); // synchronize whole states clientStores.set("selectedInstanceId", selectedInstanceIdStore); @@ -143,7 +141,6 @@ const syncStoresState = (name: SyncEventSource, publish: Publish) => { ], }, }); - // }) ); } diff --git a/packages/project/src/db/props.ts b/packages/project/src/db/props.ts index 68c6d8cdf3..761665f318 100644 --- a/packages/project/src/db/props.ts +++ b/packages/project/src/db/props.ts @@ -1,6 +1,5 @@ import warnOnce from "warn-once"; import { type Tree, StoredProps, Props } from "@webstudio-is/project-build"; -import { type AllUserProps, type InstanceProps } from "@webstudio-is/react-sdk"; import { applyPatches, type Patch } from "immer"; import { prisma } from "@webstudio-is/prisma-client"; import { formatAsset } from "@webstudio-is/asset-uploader/server"; @@ -59,35 +58,6 @@ export const parseProps = async (propsString: string) => { return props; }; -export const loadByTreeId = async (treeId: Tree["id"]) => { - const tree = await prisma.tree.findUnique({ - where: { id: treeId }, - }); - - if (tree === null) { - return []; - } - - const props = await parseProps(tree.props); - - const instancePropsMap = new Map(); - for (const propsItem of props) { - let instanceProps = instancePropsMap.get(propsItem.instanceId); - if (instanceProps === undefined) { - instanceProps = { - id: "", - instanceId: propsItem.instanceId, - treeId, - props: [], - }; - instancePropsMap.set(propsItem.instanceId, instanceProps); - } - instanceProps.props.push(propsItem); - } - - return Array.from(instancePropsMap.values()); -}; - export const serializeProps = (props: Props) => { const storedProps: StoredProps = []; for (const prop of props) { @@ -111,25 +81,18 @@ export const patch = async ( { treeId }: { treeId: Tree["id"] }, patches: Array ) => { - const allProps = await loadByTreeId(treeId); - - // We should get rid of this by applying patches on the client to the original - // model instead of the instanceId map. - // The map is handy for accessing props, but probably should be just cached interface, not the main data structure. - const allPropsMapByInstanceId = allProps.reduce((acc, prop) => { - acc[prop.instanceId] = prop.props; - return acc; - }, {} as AllUserProps); - const nextProps = applyPatches( - allPropsMapByInstanceId, - patches - ); - - const props: Props = Object.values(nextProps).flat(); + const tree = await prisma.tree.findUnique({ + where: { id: treeId }, + }); + if (tree === null) { + return; + } + const props = await parseProps(tree.props); + const patchedProps = applyPatches(props, patches); await prisma.tree.update({ data: { - props: serializeProps(props), + props: serializeProps(patchedProps), }, where: { id: treeId }, }); diff --git a/packages/react-sdk/src/db/index.ts b/packages/react-sdk/src/db/index.ts deleted file mode 100644 index eea524d655..0000000000 --- a/packages/react-sdk/src/db/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./types"; diff --git a/packages/react-sdk/src/db/types.ts b/packages/react-sdk/src/db/types.ts deleted file mode 100644 index 2592f08675..0000000000 --- a/packages/react-sdk/src/db/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { InstanceProps as DbInstanceProps } from "@webstudio-is/prisma-client"; -import type { PropsItem } from "@webstudio-is/project-build"; - -export type InstanceProps = Omit & { - props: Array; -}; diff --git a/packages/react-sdk/src/index.ts b/packages/react-sdk/src/index.ts index 8ad95fd070..3a0c921dff 100644 --- a/packages/react-sdk/src/index.ts +++ b/packages/react-sdk/src/index.ts @@ -1,9 +1,7 @@ export * from "./css"; export * from "./tree"; export * from "./components"; -export * from "./user-props/all-user-props"; export * from "./pubsub"; -export * from "./db"; export * from "./app"; export { customComponents, diff --git a/packages/react-sdk/src/tree/root.ts b/packages/react-sdk/src/tree/root.ts index 7a6ba22866..5a73209410 100644 --- a/packages/react-sdk/src/tree/root.ts +++ b/packages/react-sdk/src/tree/root.ts @@ -4,7 +4,6 @@ import type { Breakpoint } from "@webstudio-is/css-data"; import type { DesignToken } from "@webstudio-is/design-tokens"; import type { Tree } from "@webstudio-is/project-build"; import type { Asset } from "@webstudio-is/asset-uploader"; -import type { InstanceProps } from "../db"; import { createElementsTree } from "./create-elements-tree"; import { WrapperComponent } from "./wrapper-component"; import { registerComponents } from "../components"; @@ -16,7 +15,6 @@ export type Data = { tree: Tree | null; breakpoints: Array; designTokens: Array; - props: Array; assets: Array; params?: Params; }; diff --git a/packages/react-sdk/src/user-props/all-user-props.ts b/packages/react-sdk/src/user-props/all-user-props.ts deleted file mode 100644 index a16682353c..0000000000 --- a/packages/react-sdk/src/user-props/all-user-props.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { atom } from "nanostores"; -import type { Instance, PropsItem } from "@webstudio-is/project-build"; - -export type AllUserProps = { - [id: Instance["id"]]: PropsItem[]; -}; - -export const allUserPropsContainer = atom({});