import React, { useEffect, useState, useRef } from "react"; import { useParams, useNavigate } from "react-router-dom"; import Layout from "@/components/layout/Layout"; import Header from "../components/Header"; import { useUrdf } from "@/hooks/useUrdfData"; import { toast } from "sonner"; import { getImageUrl } from "@/api/urdfApi"; import { useUrdf as useUrdfContext } from "@/hooks/useUrdf"; import { downloadAndExtractZip } from "@/lib/UrdfDragAndDrop"; import { supabase } from "@/integrations/supabase/client"; import { Loader2 } from "lucide-react"; // This is needed to make TypeScript recognize webkitdirectory as a valid attribute declare module "react" { interface InputHTMLAttributes extends React.HTMLAttributes { directory?: string; webkitdirectory?: string; } } const ContentDetail: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { data: content, isLoading, error } = useUrdf(id || ""); const [imageUrl, setImageUrl] = useState("/placeholder.svg"); const { urdfProcessor, processUrdfFiles, currentRobotData } = useUrdfContext(); const [isLoadingUrdf, setIsLoadingUrdf] = useState(false); // Reference to track if we've already loaded this model const hasModelBeenLoaded = useRef(false); // State to track when the model is fully loaded and ready to display const [isModelReady, setIsModelReady] = useState(false); // If error occurred during fetch, show error and redirect useEffect(() => { if (error) { toast.error("Error loading robot details"); navigate("/"); } }, [error, navigate]); // If content not found and not loading, redirect to home useEffect(() => { if (!content && !isLoading && !error) { toast.error("Robot not found"); navigate("/"); } }, [content, isLoading, error, navigate]); // Fetch the image when content is loaded useEffect(() => { if (content?.imageUrl) { const fetchImage = async () => { const url = await getImageUrl(content.imageUrl); setImageUrl(url); }; fetchImage(); } }, [content]); // Reset the model states when the ID changes useEffect(() => { hasModelBeenLoaded.current = false; setIsModelReady(false); }, [id]); // Check if we already have this model loaded useEffect(() => { if (content && currentRobotData?.name === content.title) { // The model is already loaded hasModelBeenLoaded.current = true; setIsModelReady(true); console.log("Model already loaded and ready to display"); } }, [content, currentRobotData]); // Load the URDF model automatically when the content details are loaded useEffect(() => { const loadUrdfModel = async () => { // Only load if we have content, a processor, not currently loading, // and haven't already loaded this model if ( content?.urdfPath && urdfProcessor && !isLoadingUrdf && !hasModelBeenLoaded.current ) { // Mark that we've attempted to load this model hasModelBeenLoaded.current = true; setIsLoadingUrdf(true); setIsModelReady(false); // Reset model ready state while loading console.log(`Auto-loading URDF model for ${content.title}...`); const loadingToast = toast.loading(`Loading ${content.title}...`, { description: "Downloading and extracting URDF model", }); try { // Get the actual URL for the zip file let zipUrl = content.urdfPath; // If it's a storage path and not a full URL, get a signed URL if (!zipUrl.startsWith("http")) { const { data, error } = await supabase.storage .from("robotbucket") .createSignedUrl(zipUrl, 3600); // 1 hour expiry if (error) { throw new Error( `Failed to get URL for zip file: ${error.message}` ); } zipUrl = data.signedUrl; } // Download and extract the zip file const { files, availableModels } = await downloadAndExtractZip( zipUrl, urdfProcessor ); // Dismiss loading toast toast.dismiss(loadingToast); // If successful, process the extracted files similar to drag and drop if (files && availableModels.length > 0) { await processUrdfFiles(files, availableModels); toast.success(`Loaded model: ${content.title}`, { description: "URDF model ready for viewing", }); // Set a short timeout to ensure the URDF context is fully updated setTimeout(() => { setIsModelReady(true); }, 1000); } else { toast.error("No URDF models found in the zip file"); } } catch (error) { // Dismiss loading toast and show error toast.dismiss(loadingToast); console.error("Error processing URDF zip:", error); toast.error("Failed to load URDF model", { description: error instanceof Error ? error.message : "Unknown error", }); } finally { setIsLoadingUrdf(false); } } }; loadUrdfModel(); }, [content, urdfProcessor, processUrdfFiles, isLoadingUrdf]); // If still loading content data, show initial loading state if (isLoading || !content) { return (
Loading robot details...
); } // // If content is loaded but URDF model is still loading or processing, show URDF loading state // if (!isModelReady || isLoadingUrdf) { // return ( //
// //
// Loading {content.title} model... //
//

// Please wait while we prepare the 3D model //

//
// ); // } // Only render the full layout once the model is ready return (
{/* Layout taking full height */}
{/* Header absolutely positioned at the top */}
); }; export default ContentDetail;