Untitled
unknown
plain_text
2 months ago
5.4 kB
4
Indexable
// components/download-all.tsx import { useState, useEffect, useRef } from "react"; import { Download } from "lucide-react"; import { motion } from "framer-motion"; import { useParams } from "next/navigation"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; export function DownloadAll() { const [isDownloading, setIsDownloading] = useState(false); const [progress, setProgress] = useState(0); const [startTime, setStartTime] = useState<number | null>(null); const workerRef = useRef<Worker | null>(null); const params = useParams(); const clientId = params.clientId as string; useEffect(() => { if (typeof window !== 'undefined') { workerRef.current = new Worker('/zip-worker.js'); workerRef.current.onmessage = (e) => { const { type, payload } = e.data; switch (type) { case 'progress': setProgress(Math.min(90, payload)); break; case 'generating': setProgress(90 + (payload * 0.1)); break; case 'complete': handleDownloadComplete(payload); break; case 'error': handleError(payload); break; } }; } return () => { workerRef.current?.terminate(); }; }, []); const handleDownloadComplete = (blob: Blob) => { const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${clientId}-fotos.zip`; document.body.appendChild(link); link.click(); document.body.removeChild(link); setTimeout(() => { URL.revokeObjectURL(url); setProgress(100); toast.success("Download erfolgreich"); setTimeout(() => { setIsDownloading(false); setProgress(0); setStartTime(null); }, 3000); }, 1000); }; const handleError = (error: string) => { console.error("Download failed:", error); setIsDownloading(false); setProgress(0); setStartTime(null); toast.error("Download fehlgeschlagen", { description: error || "Bitte versuchen Sie es später erneut." }); }; const handleDownloadAll = async () => { try { setIsDownloading(true); setProgress(0); setStartTime(Date.now()); toast.info("Download wird vorbereitet", { description: "Die Bilder werden zusammengefasst. Dies kann einige Minuten dauern." }); const response = await fetch(`/api/download-urls/${clientId}`); if (!response.ok) throw new Error('Failed to get download URLs'); const { files } = await response.json(); // Initialize worker with total file count workerRef.current?.postMessage({ type: 'start', payload: { total: files.length } }); // Add files to ZIP for (const file of files) { workerRef.current?.postMessage({ type: 'add', payload: file }); } // Generate final ZIP workerRef.current?.postMessage({ type: 'generate' }); } catch (error) { handleError(error.message); } }; const formatTimeRemaining = (seconds: number) => { if (seconds < 60) return `${Math.round(seconds)} Sekunden`; const minutes = Math.ceil(seconds / 60); return `${minutes} ${minutes === 1 ? 'Minute' : 'Minuten'}`; }; const getStatusMessage = () => { if (!startTime) return "Wird vorbereitet..."; if (progress >= 100) return "Download abgeschlossen!"; if (progress >= 90) return "ZIP-Datei wird erstellt..."; const elapsedSeconds = (Date.now() - startTime) / 1000; if (progress > 0) { const estimatedTotalSeconds = (elapsedSeconds / progress) * 100; const remainingSeconds = Math.max(0, estimatedTotalSeconds - elapsedSeconds); return `Download läuft... ${progress.toFixed(1)}% (ca. ${formatTimeRemaining(remainingSeconds)} verbleibend)`; } return "Download startet..."; }; return ( <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} className="mb-12 rounded-lg border bg-card p-4 text-card-foreground shadow-sm" > <div className="flex flex-col items-center gap-4 text-center sm:flex-row sm:justify-between sm:text-left"> <div> <h2 className="text-lg font-semibold">Alle Fotos herunterladen</h2> <p className="text-sm text-muted-foreground"> Erhalten Sie alle Fotos in Originalqualität in einer einzigen Datei </p> </div> <div className="flex flex-col gap-2 min-w-[200px]"> {isDownloading ? ( <> <Progress value={progress} className="w-full" /> <p className="text-sm text-muted-foreground text-center"> {getStatusMessage()} </p> </> ) : ( <Button size="lg" onClick={handleDownloadAll}> <Download className="mr-2 h-4 w-4" /> Alle herunterladen </Button> )} </div> </div> </motion.div> ); }
Editor is loading...
Leave a Comment