mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-31 23:47:51 +00:00
"Make 3D" button to not enqueue directly but go to 3D object page
This commit is contained in:
1 parent
c7744ae545
commit
6d3ec8bfee
5 files changed
+100
-39
No files matched your search
@@ -140,6 +140,12 @@ export const ImageTo3DExperience = ({
|
||||
const clearPendingExternalImage = useImageTo3DWorldStore(
|
||||
(s) => s.clearPendingExternalImage,
|
||||
);
|
||||
const pendingObjectImage = useImageTo3DStore(
|
||||
(s) => s.pendingExternalImage,
|
||||
);
|
||||
const clearPendingObjectImage = useImageTo3DStore(
|
||||
(s) => s.clearPendingExternalImage,
|
||||
);
|
||||
|
||||
const results = variant === "object" ? objectResults : worldResults;
|
||||
const resetResults = variant === "object" ? objectReset : worldReset;
|
||||
@@ -153,24 +159,34 @@ export const ImageTo3DExperience = ({
|
||||
|
||||
useEffect(() => {
|
||||
// Read from store directly to avoid Strict Mode double-execution adding the image twice
|
||||
const pending = useImageTo3DWorldStore.getState().pendingExternalImage;
|
||||
if (variant === "world" && pending) {
|
||||
setWorldImages((prev) => {
|
||||
if (prev.length >= MAX_WORLD_IMAGES) return prev;
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
id: generateId(),
|
||||
preview: pending.url,
|
||||
mediaToken: pending.mediaToken,
|
||||
name: "Library Image",
|
||||
isUploading: false,
|
||||
},
|
||||
];
|
||||
});
|
||||
clearPendingExternalImage();
|
||||
if (variant === "world") {
|
||||
const pending = useImageTo3DWorldStore.getState().pendingExternalImage;
|
||||
if (pending) {
|
||||
setWorldImages((prev) => {
|
||||
if (prev.length >= MAX_WORLD_IMAGES) return prev;
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
id: generateId(),
|
||||
preview: pending.url,
|
||||
mediaToken: pending.mediaToken,
|
||||
name: "Library Image",
|
||||
isUploading: false,
|
||||
},
|
||||
];
|
||||
});
|
||||
clearPendingExternalImage();
|
||||
}
|
||||
} else if (variant === "object") {
|
||||
const pending = useImageTo3DStore.getState().pendingExternalImage;
|
||||
if (pending) {
|
||||
setUploadedPreview(pending.url);
|
||||
setUploadedMediaToken(pending.mediaToken);
|
||||
setUploadedName("Library Image");
|
||||
clearPendingObjectImage();
|
||||
}
|
||||
}
|
||||
}, [variant, pendingExternalImage, clearPendingExternalImage]);
|
||||
}, [variant, pendingExternalImage, clearPendingExternalImage, pendingObjectImage, clearPendingObjectImage]);
|
||||
|
||||
useEffect(() => {
|
||||
const onResize = () => setVh(window.innerHeight);
|
||||
|
||||
@@ -63,6 +63,7 @@ import {
|
||||
is3DSceneLoaded,
|
||||
set3DPageMounted,
|
||||
} from "~/pages/PageEnigma/Editor/editor";
|
||||
import { useImageTo3DStore } from "~/pages/PageImageTo3DObject/ImageTo3DStore";
|
||||
import { useImageTo3DWorldStore } from "~/pages/PageImageTo3DWorld/ImageTo3DWorldStore";
|
||||
import { useRemoveBackgroundStore } from "~/pages/PageRemoveBackground/RemoveBackgroundStore";
|
||||
import { TabId, useTabStore } from "~/pages/Stores/TabState";
|
||||
@@ -274,6 +275,23 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleMake3DObjectFromGallery = async (
|
||||
url: string,
|
||||
mediaId?: string,
|
||||
) => {
|
||||
try {
|
||||
if (mediaId) {
|
||||
useImageTo3DStore.getState().setPendingExternalImage(url, mediaId);
|
||||
}
|
||||
useTabStore.getState().setActiveTab("IMAGE_TO_3D_OBJECT");
|
||||
galleryModalVisibleViewMode.value = false;
|
||||
galleryModalVisibleDuringDrag.value = false;
|
||||
galleryModalLightboxVisible.value = false;
|
||||
} catch (e) {
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
|
||||
const handleMake3DWorldFromGallery = async (
|
||||
url: string,
|
||||
mediaId?: string,
|
||||
@@ -620,6 +638,7 @@ export const TopBar = ({ pageName }: Props) => {
|
||||
onEditClicked={handleEditFromGallery}
|
||||
onTurnIntoVideoClicked={handleTurnIntoVideoFromGallery}
|
||||
onRemoveBackgroundClicked={handleRemoveBackgroundFromGallery}
|
||||
onMake3DObjectClicked={handleMake3DObjectFromGallery}
|
||||
onMake3DWorldClicked={handleMake3DWorldFromGallery}
|
||||
/>
|
||||
|
||||
|
||||
@@ -129,6 +129,11 @@ async function captureModelThumbnail(modelUrl: string): Promise<string | null> {
|
||||
});
|
||||
}
|
||||
|
||||
export interface PendingExternalImage {
|
||||
url: string;
|
||||
mediaToken: string;
|
||||
}
|
||||
|
||||
export type ImageTo3DResult = {
|
||||
id: string;
|
||||
mode: "image" | "text";
|
||||
@@ -149,6 +154,9 @@ export const coverImageCache = new Map<string, string>();
|
||||
|
||||
type ImageTo3DState = {
|
||||
results: ImageTo3DResult[];
|
||||
pendingExternalImage: PendingExternalImage | null;
|
||||
setPendingExternalImage: (url: string, mediaToken: string) => void;
|
||||
clearPendingExternalImage: () => void;
|
||||
startGeneration: (
|
||||
mode: "image" | "text",
|
||||
note: string,
|
||||
@@ -175,6 +183,13 @@ type ImageTo3DState = {
|
||||
|
||||
export const useImageTo3DStore = create<ImageTo3DState>((set, get) => ({
|
||||
results: [],
|
||||
pendingExternalImage: null,
|
||||
setPendingExternalImage: (url: string, mediaToken: string) => {
|
||||
set({ pendingExternalImage: { url, mediaToken } });
|
||||
},
|
||||
clearPendingExternalImage: () => {
|
||||
set({ pendingExternalImage: null });
|
||||
},
|
||||
startGeneration: (
|
||||
mode: "image" | "text",
|
||||
note: string,
|
||||
|
||||
@@ -241,6 +241,10 @@ interface GalleryModalProps {
|
||||
url: string,
|
||||
media_id?: string,
|
||||
) => Promise<void> | void;
|
||||
onMake3DObjectClicked?: (
|
||||
url: string,
|
||||
media_id?: string,
|
||||
) => Promise<void> | void;
|
||||
onMake3DWorldClicked?: (
|
||||
url: string,
|
||||
media_id?: string,
|
||||
@@ -356,6 +360,7 @@ export const GalleryModal = React.memo(
|
||||
onEditClicked,
|
||||
onTurnIntoVideoClicked,
|
||||
onRemoveBackgroundClicked,
|
||||
onMake3DObjectClicked,
|
||||
onMake3DWorldClicked,
|
||||
}: GalleryModalProps) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -1286,6 +1291,7 @@ export const GalleryModal = React.memo(
|
||||
onEditClicked={onEditClicked}
|
||||
onTurnIntoVideoClicked={onTurnIntoVideoClicked}
|
||||
onRemoveBackgroundClicked={onRemoveBackgroundClicked}
|
||||
onMake3DObjectClicked={onMake3DObjectClicked}
|
||||
onMake3DWorldClicked={onMake3DWorldClicked}
|
||||
onNavigatePrev={handleNavigatePrev}
|
||||
onNavigateNext={handleNavigateNext}
|
||||
|
||||
@@ -15,11 +15,7 @@ import {
|
||||
faVideo,
|
||||
faWandMagicSparkles,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import {
|
||||
EnqueueImageTo3dObject,
|
||||
EnqueueImageTo3dObjectModel,
|
||||
MediaFileDelete,
|
||||
} from "@storyteller/tauri-api";
|
||||
import { MediaFileDelete } from "@storyteller/tauri-api";
|
||||
import { LoadingSpinner } from "@storyteller/ui-loading-spinner";
|
||||
import { Viewer3D } from "@storyteller/ui-viewer-3d";
|
||||
import {
|
||||
@@ -85,6 +81,10 @@ interface LightboxModalProps {
|
||||
url: string,
|
||||
media_id?: string,
|
||||
) => Promise<void> | void;
|
||||
onMake3DObjectClicked?: (
|
||||
url: string,
|
||||
media_id?: string,
|
||||
) => Promise<void> | void;
|
||||
onMake3DWorldClicked?: (
|
||||
url: string,
|
||||
media_id?: string,
|
||||
@@ -115,6 +115,7 @@ export function LightboxModal({
|
||||
onEditClicked,
|
||||
onTurnIntoVideoClicked,
|
||||
onRemoveBackgroundClicked,
|
||||
onMake3DObjectClicked,
|
||||
onMake3DWorldClicked,
|
||||
batchImageToken,
|
||||
onNavigatePrev,
|
||||
@@ -985,23 +986,27 @@ export function LightboxModal({
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{derivedMediaClass === "image" && (
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="secondary"
|
||||
icon={faCube}
|
||||
onClick={async (e) => {
|
||||
gtagEvent("image_to_3d_clicked");
|
||||
await EnqueueImageTo3dObject({
|
||||
image_media_token: selectedMediaToken,
|
||||
model: EnqueueImageTo3dObjectModel.Hunyuan3d3,
|
||||
frontend_caller: "mini_app",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Make 3D
|
||||
</Button>
|
||||
)}
|
||||
{onMake3DObjectClicked &&
|
||||
actionUrl &&
|
||||
derivedMediaClass === "image" && (
|
||||
<Button
|
||||
className="w-full"
|
||||
variant="secondary"
|
||||
icon={faCube}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
gtagEvent("image_to_3d_clicked");
|
||||
await onMake3DObjectClicked(
|
||||
actionUrl,
|
||||
selectedMediaToken,
|
||||
);
|
||||
onClose();
|
||||
onCloseGallery();
|
||||
}}
|
||||
>
|
||||
Make 3D
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{onMake3DWorldClicked &&
|
||||
actionUrl &&
|
||||
|
||||
Reference in new issue
Block a user