@@ -4,7 +4,7 @@ import { cn } from '@/lib/utils'
44import { useGetSettings , useModifySettings } from '@/service/api'
55import { useQueryClient } from '@tanstack/react-query'
66import { Bell , Database , ListTodo , LucideIcon , MessageCircle , Palette , Send , Settings as SettingsIcon , Webhook } from 'lucide-react'
7- import { createContext , useContext } from 'react'
7+ import { createContext , useContext , useMemo , useCallback } from 'react'
88import { useTranslation } from 'react-i18next'
99import { Outlet , useLocation , useNavigate } from 'react-router'
1010import { toast } from 'sonner'
@@ -134,80 +134,89 @@ export default function Settings() {
134134 } )
135135
136136 // Wrapper function to filter data based on active tab (only for sudo admins)
137- const handleUpdateSettings = ( data : any ) => {
138- if ( ! is_sudo ) return // No-op for non-sudo admins
139-
140- let filteredData : any = { }
141-
142- // Only include data relevant to the active tab
143- switch ( activeTab ) {
144- case 'notifications' :
145- if ( data . data ) {
146- // If data is already wrapped, use it as is
147- filteredData = data
148- } else {
149- // Wrap notification data in the expected format
150- filteredData = {
151- data : {
152- notification_enable : data . notification_enable ,
153- notification_settings : data . notification_settings ,
154- } ,
137+ const handleUpdateSettings = useCallback (
138+ ( data : any ) => {
139+ if ( ! is_sudo ) return // No-op for non-sudo admins
140+
141+ let filteredData : any = { }
142+
143+ // Only include data relevant to the active tab
144+ switch ( activeTab ) {
145+ case 'notifications' :
146+ if ( data . data ) {
147+ // If data is already wrapped, use it as is
148+ filteredData = data
149+ } else {
150+ // Wrap notification data in the expected format
151+ filteredData = {
152+ data : {
153+ notification_enable : data . notification_enable ,
154+ notification_settings : data . notification_settings ,
155+ } ,
156+ }
155157 }
156- }
157- break
158- case 'subscriptions' :
159- if ( data . subscription ) {
160- // Wrap subscription data in the expected format
161- filteredData = {
162- data : {
163- subscription : data . subscription ,
164- } ,
158+ break
159+ case 'subscriptions' :
160+ if ( data . subscription ) {
161+ // Wrap subscription data in the expected format
162+ filteredData = {
163+ data : {
164+ subscription : data . subscription ,
165+ } ,
166+ }
167+ } else {
168+ // If data is already wrapped, use it as is
169+ filteredData = data
165170 }
166- } else {
167- // If data is already wrapped, use it as is
168- filteredData = data
169- }
170- break
171- case 'telegram' :
172- // Add telegram specific filtering if needed
173- filteredData = { data : data }
174- break
175- case 'discord' :
176- // Add discord specific filtering if needed
177- filteredData = { data : data }
178- break
179- case 'webhook' :
180- // Add webhook specific filtering if needed
181- filteredData = { data : data }
182- break
183- case 'cleanup' :
184- // Add cleanup specific filtering if needed
185- filteredData = { data : data }
186- break
187- case 'theme' :
188- // Theme settings are client-side only, no API call needed
189- return
190- default :
191- filteredData = { data : data }
192- }
193-
194- updateSettings ( filteredData )
195- }
171+ break
172+ case 'telegram' :
173+ // Add telegram specific filtering if needed
174+ filteredData = { data : data }
175+ break
176+ case 'discord' :
177+ // Add discord specific filtering if needed
178+ filteredData = { data : data }
179+ break
180+ case 'webhook' :
181+ // Add webhook specific filtering if needed
182+ filteredData = { data : data }
183+ break
184+ case 'cleanup' :
185+ // Add cleanup specific filtering if needed
186+ filteredData = { data : data }
187+ break
188+ case 'theme' :
189+ // Theme settings are client-side only, no API call needed
190+ return
191+ default :
192+ filteredData = { data : data }
193+ }
196194
197- const settingsContextValue : SettingsContextType = {
198- settings : is_sudo ? settings : { } , // Non-sudo admins don't need settings data
199- isLoading : is_sudo ? isLoading : false ,
200- error : is_sudo ? error : null ,
201- updateSettings : is_sudo ? handleUpdateSettings : ( ) => { } , // No-op for non-sudo admins
202- isSaving : is_sudo ? isSaving : false ,
203- }
195+ updateSettings ( filteredData )
196+ } ,
197+ [ is_sudo , activeTab , updateSettings ] ,
198+ )
199+
200+ // Memoize context value to ensure stability during HMR
201+ const settingsContextValue : SettingsContextType = useMemo (
202+ ( ) => ( {
203+ settings : is_sudo ? ( settings || { } ) : { } , // Non-sudo admins don't need settings data
204+ isLoading : is_sudo ? isLoading : false ,
205+ error : is_sudo ? error : null ,
206+ updateSettings : is_sudo ? handleUpdateSettings : ( ) => { } , // No-op for non-sudo admins
207+ isSaving : is_sudo ? isSaving : false ,
208+ } ) ,
209+ [ is_sudo , settings , isLoading , error , isSaving , handleUpdateSettings ] ,
210+ )
204211
205212 // Generate tutorial URL for the current settings tab
206213 const getTutorialUrl = ( ) => {
207214 const locale = i18n . language || 'en'
208215 return `https://docs.pasarguard.org/${ locale } /panel/settings`
209216 }
210217
218+ // Always render the provider to ensure context is available for child routes
219+ // This prevents issues during HMR when components might render before parent is ready
211220 return (
212221 < SettingsContext . Provider value = { settingsContextValue } >
213222 < div className = "flex w-full flex-col items-start gap-0" >
0 commit comments