From 8ac92b6fe015be35b4319fbd314bc7f352eff0f5 Mon Sep 17 00:00:00 2001 From: Michael Chung Date: Wed, 14 May 2025 12:10:37 -0400 Subject: [PATCH] Add scene to 2d Lightbox modal. --- .../src/components/signaled/TopBar/TopBar.tsx | 43 ++++- .../features/ToolbarMain/ToolbarMain.tsx | 22 +-- .../apps/editor3d/app/src/routes/Signup.tsx | 150 ------------------ .../gallery-modal/src/lib/gallery-modal.tsx | 3 + .../lightbox-modal/src/lib/lightbox-modal.tsx | 57 +++++-- 5 files changed, 86 insertions(+), 189 deletions(-) delete mode 100644 frontend/apps/editor3d/app/src/routes/Signup.tsx 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 aab8660783..3735034345 100644 --- a/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx +++ b/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx @@ -19,6 +19,7 @@ import { Signal } from "@preact/signals-react"; import { useSignals } from "@preact/signals-react/runtime"; import { setLogoutStates } from "~/signals/authentication/utilities"; import { EngineContext } from "~/pages/PageEnigma/contexts/EngineContext"; +import { useAppUiContext } from "~/pages/Page2d/contextSignals/appUi"; function isEditorPath(path: string) { if (path === "/") return true; @@ -38,7 +39,12 @@ const appTabs: TabItem[] = [ { id: "3D", label: "3D" }, ]; -export const TopBar = ({ pageName, appTabIdSignal, setAppTabId, is3DInitSignal }: Props) => { +export const TopBar = ({ + pageName, + appTabIdSignal, + setAppTabId, + is3DInitSignal, +}: Props) => { useSignals(); const currentLocation = getCurrentLocationWithoutParams( @@ -55,7 +61,7 @@ export const TopBar = ({ pageName, appTabIdSignal, setAppTabId, is3DInitSignal } engine3D?.unmountEngine(); } setAppTabId(tabId); - } + }; const currentAppTabId = appTabIdSignal.value; const is3DInit = is3DInitSignal.value; @@ -143,6 +149,39 @@ export const TopBar = ({ pageName, appTabIdSignal, setAppTabId, is3DInitSignal } activeTab={activeLibraryTab} onTabChange={setActiveLibraryTab} onDownloadClicked={downloadFileFromUrl} + onAddToSceneClicked={async (url) => { + console.log("add to scene", url); + // Download the image from the URL and convert to File + const downloadImage = async (url: string): Promise => { + try { + // Fetch the image + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch image: ${response.status}`); + } + + // Get the blob data + const blob = await response.blob(); + + // Extract filename from URL or use a default name + const filename = url.split("/").pop() || "image.png"; + + // Create a File object from the blob + const file = new File([blob], filename, { type: blob.type }); + + return file; + } catch (error) { + console.error("Error downloading image:", error); + return null; + } + }; + const file = await downloadImage(url); + if (file) { + console.log("file", file); + } else { + console.error("Failed to download image"); + } + }} /> ); diff --git a/frontend/apps/editor3d/app/src/pages/Page2d/components/features/ToolbarMain/ToolbarMain.tsx b/frontend/apps/editor3d/app/src/pages/Page2d/components/features/ToolbarMain/ToolbarMain.tsx index f428fc5911..352a7c1a03 100644 --- a/frontend/apps/editor3d/app/src/pages/Page2d/components/features/ToolbarMain/ToolbarMain.tsx +++ b/frontend/apps/editor3d/app/src/pages/Page2d/components/features/ToolbarMain/ToolbarMain.tsx @@ -29,6 +29,7 @@ import { PaintModeMenu } from "../../../components/ui/ToolbarMain/PaintModeMenu" // import { EraseModeMenu } from "../../../components/ui/ToolbarMain/EraseModeMenu"; // import { BackgroundMenu } from "../../../components/ui/ToolbarMain/BackgroundMenu"; // import { bgColor } from "../../../signals/uiEvents/toolbarMain/backgroundMenu"; +import { useAppUiContext } from "~/pages/Page2d/contextSignals/appUi"; export const ToolbarMain = ({ disabled = false, @@ -147,9 +148,6 @@ export const ToolbarMain = ({ Add Image - {/* - Add Video - */} @@ -191,24 +189,6 @@ export const ToolbarMain = ({ tooltip="Paint Brush" /> )} - - {/* Conditionally show eraser menu on mode selection */} - {/* {buttonProps.ERASER.active ? ( - - ) : ( - - )} */} diff --git a/frontend/apps/editor3d/app/src/routes/Signup.tsx b/frontend/apps/editor3d/app/src/routes/Signup.tsx deleted file mode 100644 index 54f743f432..0000000000 --- a/frontend/apps/editor3d/app/src/routes/Signup.tsx +++ /dev/null @@ -1,150 +0,0 @@ -// import { faEnvelope, faKey, faUser } from "@fortawesome/pro-solid-svg-icons"; -// import { Button, H1, Input, Link, P } from "~/components"; -// import { addToast, authentication, signUp } from "~/signals"; -// import { FormEvent, useRef, useState } from "react"; -// import { AUTH_STATUS, ToastTypes } from "~/enums"; -// import { useSignalEffect, useSignals } from "@preact/signals-react/runtime"; -// import { useNavigate, useSearchParams } from "react-router-dom"; - -// const regEx = -// /^(([^<>()[\].,;:\s@"]+(.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i; - -// export default function SignUpScreen() { -// useSignals(); -// const navigate = useNavigate(); -// const formRef = useRef(null); -// const [usernameError, setUsernameError] = useState(""); -// const [emailError, setEmailError] = useState(""); -// const [passwordError, setPasswordError] = useState(""); -// const [passwordConfirmationError, setPasswordConfirmationError] = -// useState(""); -// const [searchParams] = useSearchParams(); -// const { status: authStatus } = authentication; - -// const handleOnSubmit = (ev: FormEvent) => { -// ev.preventDefault(); -// if (formRef.current) { -// const form = new FormData(formRef.current); -// const email = form.get("email")?.toString(); -// const password = form.get("password")?.toString(); -// const passwordConfirmation = form -// .get("password-confirmation") -// ?.toString(); -// const username = form.get("username")?.toString(); -// setUsernameError(""); -// if (!username || username.length < 6) { -// setUsernameError("Username must be at least 6 characters long"); -// } -// setEmailError(""); -// if (!email || !email.match(regEx)) { -// setEmailError("Email must be a valid email address"); -// } -// setPasswordError(""); -// if (!password || password.length < 8) { -// setPasswordError("Password must be at least 8 characters long"); -// } -// setPasswordConfirmationError(""); -// if (!passwordError && passwordConfirmation !== password) { -// setPasswordConfirmationError("Passwords do not match"); -// } - -// if ( -// !usernameError && -// !passwordError && -// !emailError && -// !passwordConfirmationError && -// signUp -// ) { -// signUp({ -// username: username!, -// email: email!, -// password: password!, -// passwordConfirmation: passwordConfirmation!, -// }).then((error) => { -// console.log("Error occured", error); -// if (error) { -// addToast(ToastTypes.ERROR, error); -// } -// }); -// } -// } -// }; // end handleOnSubmit - -// useSignalEffect(() => { -// const redirectPath = searchParams.get("redirect"); -// if (authStatus.value === AUTH_STATUS.LOGGED_IN) { -// navigate(redirectPath ? redirectPath : "/"); -// return; -// } -// if (authStatus.value === AUTH_STATUS.NO_ACCESS) { -// window.open("https://storyteller.ai/", "_self"); -// return; -// } -// }); - -// return ( -//
-//
-//

Sign Up to Storyteller

-//
-//
-//
-// -//
-// -//
-// -//
-// -//
-//
-// -//
-//

Back to home

-// Back -//
-//
-//
-//

Already have an account?

-// Log in instead -//
-//
-//
-//
-// ); -// } diff --git a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx index 9507ec01f1..046d75f64e 100644 --- a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx +++ b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx @@ -40,6 +40,7 @@ interface GalleryModalProps { activeTab: string; onTabChange: (tabId: string) => void; onDownloadClicked?: (url: string) => Promise; + onAddToSceneClicked?: (url: string) => Promise; } export const GalleryModal = React.memo( @@ -55,6 +56,7 @@ export const GalleryModal = React.memo( activeTab, onTabChange, onDownloadClicked, + onAddToSceneClicked, }: GalleryModalProps) => { const [groupedItems, setGroupedItems] = useState({}); const [loading, setLoading] = useState(false); @@ -342,6 +344,7 @@ export const GalleryModal = React.memo( createdAt={lightboxImage?.createdAt} downloadUrl={imageUrl} onDownloadClicked={onDownloadClicked} + onAddToSceneClicked={onAddToSceneClicked} /> ); diff --git a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx index 4c437c98c3..138ca70e42 100644 --- a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx +++ b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx @@ -16,6 +16,7 @@ interface LightboxModalProps { additionalInfo?: React.ReactNode; downloadUrl?: string; onDownloadClicked?: (url: string) => Promise; + onAddToSceneClicked?: (url: string) => Promise; } export function LightboxModal({ @@ -29,10 +30,12 @@ export function LightboxModal({ additionalInfo, downloadUrl, onDownloadClicked, + onAddToSceneClicked, }: LightboxModalProps) { return createPortal(
+ {/* backdrop */} + + {/* dialog container */}
e.stopPropagation()} > + {/* close */} + + {/* content grid */}
+ {/* image panel */}
{!imageUrl ? (
@@ -81,6 +90,8 @@ export function LightboxModal({ /> )}
+ + {/* info + actions */}
@@ -94,30 +105,44 @@ export function LightboxModal({ )} {additionalInfo}
- {downloadUrl && ( -
- {onDownloadClicked ? ( + + {/* buttons with spacing */} + {(onAddToSceneClicked && downloadUrl) || downloadUrl ? ( +
+ {onAddToSceneClicked && downloadUrl && ( - ) : ( - e.stopPropagation()} - className="no-underline" - > - - )} + + {downloadUrl && + (onDownloadClicked ? ( + + ) : ( + e.stopPropagation()} + className="no-underline" + > + + + ))}
- )} + ) : null}