@@ -346,6 +346,7 @@ interface UseGenericSettingsResult<T> {
346346 fetchSettings : ( ) => Promise < void > ;
347347 updateSettings : ( updatedSettings : Partial < T > ) => void ;
348348 saveSettings : ( ) => Promise < void > ;
349+ updateSetting : ( key : string , value : any ) => Promise < void > ;
349350}
350351
351352/**
@@ -437,130 +438,12 @@ const useGenericSettings = <T extends SettingsGroupName>(
437438 } ) ;
438439 } , [ groupName ] ) ;
439440
440- const saveSettings = useCallback ( async ( ) => {
441- if ( ! settings ) {
442- console . warn ( 'No settings to save' ) ;
443- return ;
444- }
445-
441+ const updateSetting = useCallback ( async ( key : string , value : any ) => {
446442 try {
447443 setLoading ( true ) ;
448444 setError ( null ) ;
449445
450- // By default, use settings as-is
451- let dataToSave = settings ;
452-
453- // Define settings groups that need special handling
454- const prefixedSettingsMap : Record < string , { prefix : string , formKeys : string [ ] } > = {
455- 'image_moderation' : {
456- prefix : 'image_moderation_' ,
457- formKeys : [
458- 'image_moderation_api' ,
459- 'image_moderation_check_interval' ,
460- 'image_moderation_concurrency' ,
461- 'image_moderation_enabled' ,
462- 'image_moderation_mode' ,
463- 'image_moderation_temp_dir' ,
464- 'image_moderation_threshold' ,
465- 'image_moderation_timeout'
466- ]
467- } ,
468- 'content_filter' : {
469- prefix : 'content_filter_' ,
470- formKeys : [
471- 'content_filter_cache_size' ,
472- 'content_filter_cache_ttl' ,
473- 'content_filter_enabled' ,
474- 'full_text_kinds' // Special case without prefix
475- ]
476- } ,
477- 'ollama' : {
478- prefix : 'ollama_' ,
479- formKeys : [
480- 'ollama_model' ,
481- 'ollama_timeout' ,
482- 'ollama_url'
483- ]
484- } ,
485- 'xnostr' : {
486- prefix : 'xnostr_' ,
487- formKeys : [
488- 'xnostr_browser_path' ,
489- 'xnostr_browser_pool_size' ,
490- 'xnostr_check_interval' ,
491- 'xnostr_concurrency' ,
492- 'xnostr_enabled' ,
493- 'xnostr_temp_dir' ,
494- 'xnostr_update_interval' ,
495- 'xnostr_nitter' ,
496- 'xnostr_verification_intervals'
497- ]
498- } ,
499- 'wallet' : {
500- prefix : 'wallet_' ,
501- formKeys : [
502- 'wallet_api_key' ,
503- 'wallet_name'
504- ]
505- }
506- } ;
507-
508- // Check if this group needs special handling
509- if ( groupName in prefixedSettingsMap ) {
510- console . log ( `Settings from state for ${ groupName } :` , settings ) ;
511- const { prefix, formKeys } = prefixedSettingsMap [ groupName ] ;
512-
513- // First fetch complete settings structure to preserve all values
514- console . log ( `Fetching complete settings before saving ${ groupName } ...` ) ;
515- const fetchResponse = await fetch ( `${ config . baseURL } /api/settings` , {
516- headers : {
517- 'Authorization' : `Bearer ${ token } ` ,
518- } ,
519- } ) ;
520-
521- if ( ! fetchResponse . ok ) {
522- throw new Error ( `Failed to fetch current settings: ${ fetchResponse . status } ` ) ;
523- }
524-
525- const currentData = await fetchResponse . json ( ) ;
526- const currentSettings = extractSettingsForGroup ( currentData . settings , groupName ) || { } ;
527- console . log ( `Current ${ groupName } settings from API:` , currentSettings ) ;
528-
529- // Create a properly prefixed object for the API
530- const prefixedSettings : Record < string , any > = { } ;
531-
532- // Copy all existing settings from the backend with correct prefixes
533- Object . entries ( currentSettings ) . forEach ( ( [ key , value ] ) => {
534- // Special case for content_filter's full_text_kinds which doesn't have prefix
535- if ( groupName === 'content_filter' && key === 'full_text_kinds' ) {
536- prefixedSettings [ key ] = value ;
537- } else {
538- // Skip prefixing if key already has the prefix to avoid double-prefixing
539- const prefixedKey = key . startsWith ( prefix ) ? key : `${ prefix } ${ key } ` ;
540- prefixedSettings [ prefixedKey ] = value ;
541- }
542- } ) ;
543-
544- // Update with changed values from the form
545- const settingsObj = settings as Record < string , any > ;
546-
547- // Update each field that has changed
548- formKeys . forEach ( formKey => {
549- if ( formKey in settingsObj && settingsObj [ formKey ] !== undefined ) {
550- console . log ( `Updating field: ${ formKey } from ${ prefixedSettings [ formKey ] } to ${ settingsObj [ formKey ] } ` ) ;
551- prefixedSettings [ formKey ] = settingsObj [ formKey ] ;
552- }
553- } ) ;
554-
555- console . log ( `Final ${ groupName } settings with prefixed keys for API:` , prefixedSettings ) ;
556- dataToSave = prefixedSettings as unknown as SettingsGroupType < T > ;
557- }
558-
559- console . log ( `Saving ${ groupName } settings:` , dataToSave ) ;
560-
561- // Construct the nested update structure for the new API
562- const nestedUpdate = buildNestedUpdate ( groupName , dataToSave ) ;
563- console . log ( `Nested update structure:` , nestedUpdate ) ;
446+ const nestedUpdate = buildNestedUpdate ( groupName , { [ key ] : value } ) ;
564447
565448 const response = await fetch ( `${ config . baseURL } /api/settings` , {
566449 method : 'POST' ,
@@ -572,7 +455,6 @@ const useGenericSettings = <T extends SettingsGroupName>(
572455 } ) ;
573456
574457 if ( response . status === 401 ) {
575- console . error ( 'Unauthorized access when saving, logging out' ) ;
576458 handleLogout ( ) ;
577459 return ;
578460 }
@@ -581,18 +463,38 @@ const useGenericSettings = <T extends SettingsGroupName>(
581463 throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
582464 }
583465
584- console . log ( `${ groupName } settings saved successfully` ) ;
585-
586- // Optionally refresh settings after save to get any server-side changes
587466 await fetchSettings ( ) ;
467+ } catch ( error ) {
468+ setError ( error instanceof Error ? error : new Error ( String ( error ) ) ) ;
469+ throw error ;
470+ } finally {
471+ setLoading ( false ) ;
472+ }
473+ } , [ groupName , token , handleLogout , fetchSettings ] ) ;
474+
475+ const saveSettings = useCallback ( async ( ) => {
476+ if ( ! settings ) {
477+ console . warn ( 'No settings to save' ) ;
478+ return ;
479+ }
480+
481+ try {
482+ setLoading ( true ) ;
483+ setError ( null ) ;
484+
485+ for ( const [ key , value ] of Object . entries ( settings ) ) {
486+ await updateSetting ( key , value ) ;
487+ }
488+
489+ console . log ( `${ groupName } settings saved successfully` ) ;
588490 } catch ( error ) {
589491 console . error ( `Error saving ${ groupName } settings:` , error ) ;
590492 setError ( error instanceof Error ? error : new Error ( String ( error ) ) ) ;
591493 throw error ;
592494 } finally {
593495 setLoading ( false ) ;
594496 }
595- } , [ groupName , settings , token , handleLogout , fetchSettings ] ) ;
497+ } , [ groupName , settings , updateSetting ] ) ;
596498
597499 // Fetch settings on mount
598500 useEffect ( ( ) => {
@@ -606,6 +508,7 @@ const useGenericSettings = <T extends SettingsGroupName>(
606508 fetchSettings,
607509 updateSettings,
608510 saveSettings,
511+ updateSetting,
609512 } ;
610513} ;
611514
0 commit comments