import { ChangeEvent, useEffect, useState } from "react"; import { downloadPresetBackground, openDB, readAllData, writeData } from "../hooks/BackgroundDataLoader"; import presetBackgrounds from "../assets/presetBackgrounds"; import "./BackgroundSelector.css"; import { disableTheme } from "../hooks/ThemeManagment"; // Custom Types and Interfaces export interface Background { id: string; type: string; blob: Blob; url?: string; previewUrl?: string; isPreset?: boolean; isDownloaded?: boolean; } interface BackgroundSelectorProps { selectedType: "background" | "theme"; setSelectedType: (type: "background" | "theme") => void; isEditMode: boolean; } export default function BackgroundSelector({ selectedType, setSelectedType, isEditMode }: BackgroundSelectorProps) { const [backgrounds, setBackgrounds] = useState([]); const [selectedBackground, setSelectedBackground] = useState(localStorage.getItem('selectedBackground')); const [downloadedPresetIds, setDownloadedPresetIds] = useState([]); const [downloadProgress, setDownloadProgress] = useState>({}); const handleFileChange = async (e: ChangeEvent): Promise => { const file = e.target.files?.[0]; if (!file) return; const fileId = `${Date.now()}-${file.name}`; const fileType = file.type.split('/')[0]; const blob = new Blob([file], { type: file.type }); await writeData(fileId, fileType, blob); setBackgrounds(prev => [...prev, { id: fileId, type: fileType, blob, url: URL.createObjectURL(blob) }]); }; const loadBackgrounds = async (): Promise => { const data = await readAllData(); const dataWithUrls = data.map(bg => ({ ...bg, url: URL.createObjectURL(bg.blob) })); // Update downloaded preset IDs setDownloadedPresetIds(data.map(bg => bg.id)); setBackgrounds(dataWithUrls); }; const handlePresetClick = async (bg: Background): Promise => { if (bg.isPreset) { // Check if already exists in IndexedDB or is currently being downloaded const existingBackgrounds = await readAllData(); const alreadyExists = existingBackgrounds.some(ebg => ebg.id === bg.id) || downloadProgress[bg.id] !== undefined; if (!alreadyExists) { setDownloadProgress(prev => ({ ...prev, [bg.id]: 0 })); const downloadedBg = await downloadPresetBackground(bg, progress => { setDownloadProgress(prev => ({ ...prev, [bg.id]: progress })); }); setDownloadProgress(prev => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { [bg.id]: _, ...rest } = prev; return rest; }); await writeData(downloadedBg.id, downloadedBg.type, downloadedBg.blob); setBackgrounds(prev => [...prev, downloadedBg]); setDownloadedPresetIds(prev => [...prev, downloadedBg.id]); } selectBackground(bg.id); } }; const selectBackground = (fileId: string): void => { disableTheme(); setSelectedType('background'); setSelectedBackground(fileId); localStorage.setItem('selectedBackground', fileId); }; const deleteBackground = async (fileId: string): Promise => { const db = await openDB(); const tx = db.transaction('backgrounds', 'readwrite'); const store = tx.objectStore('backgrounds'); store.delete(fileId); setBackgrounds(prev => prev.filter(bg => bg.id !== fileId)); // Check if the background being deleted is currently selected if (fileId === selectedBackground) { selectNoBackground(); // Disable the current background } }; const selectNoBackground = (): void => { setSelectedType('background'); disableTheme(); setSelectedBackground(null); localStorage.removeItem('selectedBackground'); }; const calcCircumference = (radius: number) => 2 * Math.PI * radius; useEffect(() => { loadBackgrounds(); }, []); return ( <>

Images

{/* Image uploader swatch */}
{/* Plus icon */} 
{backgrounds.filter(bg => bg.type === 'image').map(bg => (
selectBackground(bg.id)} className={`relative w-16 h-16 cursor-pointer rounded-xl transition ring dark:ring-white ring-zinc-300 ${isEditMode ? 'animate-shake' : ''} ${selectedBackground === bg.id && selectedType === "background" ? 'dark:ring-2 ring-4' : 'ring-0'}`}> {isEditMode && (
deleteBackground(bg.id)}>
)} swatch
))} {backgrounds.concat(presetBackgrounds as Background[]).filter(bg => bg.type === 'image' && bg.isPreset && !bg.isDownloaded && !downloadedPresetIds.includes(bg.id)).map(bg => ( ))}

Videos

{/* Video uploader swatch */}
{/* Plus icon */} 
{backgrounds.filter(bg => bg.type === 'video').map(bg => (
selectBackground(bg.id)} className={`relative w-16 h-16 cursor-pointer rounded-xl transition ring dark:ring-white ring-zinc-300 ${isEditMode ? 'animate-shake' : ''} ${selectedBackground === bg.id && selectedType === "background" ? 'dark:ring-2 ring-4' : 'ring-0'}`}> {isEditMode && (
deleteBackground(bg.id)}>
)}
))} {backgrounds.concat(presetBackgrounds as Background[]).filter(bg => bg.type === 'video' && bg.isPreset && !bg.isDownloaded && !downloadedPresetIds.includes(bg.id)).map(bg => (
handlePresetClick(bg)} className={`relative w-16 h-16 transition cursor-pointer rounded-xl duration-300 ${ isEditMode ? 'opacity-0 pointer-events-none hidden' : 'opacity-100'}`}> {bg.isPreset && downloadProgress[bg.id] !== undefined && (
)}
{downloadProgress[bg.id] === undefined ? '' : ''}
))}
); }