import { useState, useEffect, useCallback } from "react"; import { toast } from "sonner"; import { ImageFile, ConversionSettings } from "@/types"; import { processImage, generateFinalFilename, downloadDataUrl } from "@/lib/image-processor"; export const initialSettings: ConversionSettings = { width: "", height: "", aspectRatio: "custom", keepOrientation: true, format: "webp", quality: 90, prefix: "", suffix: "", useCounter: false, counterStart: 1, counterDigits: 3, useDefaultBaseName: false, defaultBaseName: "", scaleMode: 'cover', objectPosition: 'center center', }; export function useImageConverter() { const [images, setImages] = useState([]); const [settings, setSettings] = useState(initialSettings); const [isConverting, setIsConverting] = useState(false); const [convertingIndex, setConvertingIndex] = useState(null); useEffect(() => { const urls = images.map(img => img.previewUrl); return () => { urls.forEach(url => URL.revokeObjectURL(url)); }; }, [images]); const updateSettings = useCallback((newSettings: Partial) => { setSettings(prev => ({ ...prev, ...newSettings })); }, []); const handleFiles = useCallback((files: FileList | null) => { if (!files || files.length === 0) return; const imageFiles = Array.from(files).filter(file => file.type.startsWith("image/")); if (imageFiles.length === 0) { toast.error("Keine gültigen Bilddateien gefunden."); return; } const newImageFiles: ImageFile[] = imageFiles.map(file => ({ file, previewUrl: URL.createObjectURL(file), filename: settings.useDefaultBaseName && settings.defaultBaseName ? settings.defaultBaseName : file.name.substring(0, file.name.lastIndexOf(".")), })); setImages(prev => [...prev, ...newImageFiles]); toast.success(`${imageFiles.length} Bild(er) hinzugefügt.`); }, [settings.useDefaultBaseName, settings.defaultBaseName]); const handleRemoveImage = useCallback((indexToRemove: number) => { setImages(prev => { const imageToRemove = prev[indexToRemove]; if (imageToRemove) { URL.revokeObjectURL(imageToRemove.previewUrl); } return prev.filter((_, i) => i !== indexToRemove); }); }, []); const handleClearAll = useCallback(() => { setImages([]); updateSettings({ width: initialSettings.width, height: initialSettings.height }); toast.info("Alle Bilder gelöscht."); }, [updateSettings]); const handleFilenameChange = useCallback((index: number, newName: string) => { setImages(prev => { const newImages = [...prev]; if (newImages[index]) { newImages[index].filename = newName; } return newImages; }); }, []); const handleConvertAndDownloadSingle = useCallback(async (index: number) => { setConvertingIndex(index); toast.info(`Starte Konvertierung für ${images[index].filename}...`); try { const imageToConvert = images[index]; const dataUrl = await processImage(imageToConvert, settings); const finalFilename = generateFinalFilename(imageToConvert.filename, settings, index); downloadDataUrl(dataUrl, `${finalFilename}.${settings.format}`); toast.success(`${imageToConvert.filename} erfolgreich exportiert!`); } catch (error) { const message = error instanceof Error ? error.message : "Ein unbekannter Fehler ist aufgetreten."; toast.error(message); } finally { setConvertingIndex(null); } }, [images, settings]); const handleConvertAndDownloadAll = useCallback(async () => { if (images.length === 0) { toast.error("Bitte laden Sie zuerst Bilder hoch."); return; } setIsConverting(true); toast.info(`Starte Konvertierung für ${images.length} Bilder...`); const conversionPromises = images.map(async (image, index) => { try { const dataUrl = await processImage(image, settings); const finalFilename = generateFinalFilename(image.filename, settings, index); downloadDataUrl(dataUrl, `${finalFilename}.${settings.format}`); } catch (error) { const message = error instanceof Error ? error.message : `Verarbeitung von ${image.filename} fehlgeschlagen`; toast.error(message); throw error; } }); try { await Promise.all(conversionPromises); toast.success(`Alle ${images.length} Bilder erfolgreich exportiert!`); } catch (error) { toast.error("Einige Bilder konnten nicht konvertiert werden. Siehe einzelne Fehler."); } finally { setIsConverting(false); } }, [images, settings]); const handleResetSettings = useCallback(() => { setSettings(initialSettings); toast.success("Alle Einstellungen wurden auf ihre Standardwerte zurückgesetzt."); }, []); const handleAspectRatioChange = useCallback((value: string) => { updateSettings({ aspectRatio: value }); if (value === "custom") return; const [w, h] = value.split("/").map(Number); let newWidth: number, newHeight: number; if (w > h) { newWidth = 1000; newHeight = Math.round((1000 * h) / w); } else if (h > w) { newHeight = 1000; newWidth = Math.round((1000 * w) / h); } else { newWidth = 1000; newHeight = 1000; } updateSettings({ width: newWidth, height: newHeight }); }, [updateSettings]); const handleSwapDimensions = useCallback(() => { updateSettings({ width: settings.height, height: settings.width }); }, [settings.height, settings.width, updateSettings]); const handleApplyDefaultBaseNameToAll = useCallback(() => { if (!settings.defaultBaseName) { toast.error("Bitte geben Sie einen Standard-Basisnamen zum Anwenden ein."); return; } if (images.length === 0) { toast.info("Laden Sie zuerst einige Bilder hoch."); return; } setImages(prev => prev.map(img => ({ ...img, filename: settings.defaultBaseName }))); toast.success(`Basisname für alle ${images.length} Bilder auf "${settings.defaultBaseName}" gesetzt.`); }, [images.length, settings.defaultBaseName]); return { images, settings, isConverting, convertingIndex, updateSettings, handleFiles, handleRemoveImage, handleClearAll, handleFilenameChange, handleConvertAndDownloadSingle, handleConvertAndDownloadAll, handleResetSettings, handleAspectRatioChange, handleSwapDimensions, handleApplyDefaultBaseNameToAll, }; }