diff --git a/App.tsx b/App.tsx index eba5291..a0e88b2 100644 --- a/App.tsx +++ b/App.tsx @@ -31,6 +31,7 @@ const App: React.FC = () => { 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); @@ -53,6 +54,27 @@ const App: React.FC = () => { setConfig(prev => ({ ...prev, ...newConfig })); }; + const handleNextWallpaper = () => { + 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); + }; + const handleSaveWebsite = (website: Partial) => { if (editingWebsite) { const idToUpdate = website.id ?? editingWebsite.id; @@ -185,6 +207,7 @@ const App: React.FC = () => { brightness={config.wallpaperBrightness} opacity={config.wallpaperOpacity} wallpaperFrequency={config.wallpaperFrequency} + wallpaperVersion={wallpaperVersion} /> setIsEditing(!isEditing)} /> setIsConfigModalOpen(true)} /> @@ -258,6 +281,7 @@ const App: React.FC = () => { onClose={() => setIsConfigModalOpen(false)} onSave={handleSaveConfig} onWallpaperChange={handleWallpaperChange} + onNextWallpaper={handleNextWallpaper} /> )} diff --git a/components/ConfigurationModal.tsx b/components/ConfigurationModal.tsx index 009a372..4d783a4 100644 --- a/components/ConfigurationModal.tsx +++ b/components/ConfigurationModal.tsx @@ -13,6 +13,7 @@ interface ConfigurationModalProps { onSave: (config: Config) => void; currentConfig: Config; onWallpaperChange: (newConfig: Partial) => void; + onNextWallpaper: () => void; } const ConfigurationModal: React.FC = ({ @@ -20,6 +21,7 @@ const ConfigurationModal: React.FC = ({ onSave, currentConfig, onWallpaperChange, + onNextWallpaper, }) => { const [config, setConfig] = useState(currentConfig); const [activeTab, setActiveTab] = useState('general'); @@ -175,6 +177,7 @@ const ConfigurationModal: React.FC = ({ onAddWallpaper={handleAddWallpaper} onAddWallpaperFile={handleAddWallpaperFile} onDeleteWallpaper={handleDeleteWallpaper} + onNextWallpaper={onNextWallpaper} /> )} {activeTab === 'clock' && ( diff --git a/components/Wallpaper.tsx b/components/Wallpaper.tsx index afe9e13..7b182c5 100644 --- a/components/Wallpaper.tsx +++ b/components/Wallpaper.tsx @@ -10,35 +10,41 @@ interface WallpaperProps { brightness: number; opacity: number; wallpaperFrequency: string; + wallpaperVersion: number; } const getWallpaperUrlByName = async (name: string): Promise => { + if (!name) return undefined; const foundInBase = baseWallpapers.find((w: WallpaperType) => w.name === name); if (foundInBase) { return foundInBase.url || foundInBase.base64; } - const userWallpapers: WallpaperType[] = JSON.parse(localStorage.getItem('userWallpapers') || '[]'); - const foundInUser = userWallpapers.find((w: WallpaperType) => w.name === name); - if (foundInUser) { - try { - const wallpaperData = await getWallpaperFromChromeStorageLocal(name); - if (wallpaperData && wallpaperData.startsWith('http')) { - return wallpaperData; + try { + const storedUserWallpapers: WallpaperType[] = + JSON.parse(localStorage.getItem('userWallpapers') || '[]'); + const foundInUser = storedUserWallpapers.find((w: WallpaperType) => w.name === name); + if (foundInUser) { + try { + const wallpaperData = await getWallpaperFromChromeStorageLocal(name); + if (wallpaperData && wallpaperData.startsWith('http')) { + return wallpaperData; + } + return wallpaperData || undefined; + } catch (error) { + console.error('Error getting wallpaper from chrome storage', error); + return undefined; } - return wallpaperData || undefined; - } catch (error) { - console.error('Error getting wallpaper from chrome storage', error); - return undefined; } + } catch (error) { + console.error('Error reading userWallpapers from localStorage', error); } return undefined; }; -const Wallpaper: React.FC = ({ wallpaperNames, blur, brightness, opacity, wallpaperFrequency }) => { +const Wallpaper: React.FC = ({ wallpaperNames, blur, brightness, opacity, wallpaperFrequency, wallpaperVersion }) => { const [imageUrl, setImageUrl] = useState(undefined); - const [currentWallpaperIndex, setCurrentWallpaperIndex] = useState(0); // Helper to parse wallpaperFrequency string to ms const parseFrequencyToMs = (freq: string): number => { @@ -54,36 +60,61 @@ const Wallpaper: React.FC = ({ wallpaperNames, blur, brightness, useEffect(() => { const updateWallpaper = async () => { - if (wallpaperNames.length === 0) return; - // Read wallpaperState from localStorage + if (wallpaperNames.length === 0) { + setImageUrl(undefined); + localStorage.setItem( + 'wallpaperState', + JSON.stringify({ lastWallpaperChange: new Date().toISOString(), currentIndex: 0 }), + ); + return; + } + const wallpaperState = JSON.parse(localStorage.getItem('wallpaperState') || '{}'); - const lastChange = wallpaperState.lastWallpaperChange ? new Date(wallpaperState.lastWallpaperChange).getTime() : 0; + const lastChange = wallpaperState.lastWallpaperChange + ? new Date(wallpaperState.lastWallpaperChange).getTime() + : 0; const now = Date.now(); const freqMs = parseFrequencyToMs(wallpaperFrequency); - let currentIndex = typeof wallpaperState.currentIndex === 'number' ? wallpaperState.currentIndex : 0; - // If enough time has passed, pick a new wallpaper - if (now - lastChange >= freqMs) { - currentIndex = (currentIndex + 1) % wallpaperNames.length; - localStorage.setItem('wallpaperState', JSON.stringify({ - lastWallpaperChange: new Date().toISOString(), - currentIndex - })); - } else { - // Keep currentIndex in sync with localStorage if not updating - localStorage.setItem('wallpaperState', JSON.stringify({ - lastWallpaperChange: wallpaperState.lastWallpaperChange || new Date().toISOString(), - currentIndex - })); + let storedIndex = + typeof wallpaperState.currentIndex === 'number' ? wallpaperState.currentIndex : 0; + if (storedIndex < 0 || storedIndex >= wallpaperNames.length) storedIndex = 0; + + const shouldRotate = now - lastChange >= freqMs; + let resolvedIndex = shouldRotate + ? (storedIndex + 1) % wallpaperNames.length + : storedIndex; + + const tried = new Set(); + let resolvedUrl: string | undefined; + + for (let i = 0; i < wallpaperNames.length; i++) { + if (tried.has(resolvedIndex)) break; + tried.add(resolvedIndex); + const url = await getWallpaperUrlByName(wallpaperNames[resolvedIndex]); + if (url) { + resolvedUrl = url; + break; + } + resolvedIndex = (resolvedIndex + 1) % wallpaperNames.length; } - setCurrentWallpaperIndex(currentIndex); - const wallpaperName = wallpaperNames[currentIndex]; - const url = await getWallpaperUrlByName(wallpaperName); - setImageUrl(url); + + const nextLastChange = shouldRotate + ? new Date().toISOString() + : wallpaperState.lastWallpaperChange || new Date().toISOString(); + + localStorage.setItem( + 'wallpaperState', + JSON.stringify({ + lastWallpaperChange: nextLastChange, + currentIndex: resolvedIndex, + }), + ); + + setImageUrl(resolvedUrl); }; updateWallpaper(); - // No timer, just run on render/dependency change - }, [wallpaperNames, wallpaperFrequency]); + }, [wallpaperNames, wallpaperFrequency, wallpaperVersion]); if (!imageUrl) return null; diff --git a/components/configuration/ThemeTab.tsx b/components/configuration/ThemeTab.tsx index 3fe782b..754cb8e 100644 --- a/components/configuration/ThemeTab.tsx +++ b/components/configuration/ThemeTab.tsx @@ -11,6 +11,7 @@ interface ThemeTabProps { onAddWallpaper: (name: string, url: string) => Promise; onAddWallpaperFile: (file: File) => Promise; onDeleteWallpaper: (wallpaper: Wallpaper) => Promise; + onNextWallpaper: () => void; } const ThemeTab: React.FC = ({ @@ -22,6 +23,7 @@ const ThemeTab: React.FC = ({ onAddWallpaper, onAddWallpaperFile, onDeleteWallpaper, + onNextWallpaper, }) => { const [newWallpaperName, setNewWallpaperName] = useState(''); const [newWallpaperUrl, setNewWallpaperUrl] = useState(''); @@ -177,7 +179,7 @@ const ThemeTab: React.FC = ({ /> @@ -221,6 +223,24 @@ const ThemeTab: React.FC = ({ )} +
+ +
); };