diff --git a/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx b/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx index 0717fbffd4..0cc159a5df 100644 --- a/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx +++ b/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState } from "react"; import { faGear, faImages, @@ -23,19 +23,21 @@ import { MenuIconSelector, MenuIconItem, } from "@storyteller/ui-menu-icon-selector"; -import { signal } from "@preact/signals-react"; +import { signal } from "@preact/signals-react"; import { useSignals } from "@preact/signals-react/runtime"; import { setLogoutStates } from "~/signals/authentication/utilities"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useTabStore } from "~/pages/Stores/TabState"; -import { set3DPageMounted } from "~/pages/PageEnigma/Editor/editor"; +import { + is3DEditorInitialized, + is3DSceneLoaded, + set3DPageMounted, +} from "~/pages/PageEnigma/Editor/editor"; interface Props { pageName: string; } - - const appMenuTabs: MenuIconItem[] = [ { id: "2D", @@ -62,9 +64,7 @@ const appMenuTabs: MenuIconItem[] = [ export const topNavMediaId = signal(""); export const topNavMediaUrl = signal(""); -export const TopBar = ({ - pageName -}: Props) => { +export const TopBar = ({ pageName }: Props) => { useSignals(); const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false); @@ -73,9 +73,19 @@ export const TopBar = ({ galleryModalVisibleViewMode.value = true; galleryModalVisibleDuringDrag.value = true; }; - + const tabStore = useTabStore(); + const is3DSceneReady = is3DSceneLoaded.value; + const is3DEditorReady = is3DEditorInitialized.value; + const disableTabSwitcher = () => { + return ( + useTabStore.getState().activeTabId === "3D" && + !is3DEditorReady && + !is3DSceneReady + ); + }; + return ( <>
@@ -95,7 +105,7 @@ export const TopBar = ({ { // Disable 3d engine to prevent memory leak. if (tabId === "3D") { @@ -103,9 +113,7 @@ export const TopBar = ({ } else { set3DPageMounted(false); } - useTabStore - .getState() - .setActiveTab(tabId); + useTabStore.getState().setActiveTab(tabId); }} className="w-fit" /> @@ -158,10 +166,7 @@ export const TopBar = ({ globalAccountLogoutCallback={() => setLogoutStates()} /> - + ); }; diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/editor.ts b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/editor.ts index a366ffd8cc..eefbcf2617 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/editor.ts +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/Editor/editor.ts @@ -63,15 +63,24 @@ export type EditorInitializeConfig = { }; // ONLY USED IN THIS CLASS +// Need this one to ensure the canvas is inflated and non-zero sized const is3DPageMounted = signal(false); export const set3DPageMounted = (isMounted: boolean) => { is3DPageMounted.value = isMounted; }; + +// Need this one to know when the engine has initialized (but not necessarily loaded saves) export const is3DEditorInitialized = signal(false); export const setIs3DEditorInitialized = (isInitialized: boolean) => { is3DEditorInitialized.value = isInitialized; }; +// Need this one to know when the engine is done loading stuff into the scene for display +export const is3DSceneLoaded = signal(false); +export const setIs3DSceneLoaded = (isLoaded: boolean) => { + is3DSceneLoaded.value = isLoaded; +}; + class Editor { version: number; activeScene: Scene; @@ -660,10 +669,15 @@ class Editor { // Attach freecam state controller this.mouseOverEventHandler = this.mouseOverEventHandler.bind(this); + const onloadCallback = () => { + console.log("Setting Scene is loaded"); + setIs3DSceneLoaded(true); + }; + if (!this.utils.isEmpty(cacheJson)) { - this.loadCache(cacheJson); + this.loadCache(cacheJson).then(onloadCallback); } else if (!this.utils.isEmpty(sceneToken)) { - this.loadScene(sceneToken); + this.loadScene(sceneToken).then(onloadCallback); } else { signalScene({ title: "Untitled New Scene", @@ -671,6 +685,7 @@ class Editor { ownerToken: authentication.userInfo.value?.user_token, isModified: false, }); + onloadCallback(); } this._configurePostProcessingRaw(); @@ -1484,6 +1499,7 @@ class Editor { } unmountEngine() { + setIs3DSceneLoaded(false); this.stopRenderLoop(); this.sceneManager?.detachEventListeners(); this.disableFreeCamControls(); @@ -1496,6 +1512,7 @@ class Editor { this.rawRenderer?.dispose(); this.isMounted = false; + setIs3DEditorInitialized(false); console.log("3D Editor Engine unmounted"); } diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/contexts/EngineContext/EngineProvider.tsx b/frontend/apps/editor3d/app/src/pages/PageEnigma/contexts/EngineContext/EngineProvider.tsx index 4b9a3b08d9..fbad62259b 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/contexts/EngineContext/EngineProvider.tsx +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/contexts/EngineContext/EngineProvider.tsx @@ -1,7 +1,7 @@ import { ReactNode, useEffect, useRef, useState } from "react"; import { EngineContext } from "./EngineContext"; -import Editor from "~/pages/PageEnigma/Editor/editor"; +import Editor, { is3DSceneLoaded } from "~/pages/PageEnigma/Editor/editor"; import { useSignals } from "@preact/signals-react/runtime"; import { signal } from "@preact/signals-react"; @@ -59,16 +59,20 @@ export const EngineProvider = ({ sceneToken, children }: Props) => { } // If the tab is changed, cache state, unmount the editor and clear all input params - const sceneGenerationMetadata = getSceneGenerationMetaData( - activeEditorRef.current, - ); - const cacheJson = activeEditorRef.current.cacheScene({ - sceneTitle: "", - sceneToken: "", - sceneGenerationMetadata: sceneGenerationMetadata, - }); - const cacheString = JSON.stringify(cacheJson); - tabStore.updateTabData("3D", cacheString); + // This condition makes sure the editor saves a fully loaded scene so it doesn't lose data + // If the load was incomplete, we'll preserve the last cache anyway. + if (is3DSceneLoaded.peek()) { + const sceneGenerationMetadata = getSceneGenerationMetaData( + activeEditorRef.current, + ); + const cacheJson = activeEditorRef.current.cacheScene({ + sceneTitle: "", + sceneToken: "", + sceneGenerationMetadata: sceneGenerationMetadata, + }); + const cacheString = JSON.stringify(cacheJson); + tabStore.updateTabData("3D", cacheString); + } // Unmount/Destructor flow activeEditorRef.current?.unmountEngine();