11/* eslint-disable no-undef */
2- import { Component , useEffect , useState } from 'react' ;
2+ import { Component , useEffect } from 'react' ;
33import { Provider , useSelector } from 'react-redux' ;
44import { BrowserRouter as Router , useLocation } from 'react-router-dom' ;
55import { PersistGate } from 'redux-persist/integration/react' ;
@@ -25,8 +25,10 @@ const queryClient = new QueryClient({
2525 } ,
2626} ) ;
2727
28- // Move auth initialization to after store is ready
29- let authInitialized = false ;
28+ // Check for token
29+ if ( process . env . NODE_ENV !== 'test' ) {
30+ initAuth ( ) ;
31+ }
3032
3133function UpdateDocumentTitle ( ) {
3234 const location = useLocation ( ) ;
@@ -124,228 +126,36 @@ function UpdateDocumentTitle() {
124126 try {
125127 initMessagingSocket ( token ) ;
126128 } catch ( error ) {
127- // eslint-disable-next-line no- console
128- console . error ( 'WebSocket initialization failed:' , error ) ;
129+ // console.error('WebSocket initialization failed:', error);
130+ return error ;
129131 }
130132 }
133+ // else {
134+ // // console.warn('No auth token found for WebSocket connection');
135+ // }
131136 } , [ ] ) ;
132137
133138 return null ;
134139}
135140
136- // Error Boundary Component
137- class ErrorBoundary extends Component {
138- constructor ( props ) {
139- super ( props ) ;
140- this . state = { hasError : false , error : null , errorInfo : null } ;
141- }
142-
143- static getDerivedStateFromError ( error ) {
144- return { hasError : true } ;
145- }
146-
147- componentDidCatch ( error , errorInfo ) {
148- this . setState ( {
149- error : error ,
150- errorInfo : errorInfo ,
151- } ) ;
152-
153- // Log the error
154- logger . logError ( error ) ;
155-
156- // Log additional error info for debugging
157- console . error ( 'Error Boundary caught an error:' , error , errorInfo ) ;
158- }
159-
160- render ( ) {
161- if ( this . state . hasError ) {
162- return (
163- < div
164- className = "d-flex justify-content-center align-items-center"
165- style = { { minHeight : '100vh' } }
166- >
167- < div className = "text-center" >
168- < h2 > Something went wrong</ h2 >
169- < p > We are sorry, but something unexpected happened. Please try refreshing the page.</ p >
170- < button
171- className = "btn btn-primary"
172- onClick = { ( ) => {
173- this . setState ( { hasError : false , error : null , errorInfo : null } ) ;
174- window . location . reload ( ) ;
175- } }
176- >
177- Refresh Page
178- </ button >
179- { process . env . NODE_ENV === 'development' && this . state . error && (
180- < details style = { { whiteSpace : 'pre-wrap' , marginTop : '20px' } } >
181- < summary > Error Details (Development)</ summary >
182- < p > { this . state . error . toString ( ) } </ p >
183- < p > { this . state . errorInfo . componentStack } </ p >
184- </ details >
185- ) }
186- </ div >
187- </ div >
188- ) ;
189- }
190-
191- return this . props . children ;
192- }
193- }
194-
195141class App extends Component {
196142 constructor ( props ) {
197143 super ( props ) ;
198144 this . state = {
199- storeReady : false ,
200- authInitialized : false ,
201145 hasError : false ,
202146 error : null ,
203147 errorInfo : null ,
204- } ;
205- this . isLoggingOut = false ; // Flag to prevent infinite logout loops
206- this . broadcastChannel = null ; // BroadcastChannel for cross-tab communication
207- this . tokenCheckInterval = null ; // Interval for periodic token checking
208- this . lastTokenValue = null ; // Track last known token value
148+ } ; // Moving state initialization into constructor as per linting rule.
209149 }
210150
211- componentDidMount ( ) {
212- // Wait for store to be ready before initializing auth
213- this . setState ( { storeReady : true } ) ;
214-
215- // Initialize auth after a short delay to ensure store is fully ready
216- setTimeout ( ( ) => {
217- if ( process . env . NODE_ENV !== 'test' && ! authInitialized ) {
218- try {
219- initAuth ( ) ;
220- authInitialized = true ;
221- this . setState ( { authInitialized : true } ) ;
222- // Store initial token value
223- this . lastTokenValue = localStorage . getItem ( 'token' ) ;
224- } catch ( error ) {
225- console . error ( 'Error initializing auth:' , error ) ;
226- logger . logError ( error ) ;
227- }
228- }
229- } , 100 ) ;
230-
231- // Listen for storage events to sync auth state between tabs
232- this . handleStorageChange = this . handleStorageChange . bind ( this ) ;
233- window . addEventListener ( 'storage' , this . handleStorageChange ) ;
234-
235- // Use BroadcastChannel API for more reliable cross-tab communication
236- if ( typeof BroadcastChannel !== 'undefined' ) {
237- this . broadcastChannel = new BroadcastChannel ( 'auth_sync' ) ;
238- this . broadcastChannel . onmessage = event => {
239- if ( event . data && event . data . type === 'logout' ) {
240- this . handleCrossTabLogout ( ) ;
241- }
242- } ;
243- }
244-
245- // Periodic token check as fallback (checks every 500ms)
246- // This ensures we catch logout even if storage events don't fire
247- this . tokenCheckInterval = setInterval ( ( ) => {
248- this . checkTokenStatus ( ) ;
249- } , 500 ) ;
250- }
251-
252- componentWillUnmount ( ) {
253- // Clean up storage event listener
254- if ( this . handleStorageChange ) {
255- window . removeEventListener ( 'storage' , this . handleStorageChange ) ;
256- }
257- // Clean up BroadcastChannel
258- if ( this . broadcastChannel ) {
259- this . broadcastChannel . close ( ) ;
260- }
261- // Clean up token check interval
262- if ( this . tokenCheckInterval ) {
263- clearInterval ( this . tokenCheckInterval ) ;
264- }
265- }
266-
267- checkTokenStatus ( ) {
268- // Periodic check to detect if token was removed in another tab
269- // This is a fallback for when storage events don't fire reliably
270- if ( this . isLoggingOut ) return ; // Skip if already logging out
271-
272- const currentToken = localStorage . getItem ( 'token' ) ;
273- const wasAuthenticated = this . lastTokenValue !== null ;
274- const isNowUnauthenticated = currentToken === null ;
275-
276- // If we had a token before and now it's gone, we were logged out
277- if ( wasAuthenticated && isNowUnauthenticated ) {
278- this . handleCrossTabLogout ( ) ;
279- }
280-
281- // Update last known token value
282- this . lastTokenValue = currentToken ;
283- }
284-
285- handleCrossTabLogout ( ) {
286- // Handle cross-tab logout (called from storage events, BroadcastChannel, or token check)
287- if ( ! this . isLoggingOut ) {
288- this . isLoggingOut = true ;
289-
290- // Immediately redirect to login - don't wait for Redux state update
291- // This ensures the user sees the login page right away
292- if (
293- window . location . pathname !== '/login' &&
294- ! window . location . pathname . startsWith ( '/login' ) &&
295- ! window . location . pathname . startsWith ( '/forgotpassword' )
296- ) {
297- window . location . href = '/login' ;
298- return ; // Exit early, let the redirect handle cleanup
299- }
300-
301- // If already on login page, just update Redux state
302- if ( store && store . dispatch ) {
303- const { logoutUser } = require ( '../actions/authActions' ) ;
304- // Use a version of logoutUser that doesn't set the flag to prevent loops
305- store . dispatch ( logoutUser ( true ) ) ; // Pass true to indicate cross-tab logout
306- }
307-
308- // Reset flag after a delay
309- setTimeout ( ( ) => {
310- this . isLoggingOut = false ;
311- } , 1000 ) ;
312- }
313- }
314-
315- handleStorageChange ( event ) {
316- // Sync auth state when localStorage changes in other tabs
317- if ( event . key === 'token' || event . key === 'authToken' ) {
318- // Handle login sync - token was added in another tab
319- if ( event . newValue && ! authInitialized ) {
320- try {
321- initAuth ( ) ;
322- authInitialized = true ;
323- this . setState ( { authInitialized : true } ) ;
324- } catch ( error ) {
325- console . error ( 'Error syncing auth from storage event:' , error ) ;
326- }
327- }
328- // Handle logout sync - token was removed in another tab
329- if ( event . newValue === null && event . oldValue !== null && ! this . isLoggingOut ) {
330- // Token was removed in another tab, log out this tab
331- this . handleCrossTabLogout ( ) ;
332- }
333- }
334- // Also check for explicit logout flag
335- if ( event . key === 'logoutFlag' ) {
336- if ( event . newValue === 'true' && ! this . isLoggingOut ) {
337- // Another tab triggered logout, log out this tab
338- this . handleCrossTabLogout ( ) ;
339- // Clear the flag
340- localStorage . removeItem ( 'logoutFlag' ) ;
341- }
342- }
151+ static getDerivedStateFromError ( error ) {
152+ // Update state so the next render will show the fallback UI
153+ return { hasError : true } ;
343154 }
344155
345156 componentDidCatch ( error , errorInfo ) {
157+ // Log the error
346158 logger . logError ( error ) ;
347- // eslint-disable-next-line no-console
348- console . error ( 'App component caught an error:' , error , errorInfo ) ;
349159
350160 // Update state with error details
351161 this . setState ( {
@@ -416,17 +226,15 @@ class App extends Component {
416226 return (
417227 < Provider store = { store } >
418228 < PersistGate loading = { < Loading /> } persistor = { persistor } >
419- < ErrorBoundary >
420- < QueryClientProvider client = { queryClient } >
421- < ModalProvider >
422- < Router >
423- < ThemeManager />
424- < UpdateDocumentTitle />
425- { routes }
426- </ Router >
427- </ ModalProvider >
428- </ QueryClientProvider >
429- </ ErrorBoundary >
229+ < QueryClientProvider client = { queryClient } >
230+ < ModalProvider >
231+ < Router >
232+ < ThemeManager />
233+ < UpdateDocumentTitle />
234+ { routes }
235+ </ Router >
236+ </ ModalProvider >
237+ </ QueryClientProvider >
430238 </ PersistGate >
431239 </ Provider >
432240 ) ;
0 commit comments