import { useState, useEffect, useCallback, lazy, Suspense } from 'react'; import ServerWidget from './components/ServerWidget'; import { DEFAULT_CATEGORIES } from './constants'; import { Category, Website, Config } from './types'; import Header from './components/layout/Header'; import EditButton from './components/layout/EditButton'; import ConfigurationButton from './components/layout/ConfigurationButton'; import CategoryGroup from './components/layout/CategoryGroup'; import Wallpaper from './components/Wallpaper'; import { ConfigurationService } from './components/services/ConfigurationService'; const ConfigurationModal = lazy(() => import('./components/ConfigurationModal')); const WebsiteEditModal = lazy(() => import('./components/WebsiteEditModal')); const CategoryEditModal = lazy(() => import('./components/CategoryEditModal')); const getAlignmentClass = (alignment: string) => { switch (alignment) { case 'top': return 'justify-start'; case 'middle': return 'justify-center'; case 'bottom': return 'justify-end'; default: return 'justify-center'; } }; const getHorizontalAlignmentClass = (alignment: string) => { switch (alignment) { case 'left': return 'justify-start'; case 'middle': return 'justify-center'; case 'right': return 'justify-end'; default: return 'justify-center'; } }; const App: React.FC = () => { const [categories, setCategories] = useState(() => { try { const storedCategories = localStorage.getItem('categories'); if (storedCategories) { return JSON.parse(storedCategories); } } catch (error) { console.error('Error parsing categories from localStorage', error); } return DEFAULT_CATEGORIES; }); const [isEditing, setIsEditing] = useState(false); const [isConfigModalOpen, setIsConfigModalOpen] = useState(false); const [editingWebsite, setEditingWebsite] = useState(null); const [addingWebsite, setAddingWebsite] = useState(null); const [editingCategory, setEditingCategory] = useState(null); const [isCategoryModalOpen, setIsCategoryModalOpen] = useState(false); const [config, setConfig] = useState(() => ConfigurationService.loadConfig()); const [wallpaperVersion, setWallpaperVersion] = useState(0); useEffect(() => { ConfigurationService.saveConfig(config); }, [config]); useEffect(() => { try { localStorage.setItem('categories', JSON.stringify(categories)); } catch (error) { console.error('Error saving categories to localStorage', error); } }, [categories]); const handleSaveConfig = useCallback((newConfig: Config) => { setConfig(newConfig); setIsConfigModalOpen(false); }, []); const handleWallpaperChange = useCallback((newConfig: Partial) => { setConfig(prev => ({ ...prev, ...newConfig })); }, []); const handleNextWallpaper = useCallback(() => { const names = config.currentWallpapers; if (names.length === 0) return; try { const state = JSON.parse(localStorage.getItem('wallpaperState') || '{}'); const current = typeof state.currentIndex === 'number' ? state.currentIndex : 0; const safeCurrent = current < 0 || current >= names.length ? 0 : current; const nextIndex = (safeCurrent + 1) % names.length; localStorage.setItem( 'wallpaperState', JSON.stringify({ lastWallpaperChange: new Date().toISOString(), currentIndex: nextIndex, }), ); } catch (error) { console.error('Error advancing wallpaper state', error); } setWallpaperVersion(v => v + 1); }, [config.currentWallpapers]); const handleSaveWebsite = useCallback((website: Partial) => { if (editingWebsite) { const idToUpdate = website.id ?? editingWebsite.id; const newCategories = categories.map(category => ({ ...category, websites: category.websites.map(w => w.id === idToUpdate ? { ...w, ...website, id: idToUpdate } : w ), })); setCategories(newCategories); setEditingWebsite(null); } else if (addingWebsite) { const newWebsite: Website = { id: Date.now().toString(), name: website.name || '', url: website.url || '', icon: website.icon || '', categoryId: addingWebsite.id, }; const newCategories = categories.map(category => category.id === addingWebsite.id ? { ...category, websites: [...category.websites, newWebsite] } : category ); setCategories(newCategories); setAddingWebsite(null); } }, [editingWebsite, addingWebsite, categories]); const handleSaveCategory = useCallback((name: string) => { if (editingCategory) { const newCategories = categories.map(category => category.id === editingCategory.id ? { ...category, name } : category ); setCategories(newCategories); } else { const newCategory: Category = { id: Date.now().toString(), name, websites: [], }; setCategories([...categories, newCategory]); } setEditingCategory(null); setIsCategoryModalOpen(false); }, [editingCategory, categories]); const handleDeleteWebsite = useCallback(() => { if (!editingWebsite) return; const newCategories = categories.map(category => ({ ...category, websites: category.websites.filter(w => w.id !== editingWebsite.id), })); setCategories(newCategories); setEditingWebsite(null); }, [editingWebsite, categories]); const handleDeleteCategory = useCallback(() => { if (!editingCategory) return; const newCategories = categories.filter(c => c.id !== editingCategory.id); setCategories(newCategories); setEditingCategory(null); setIsCategoryModalOpen(false); }, [editingCategory, categories]); const handleMoveWebsite = useCallback((website: Website, direction: 'left' | 'right') => { const categoryIndex = categories.findIndex(cat => cat.websites.some(w => w.id === website.id)); if (categoryIndex === -1) return; const category = categories[categoryIndex]; const websiteIndex = category.websites.findIndex(w => w.id === website.id); if (websiteIndex === -1) return; const newCategories = [...categories]; const newWebsites = [...category.websites]; const [movedWebsite] = newWebsites.splice(websiteIndex, 1); if (direction === 'left') { const newCategoryIndex = (categoryIndex - 1 + categories.length) % categories.length; newCategories[categoryIndex] = { ...category, websites: newWebsites }; const destCategory = newCategories[newCategoryIndex]; const destWebsites = [...destCategory.websites, { ...movedWebsite, categoryId: destCategory.id }]; newCategories[newCategoryIndex] = { ...destCategory, websites: destWebsites }; } else { const newCategoryIndex = (categoryIndex + 1) % categories.length; newCategories[categoryIndex] = { ...category, websites: newWebsites }; const destCategory = newCategories[newCategoryIndex]; const destWebsites = [...destCategory.websites, { ...movedWebsite, categoryId: destCategory.id }]; newCategories[newCategoryIndex] = { ...destCategory, websites: destWebsites }; } setCategories(newCategories); }, [categories]); return (
setIsEditing(!isEditing)} /> setIsConfigModalOpen(true)} />
{categories.map((category) => ( ))} {isEditing && (
)}
{config.serverWidget.enabled && } {(editingWebsite || addingWebsite) && ( { setEditingWebsite(null); setAddingWebsite(null); }} onSave={handleSaveWebsite} onDelete={handleDeleteWebsite} /> )} {isCategoryModalOpen && ( { setEditingCategory(null); setIsCategoryModalOpen(false); }} onSave={handleSaveCategory} onDelete={handleDeleteCategory} /> )} {isConfigModalOpen && ( setIsConfigModalOpen(false)} onSave={handleSaveConfig} onWallpaperChange={handleWallpaperChange} onNextWallpaper={handleNextWallpaper} /> )}
); } export default App;