Skip to content

Commit 40e2e9f

Browse files
Merge pull request #4611 from OneCommunityGlobal/revert-4024-kanishk_fix_for_white_screen_page
Revert "Kanishk attempted fix white screen bug on first visit to pages"
2 parents ab7fac8 + 47aa694 commit 40e2e9f

9 files changed

Lines changed: 234 additions & 638 deletions

File tree

src/actions/authActions.js

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -115,40 +115,10 @@ export const getHeaderData = userId => {
115115
};
116116
};
117117

118-
export const logoutUser = (isCrossTabLogout = false) => dispatch => {
118+
export const logoutUser = () => dispatch => {
119119
// Clear any active force-logout timer before logging out
120120
dispatch(stopForceLogout());
121-
122-
// Only signal other tabs if this is NOT a cross-tab logout
123-
// (to prevent infinite loops when responding to storage events)
124-
if (!isCrossTabLogout) {
125-
// Set a flag to signal other tabs to log out
126-
// This flag will trigger a storage event in other tabs
127-
localStorage.setItem('logoutFlag', 'true');
128-
129-
// Use BroadcastChannel API for more reliable cross-tab communication
130-
if (typeof BroadcastChannel !== 'undefined') {
131-
try {
132-
const channel = new BroadcastChannel('auth_sync');
133-
channel.postMessage({ type: 'logout', timestamp: Date.now() });
134-
// Close the channel after sending
135-
setTimeout(() => channel.close(), 100);
136-
} catch (error) {
137-
// eslint-disable-next-line no-console
138-
console.warn('BroadcastChannel not available:', error);
139-
}
140-
}
141-
142-
// Clear the logout flag after a short delay to allow other tabs to detect it
143-
// We use setTimeout to ensure the storage event fires first
144-
setTimeout(() => {
145-
localStorage.removeItem('logoutFlag');
146-
}, 500);
147-
}
148-
149-
// Remove the token
150121
localStorage.removeItem(tokenKey);
151-
152122
httpService.setjwt(false);
153123
dispatch(setCurrentUser(null));
154124
};

src/components/App.jsx

Lines changed: 24 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable no-undef */
2-
import { Component, useEffect, useState } from 'react';
2+
import { Component, useEffect } from 'react';
33
import { Provider, useSelector } from 'react-redux';
44
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
55
import { 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

3133
function 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-
195141
class 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

Comments
 (0)