diff --git a/apps/designer/app/canvas/canvas.tsx b/apps/designer/app/canvas/canvas.tsx index f5b3d76933..81914d2ae5 100644 --- a/apps/designer/app/canvas/canvas.tsx +++ b/apps/designer/app/canvas/canvas.tsx @@ -46,6 +46,7 @@ import { usePublishScrollState } from "./shared/use-publish-scroll-state"; import { useDragAndDrop } from "./shared/use-drag-drop"; import { setInstanceChildrenMutable } from "~/shared/tree-utils"; import { CanvasData } from "~/shared/db"; +import { useSubscribeDesignerReady } from "./shared/use-designer-ready"; registerContainers(); @@ -118,6 +119,7 @@ export const Canvas = ({ data }: CanvasProps): JSX.Element | null => { if (data.tree === null) { throw new Error("Tree is null"); } + const isDesignerReady = useSubscribeDesignerReady(); useInitializeBreakpoints(data.breakpoints); globalStyles(); useAllUserProps(data.props); @@ -129,7 +131,7 @@ export const Canvas = ({ data }: CanvasProps): JSX.Element | null => { if (elements === undefined) return null; - if (isPreviewMode) { + if (isPreviewMode || isDesignerReady === false) { return elements; } diff --git a/apps/designer/app/canvas/shared/use-designer-ready.ts b/apps/designer/app/canvas/shared/use-designer-ready.ts new file mode 100644 index 0000000000..1a13769ca1 --- /dev/null +++ b/apps/designer/app/canvas/shared/use-designer-ready.ts @@ -0,0 +1,18 @@ +import { useState } from "react"; +import { publish, useSubscribe } from "~/shared/pubsub"; + +declare module "~/shared/pubsub" { + export interface PubsubMap { + designerReady: undefined; + designerReadyAck: undefined; + } +} + +export const useSubscribeDesignerReady = () => { + const [isReady, setIsReady] = useState(false); + useSubscribe("designerReady", () => { + setIsReady(true); + publish({ type: "designerReadyAck" }); + }); + return isReady; +}; diff --git a/apps/designer/app/designer/designer.tsx b/apps/designer/app/designer/designer.tsx index a268d51991..539cd507ba 100644 --- a/apps/designer/app/designer/designer.tsx +++ b/apps/designer/app/designer/designer.tsx @@ -1,4 +1,4 @@ -import { useCallback } from "react"; +import { useCallback, useEffect, useState } from "react"; import { type Publish, usePublish, useSubscribe } from "~/shared/pubsub"; import type { Project } from "@webstudio-is/project"; import type { Config } from "~/config"; @@ -7,6 +7,7 @@ import interStyles from "~/shared/font-faces/inter.css"; import { SidebarLeft } from "./features/sidebar-left"; import { Inspector } from "./features/inspector"; import { + useAssets, useHoveredInstanceData, useSelectedInstanceData, useSyncStatus, @@ -33,6 +34,8 @@ import { import { useClientSettings } from "./shared/client-settings"; import { Navigator } from "./features/sidebar-left"; import { PANEL_WIDTH } from "./shared/constants"; +import { Asset } from "@webstudio-is/asset-uploader"; +import { useInterval } from "react-use"; export const links = () => { return [ @@ -61,6 +64,15 @@ const useSubscribeSyncStatus = () => { useSubscribe("syncStatus", setValue); }; +const useSetAssets = (assets?: Array) => { + const [, setAssets] = useAssets(); + useEffect(() => { + if (assets) { + setAssets(assets); + } + }, [assets, setAssets]); +}; + const useNavigatorLayout = () => { // We need to render the detached state only once the setting was actually loaded from local storage. // Otherwise we may show the detached state because its the default and then hide it immediately. @@ -68,6 +80,23 @@ const useNavigatorLayout = () => { return isLoaded ? clientSettings.navigatorLayout : "docked"; }; +const usePublishDesignerReady = (publish: Publish) => { + const [isAcknowledged, setIsAcknowledged] = useState(false); + + useInterval( + () => { + // We publish this even to let canvas know that we are now listening to the events, otherwise if canvas loads faster than designer, which is possible with SSR, + // we can miss the events and designer will just not connect to the canvas. + publish({ type: "designerReady" }); + }, + isAcknowledged ? null : 100 + ); + + useSubscribe("designerReadyAck", () => { + setIsAcknowledged(true); + }); +}; + type SidePanelProps = { children: JSX.Element | Array; isPreviewMode: boolean; @@ -220,12 +249,14 @@ export const Designer = ({ config, project }: DesignerProps) => { useSubscribeSelectedInstanceData(); useSubscribeHoveredInstanceData(); useSubscribeBreakpoints(); + useSetAssets(project.assets); const [publish, publishRef] = usePublish(); const [isPreviewMode] = useIsPreviewMode(); usePublishShortcuts(publish); const onRefReadCanvasWidth = useUpdateCanvasWidth(); const { onRef: onRefReadCanvas, onTransitionEnd } = useReadCanvasRect(); const [dragAndDropState] = useDragAndDropState(); + usePublishDesignerReady(publish); const iframeRefCallback = useCallback( (ref) => { @@ -263,7 +294,7 @@ export const Designer = ({ config, project }: DesignerProps) => { - + void; }) => { const [isInfoOpen, setInfoOpen] = useState(false); diff --git a/apps/designer/app/designer/features/sidebar-left/panels/asset-manager/asset-info.tsx b/apps/designer/app/designer/features/sidebar-left/panels/assets/asset-info.tsx similarity index 76% rename from apps/designer/app/designer/features/sidebar-left/panels/asset-manager/asset-info.tsx rename to apps/designer/app/designer/features/sidebar-left/panels/assets/asset-info.tsx index 94ce843b40..491709c4a4 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/asset-manager/asset-info.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/assets/asset-info.tsx @@ -8,7 +8,7 @@ import { } from "@webstudio-is/icons"; import { useSubmit } from "@remix-run/react"; import prettyBytes from "pretty-bytes"; -import type { BaseAsset } from "~/designer/shared/assets"; +import { Asset } from "@webstudio-is/asset-uploader"; const Filename = ({ name }: { name: string }) => { const splitName = name.split("."); @@ -45,7 +45,7 @@ const Filename = ({ name }: { name: string }) => { ); }; -type AssetInfoProps = BaseAsset & { +type AssetInfoProps = Asset & { onClose: () => void; onDelete: () => void; }; @@ -81,20 +81,22 @@ export const AssetInfo = ({ - - - - - - {meta.width} x {meta.height} - - {" "} - - - {getFormattedAspectRatio(meta)} - - - + {"width" in meta && "height" in meta ? ( + + + + + + {meta.width} x {meta.height} + + {" "} + + + {getFormattedAspectRatio(meta)} + + + + ) : null}