From a9ab5d965192c2310a28bbaf285f6cb1f8d2ce55 Mon Sep 17 00:00:00 2001 From: alexsparkes Date: Tue, 3 Feb 2026 12:01:07 +0000 Subject: [PATCH] feat: enhance BackgroundOptions and PhotoPackSettings for improved offline handling and validation --- .../background/options/BackgroundOptions.jsx | 10 ++- .../options/sections/PhotoPackSettings.jsx | 86 ++++++++++--------- 2 files changed, 53 insertions(+), 43 deletions(-) diff --git a/src/features/background/options/BackgroundOptions.jsx b/src/features/background/options/BackgroundOptions.jsx index ad899747..37c53b4b 100644 --- a/src/features/background/options/BackgroundOptions.jsx +++ b/src/features/background/options/BackgroundOptions.jsx @@ -27,9 +27,12 @@ const BackgroundOptions = memo(({ currentSubSection, onSubSectionChange, section const [backgroundFilter, setBackgroundFilter] = useState( localStorage.getItem('backgroundFilter') || 'none', ); - const [backgroundCategories, setBackgroundCategories] = useState([ - variables.getMessage('modals.main.loading'), - ]); + const [backgroundCategories, setBackgroundCategories] = useState(() => { + if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') { + return [variables.getMessage('modals.update.offline.title')]; + } + return [variables.getMessage('modals.main.loading')]; + }); const [backgroundCategoriesOG, setBackgroundCategoriesOG] = useState([]); const [backgroundAPI, setBackgroundAPI] = useState( localStorage.getItem('backgroundAPI') || 'mue', @@ -109,7 +112,6 @@ const BackgroundOptions = memo(({ currentSubSection, onSubSectionChange, section controllerRef.current = new AbortController(); if (navigator.onLine === false || localStorage.getItem('offlineMode') === 'true') { - setBackgroundCategories([variables.getMessage('modals.update.offline.title')]); return; } diff --git a/src/features/background/options/sections/PhotoPackSettings.jsx b/src/features/background/options/sections/PhotoPackSettings.jsx index ad8ae674..31347d04 100644 --- a/src/features/background/options/sections/PhotoPackSettings.jsx +++ b/src/features/background/options/sections/PhotoPackSettings.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import variables from 'config/variables'; import EventBus from 'utils/eventbus'; import { Dropdown, Text, Switch, Slider, ChipSelect } from 'components/Form/Settings'; @@ -9,10 +9,6 @@ import { refreshAPIPackCache } from 'features/background/api/photoPackAPI'; import { MdRefresh, MdWarning, MdExpandMore, MdExpandLess } from 'react-icons/md'; const PhotoPackSettings = ({ pack }) => { - if (!pack.settings_schema || pack.settings_schema.length === 0) { - return null; - } - const [settings, setSettings] = useState(() => { const saved = localStorage.getItem(`photopack_settings_${pack.id}`); return saved ? JSON.parse(saved) : {}; @@ -23,36 +19,7 @@ const PhotoPackSettings = ({ pack }) => { const [validationErrors, setValidationErrors] = useState([]); const [isExpanded, setIsExpanded] = useState(false); - // Load dynamic options (e.g., categories from API) - useEffect(() => { - pack.settings_schema.forEach((field) => { - if (field.dynamic && field.options_source) { - loadDynamicOptions(field); - } - }); - }, [pack.id]); - - // Validate settings - useEffect(() => { - validateSettings(); - }, [settings]); - - const loadDynamicOptions = async (field) => { - if (field.options_source === 'api:categories') { - try { - const response = await fetch(`${variables.constants.API_URL}/images/categories`); - const categories = await response.json(); - setDynamicOptions((prev) => ({ - ...prev, - [field.key]: categories, - })); - } catch (error) { - console.error('Failed to load categories:', error); - } - } - }; - - const validateSettings = () => { + const validateSettings = useCallback(() => { const errors = []; pack.settings_schema.forEach((field) => { if (field.required && !settings[field.key]) { @@ -73,6 +40,41 @@ const PhotoPackSettings = ({ pack }) => { const filtered = apiPacksReady.filter((id) => id !== pack.id); localStorage.setItem('api_packs_ready', JSON.stringify(filtered)); } + }, [pack.id, pack.settings_schema, settings]); + + // Load dynamic options (e.g., categories from API) + useEffect(() => { + if (!pack.settings_schema || pack.settings_schema.length === 0) { + return; + } + pack.settings_schema.forEach((field) => { + if (field.dynamic && field.options_source) { + loadDynamicOptions(field); + } + }); + }, [pack.id, pack.settings_schema]); + + // Validate settings + useEffect(() => { + if (!pack.settings_schema || pack.settings_schema.length === 0) { + return; + } + validateSettings(); + }, [settings, validateSettings, pack.settings_schema]); + + const loadDynamicOptions = async (field) => { + if (field.options_source === 'api:categories') { + try { + const response = await fetch(`${variables.constants.API_URL}/images/categories`); + const categories = await response.json(); + setDynamicOptions((prev) => ({ + ...prev, + [field.key]: categories, + })); + } catch (error) { + console.error('Failed to load categories:', error); + } + } }; const handleSettingChange = (key, value, secure = false) => { @@ -90,14 +92,14 @@ const PhotoPackSettings = ({ pack }) => { EventBus.emit('refresh', 'background'); }; - const renderField = (field, index) => { + const renderField = (field) => { const value = field.secure && settings[field.key] ? atob(settings[field.key]) : settings[field.key] || field.default; switch (field.type) { - case 'dropdown': + case 'dropdown': { const dropdownItems = field.options.map((opt) => ({ value: opt.value, text: opt.label, @@ -111,8 +113,9 @@ const PhotoPackSettings = ({ pack }) => { onChange={(newValue) => handleSettingChange(field.key, newValue)} /> ); + } - case 'chipselect': + case 'chipselect': { const options = field.dynamic ? dynamicOptions[field.key] || [] : field.options; return ( { onChange={(newValue) => handleSettingChange(field.key, newValue)} /> ); + } case 'text': return ( @@ -164,6 +168,10 @@ const PhotoPackSettings = ({ pack }) => { } }; + if (!pack.settings_schema || pack.settings_schema.length === 0) { + return null; + } + return ( <>
{ {pack.settings_schema.map((field, index) => ( - {renderField(field, index)} + {renderField(field)} ))}