@@ -9,7 +9,9 @@ import { dbAppSettingsUpdate } from '@services/dexie/settings';
99import { generateDisplayName } from '@utils/common' ;
1010
1111const useCircuitOverseer = ( ) => {
12- const timer = useRef < NodeJS . Timeout > ( ) ;
12+ type FieldKey = 'firstname' | 'lastname' | 'displayname' ;
13+
14+ const saveTimers = useRef < Partial < Record < FieldKey , ReturnType < typeof setTimeout > > > > ( { } ) ;
1315
1416 const settings = useAtomValue ( settingsState ) ;
1517 const fullnameOption = useAtomValue ( fullnameOptionState ) ;
@@ -18,70 +20,116 @@ const useCircuitOverseer = () => {
1820 const [ firstname , setFirstname ] = useState ( '' ) ;
1921 const [ lastname , setLastname ] = useState ( '' ) ;
2022 const [ displayname , setDisplayname ] = useState ( '' ) ;
23+ const [ editing , setEditing ] = useState < Record < FieldKey , boolean > > ( {
24+ firstname : false ,
25+ lastname : false ,
26+ displayname : false ,
27+ } ) ;
28+
29+ const clearTimer = ( key : FieldKey ) => {
30+ const timer = saveTimers . current [ key ] ;
31+ if ( timer ) {
32+ clearTimeout ( timer ) ;
33+ saveTimers . current [ key ] = undefined ;
34+ }
35+ } ;
36+
37+ const scheduleSave = ( key : FieldKey , fn : ( ) => Promise < void > , markCompleteKeys : FieldKey [ ] ) => {
38+ clearTimer ( key ) ;
39+
40+ saveTimers . current [ key ] = setTimeout ( async ( ) => {
41+ saveTimers . current [ key ] = undefined ;
42+ await fn ( ) ;
43+ setEditing ( ( prev ) => {
44+ const next = { ...prev } ;
45+ for ( const field of markCompleteKeys ) {
46+ next [ field ] = false ;
47+ }
48+ return next ;
49+ } ) ;
50+ } , 1000 ) ;
51+ } ;
52+
53+ const markEditing = ( keys : FieldKey [ ] ) => {
54+ setEditing ( ( prev ) => {
55+ const next = { ...prev } ;
56+ for ( const field of keys ) {
57+ next [ field ] = true ;
58+ }
59+ return next ;
60+ } ) ;
61+ } ;
2162
2263 const handleFirstnameChange = ( value : string ) => {
64+ markEditing ( [ 'firstname' , 'displayname' ] ) ;
2365 setFirstname ( value ) ;
2466
2567 const dispName = generateDisplayName ( lastname , value ) ;
2668 setDisplayname ( dispName ) ;
2769 } ;
2870
2971 const handleLastnameChange = ( value : string ) => {
72+ markEditing ( [ 'lastname' , 'displayname' ] ) ;
3073 setLastname ( value ) ;
3174
3275 const dispName = generateDisplayName ( value , firstname ) ;
3376 setDisplayname ( dispName ) ;
3477 } ;
3578
36- const handleDisplaynameChange = ( value : string ) => setDisplayname ( value ) ;
79+ const handleDisplaynameChange = ( value : string ) => {
80+ markEditing ( [ 'displayname' ] ) ;
81+ setDisplayname ( value ) ;
82+ } ;
3783
3884 const handleFirstnameSave = ( ) => {
39- if ( timer . current ) clearTimeout ( timer . current ) ;
40-
41- timer . current = setTimeout ( handleFirstnameSaveDb , 1000 ) ;
85+ scheduleSave ( 'firstname' , handleFirstnameSaveDb , [ 'firstname' , 'displayname' ] ) ;
4286 } ;
4387
4488 const handleLastnameSave = ( ) => {
45- if ( timer . current ) clearTimeout ( timer . current ) ;
46-
47- timer . current = setTimeout ( handleLastnameSaveDb , 1000 ) ;
89+ scheduleSave ( 'lastname' , handleLastnameSaveDb , [ 'lastname' , 'displayname' ] ) ;
4890 } ;
4991
5092 const handleDisplaynameSave = ( ) => {
51- if ( timer . current ) clearTimeout ( timer . current ) ;
52-
53- timer . current = setTimeout ( handleDisplaynameSaveDb , 1000 ) ;
93+ scheduleSave ( 'displayname' , handleDisplaynameSaveDb , [ 'displayname' ] ) ;
5494 } ;
5595
5696 const handleFirstnameSaveDb = async ( ) => {
57- const circuitOverseer = structuredClone (
58- settings . cong_settings . circuit_overseer
97+ const firstnameField = structuredClone (
98+ settings . cong_settings . circuit_overseer . firstname
99+ ) ;
100+ const displayNameField = structuredClone (
101+ settings . cong_settings . circuit_overseer . display_name
59102 ) ;
60103
61- circuitOverseer . firstname . value = firstname ;
62- circuitOverseer . firstname . updatedAt = new Date ( ) . toISOString ( ) ;
104+ firstnameField . value = firstname ;
105+ firstnameField . updatedAt = new Date ( ) . toISOString ( ) ;
63106
64- circuitOverseer . display_name . value = displayname ;
65- circuitOverseer . display_name . updatedAt = new Date ( ) . toISOString ( ) ;
107+ displayNameField . value = displayname ;
108+ displayNameField . updatedAt = new Date ( ) . toISOString ( ) ;
66109
67110 await dbAppSettingsUpdate ( {
68- 'cong_settings.circuit_overseer' : circuitOverseer ,
111+ 'cong_settings.circuit_overseer.firstname' : firstnameField ,
112+ 'cong_settings.circuit_overseer.display_name' : displayNameField ,
69113 } ) ;
70114 } ;
71115
72116 const handleLastnameSaveDb = async ( ) => {
73- const circuitOverseer = structuredClone (
74- settings . cong_settings . circuit_overseer
117+ const lastnameField = structuredClone (
118+ settings . cong_settings . circuit_overseer . lastname
119+ ) ;
120+ const displayNameField = structuredClone (
121+ settings . cong_settings . circuit_overseer . display_name
75122 ) ;
76123
77- circuitOverseer . lastname . value = lastname ;
78- circuitOverseer . lastname . updatedAt = new Date ( ) . toISOString ( ) ;
124+ lastnameField . value = lastname ;
125+ lastnameField . updatedAt = new Date ( ) . toISOString ( ) ;
79126
80- circuitOverseer . display_name . value = displayname ;
81- circuitOverseer . display_name . updatedAt = new Date ( ) . toISOString ( ) ;
127+ displayNameField . value = displayname ;
128+ displayNameField . updatedAt = new Date ( ) . toISOString ( ) ;
82129
83130 await dbAppSettingsUpdate ( {
84- 'cong_settings.circuit_overseer' : circuitOverseer ,
131+ 'cong_settings.circuit_overseer.lastname' : lastnameField ,
132+ 'cong_settings.circuit_overseer.display_name' : displayNameField ,
85133 } ) ;
86134 } ;
87135
@@ -101,10 +149,23 @@ const useCircuitOverseer = () => {
101149 useEffect ( ( ) => {
102150 const co = settings . cong_settings . circuit_overseer ;
103151
104- setFirstname ( co . firstname . value ) ;
105- setLastname ( co . lastname . value ) ;
106- setDisplayname ( co . display_name . value ) ;
107- } , [ settings ] ) ;
152+ setFirstname ( ( prev ) => ( editing . firstname ? prev : co . firstname . value ) ) ;
153+ setLastname ( ( prev ) => ( editing . lastname ? prev : co . lastname . value ) ) ;
154+ setDisplayname ( ( prev ) => ( editing . displayname ? prev : co . display_name . value ) ) ;
155+ } , [ settings , editing ] ) ;
156+
157+ useEffect ( ( ) => {
158+ const timersOnUnmount = saveTimers . current ;
159+
160+ return ( ) => {
161+ ( Object . keys ( timersOnUnmount ) as FieldKey [ ] ) . forEach ( ( key ) => {
162+ const timer = timersOnUnmount [ key ] ;
163+ if ( timer ) {
164+ clearTimeout ( timer ) ;
165+ }
166+ } ) ;
167+ } ;
168+ } , [ ] ) ;
108169
109170 return {
110171 fullnameOption,
0 commit comments