react-session.manager.sk 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.LICENSE.txt +9 -0
- package/package.json +1 -1
- package/src/components/ErrorBoundary.js +143 -0
- package/src/components/ThemeWrapper.js +36 -0
- package/src/index.js +404 -82
package/src/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React, { createContext, useState, useEffect, useCallback } from "react";
|
|
2
2
|
import versionCompare from "./components/versionCompare";
|
|
3
3
|
import getDeviceFingerprint from "./components/FingerPrint";
|
|
4
|
+
import ErrorBoundary from "./components/ErrorBoundary";
|
|
5
|
+
import ThemeWrapper from "./components/ThemeWrapper";
|
|
4
6
|
// Notifications
|
|
5
7
|
import { ToastContainer, toast } from "react-toastify";
|
|
6
8
|
import "react-toastify/dist/ReactToastify.css";
|
|
@@ -40,9 +42,26 @@ const SessionManagerProvider = ({
|
|
|
40
42
|
|
|
41
43
|
useEffect(() => {
|
|
42
44
|
if (!deviceUID) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
try {
|
|
46
|
+
const uid = getDeviceFingerprint();
|
|
47
|
+
if (uid) {
|
|
48
|
+
setDeviceUID(uid);
|
|
49
|
+
localStorage.setItem("deviceUID", uid);
|
|
50
|
+
AuthenticatedAxiosObject.defaults.headers.common["deviceUID"] = uid;
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error("Failed to generate device fingerprint:", err);
|
|
54
|
+
// Generate a fallback UUID
|
|
55
|
+
const fallbackUID =
|
|
56
|
+
"fallback-" +
|
|
57
|
+
Date.now() +
|
|
58
|
+
"-" +
|
|
59
|
+
Math.random().toString(36).substr(2, 9);
|
|
60
|
+
setDeviceUID(fallbackUID);
|
|
61
|
+
localStorage.setItem("deviceUID", fallbackUID);
|
|
62
|
+
AuthenticatedAxiosObject.defaults.headers.common["deviceUID"] =
|
|
63
|
+
fallbackUID;
|
|
64
|
+
}
|
|
46
65
|
}
|
|
47
66
|
}, [deviceUID, AuthenticatedAxiosObject]);
|
|
48
67
|
|
|
@@ -60,7 +79,7 @@ const SessionManagerProvider = ({
|
|
|
60
79
|
setTimeout(() => {
|
|
61
80
|
refreshToken()
|
|
62
81
|
.then((data) => {
|
|
63
|
-
if (data
|
|
82
|
+
if (data?.refreshed) {
|
|
64
83
|
const token = `Bearer ${data.access_token}`;
|
|
65
84
|
setCurrent(token);
|
|
66
85
|
AuthenticatedAxiosObject.defaults.headers.common[
|
|
@@ -71,10 +90,35 @@ const SessionManagerProvider = ({
|
|
|
71
90
|
}
|
|
72
91
|
})
|
|
73
92
|
.catch((err) => {
|
|
74
|
-
console.
|
|
75
|
-
|
|
93
|
+
console.error("Token refresh failed:", err);
|
|
94
|
+
|
|
95
|
+
// Track auth failures
|
|
96
|
+
if (err?.code === "ERR_NETWORK" || err?.code === "ERR_CONNECTION_REFUSED") {
|
|
97
|
+
setAuthError(true);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Clear invalid tokens
|
|
76
101
|
localStorage.removeItem("Authorization");
|
|
77
102
|
sessionStorage.removeItem("Authorization");
|
|
103
|
+
setCurrent("");
|
|
104
|
+
setCurrentLoggedIn(false);
|
|
105
|
+
|
|
106
|
+
// Only show toast for actual network/server errors, not auth failures
|
|
107
|
+
if (err?.code === "ERR_NETWORK" || err?.response?.status >= 500) {
|
|
108
|
+
// Don't spam with toasts during retries
|
|
109
|
+
const lastToast = sessionStorage.getItem("lastRefreshErrorToast");
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
if (!lastToast || now - parseInt(lastToast) > 30000) { // 30 seconds
|
|
112
|
+
toast.error(
|
|
113
|
+
"Unable to refresh your session. Please login again.",
|
|
114
|
+
{
|
|
115
|
+
toastId: "token-refresh-error",
|
|
116
|
+
icon: <Logout />,
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
sessionStorage.setItem("lastRefreshErrorToast", now.toString());
|
|
120
|
+
}
|
|
121
|
+
}
|
|
78
122
|
});
|
|
79
123
|
}, 100);
|
|
80
124
|
},
|
|
@@ -87,8 +131,8 @@ const SessionManagerProvider = ({
|
|
|
87
131
|
|
|
88
132
|
if (local_auth) fromPrevious(local_auth, true);
|
|
89
133
|
else if (sess_auth) fromPrevious(sess_auth, false);
|
|
90
|
-
|
|
91
|
-
}, [
|
|
134
|
+
// No cleanup needed here since fromPrevious doesn't create intervals
|
|
135
|
+
}, [fromPrevious]);
|
|
92
136
|
|
|
93
137
|
// Wrap setHeader to store new header names in localStorage
|
|
94
138
|
const setHeader = (header) => {
|
|
@@ -99,18 +143,42 @@ const SessionManagerProvider = ({
|
|
|
99
143
|
const [isAdmin, setIsAdmin] = useState(false);
|
|
100
144
|
const [userInfo, setUserInfo] = useState({});
|
|
101
145
|
const [loadingUser, setLoadingUser] = useState(true);
|
|
146
|
+
const [authError, setAuthError] = useState(false);
|
|
102
147
|
|
|
103
148
|
useEffect(() => {
|
|
104
149
|
setTimeout(() => {
|
|
105
150
|
userLoader()
|
|
106
151
|
.then((res) => {
|
|
107
|
-
const data = res
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
152
|
+
const data = res?.data;
|
|
153
|
+
if (data) {
|
|
154
|
+
setCurrentLoggedIn(data.logged_in);
|
|
155
|
+
setIsAdmin(data.is_admin);
|
|
156
|
+
setUserInfo(data.Info || {});
|
|
157
|
+
setAuthError(false);
|
|
158
|
+
}
|
|
111
159
|
})
|
|
112
160
|
.catch((err) => {
|
|
113
|
-
console.
|
|
161
|
+
console.error("User data loading failed:", err);
|
|
162
|
+
|
|
163
|
+
// Check if this is a persistent auth error
|
|
164
|
+
if (err?.code === "ERR_NETWORK" || err?.code === "ERR_CONNECTION_REFUSED") {
|
|
165
|
+
setAuthError(true);
|
|
166
|
+
// Don't show toast immediately, let retries happen first
|
|
167
|
+
} else if (err?.response?.status !== 401 && err?.response?.status !== 403) {
|
|
168
|
+
if (err?.response?.status >= 500) {
|
|
169
|
+
toast.error(
|
|
170
|
+
"Unable to load user information. Some features may be limited.",
|
|
171
|
+
{
|
|
172
|
+
toastId: "user-load-error",
|
|
173
|
+
icon: <GppBad />,
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Set safe defaults
|
|
179
|
+
setCurrentLoggedIn(false);
|
|
180
|
+
setIsAdmin(false);
|
|
181
|
+
setUserInfo({});
|
|
114
182
|
})
|
|
115
183
|
.finally(() => {
|
|
116
184
|
setLoadingUser(false);
|
|
@@ -138,18 +206,23 @@ const SessionManagerProvider = ({
|
|
|
138
206
|
clearInterval(interval);
|
|
139
207
|
}
|
|
140
208
|
return () => clearInterval(interval);
|
|
141
|
-
}, [
|
|
209
|
+
}, [fromPrevious, currentLoggin, refreshTimer]);
|
|
142
210
|
|
|
143
211
|
useEffect(() => {
|
|
144
212
|
const customInterceptor = () => {
|
|
145
213
|
AuthenticatedAxiosObject.interceptors.response.use(
|
|
146
214
|
(response) => {
|
|
215
|
+
// Reset connection failure count on successful response
|
|
216
|
+
sessionStorage.setItem("connectionFailures", "0");
|
|
147
217
|
return response;
|
|
148
218
|
},
|
|
149
219
|
(error) => {
|
|
220
|
+
console.error("Axios interceptor caught error:", error);
|
|
221
|
+
|
|
222
|
+
// Handle session expiry (455)
|
|
150
223
|
if (error?.response?.status === 455) {
|
|
151
224
|
try {
|
|
152
|
-
const status = error.response.data
|
|
225
|
+
const status = error.response.data?.logged_in || false;
|
|
153
226
|
if (!status) {
|
|
154
227
|
console.log("Session ended");
|
|
155
228
|
setCurrentLoggedIn(false);
|
|
@@ -157,50 +230,91 @@ const SessionManagerProvider = ({
|
|
|
157
230
|
"Authorization"
|
|
158
231
|
] = ``;
|
|
159
232
|
toast.info(
|
|
160
|
-
"Your session
|
|
233
|
+
"Your session is no longer valid, please login again.",
|
|
161
234
|
{ toastId: "Forced_log_out", icon: <Logout /> }
|
|
162
235
|
);
|
|
163
236
|
}
|
|
164
237
|
} catch (err) {
|
|
165
|
-
|
|
238
|
+
console.error("Error handling session expiry:", err);
|
|
239
|
+
// Don't throw here - just log and continue
|
|
166
240
|
}
|
|
241
|
+
return Promise.reject(error);
|
|
167
242
|
}
|
|
243
|
+
|
|
244
|
+
// Handle version mismatch (426)
|
|
168
245
|
if (error?.response?.status === 426) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
let reloads = sessionStorage.getItem("appReloads") || 0;
|
|
175
|
-
if (reloads < 2) {
|
|
176
|
-
sessionStorage.setItem("appReloads", reloads + 1);
|
|
177
|
-
setTimeout(() => {
|
|
178
|
-
window.location.reload();
|
|
179
|
-
}, 1000 * reloads);
|
|
180
|
-
} else {
|
|
181
|
-
toast.warning(
|
|
182
|
-
"The application needs to be updated please wait for some time then reload the page.",
|
|
183
|
-
{
|
|
184
|
-
toastId: "appReloadError",
|
|
185
|
-
icon: <Update />,
|
|
186
|
-
}
|
|
246
|
+
try {
|
|
247
|
+
sessionStorage.setItem("appVersionOld", true);
|
|
248
|
+
sessionStorage.setItem(
|
|
249
|
+
"requiredVersion",
|
|
250
|
+
error.response.data?.minVersion || "unknown"
|
|
187
251
|
);
|
|
252
|
+
let reloads = parseInt(
|
|
253
|
+
sessionStorage.getItem("appReloads") || "0"
|
|
254
|
+
);
|
|
255
|
+
if (reloads < 2) {
|
|
256
|
+
sessionStorage.setItem("appReloads", (reloads + 1).toString());
|
|
257
|
+
setTimeout(() => {
|
|
258
|
+
window.location.reload();
|
|
259
|
+
}, 1000 * reloads);
|
|
260
|
+
} else {
|
|
261
|
+
toast.warning(
|
|
262
|
+
"The application needs to be updated. Please wait for some time then reload the page.",
|
|
263
|
+
{
|
|
264
|
+
toastId: "appReloadError",
|
|
265
|
+
icon: <Update />,
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
} catch (err) {
|
|
270
|
+
console.error("Error handling version mismatch:", err);
|
|
188
271
|
}
|
|
272
|
+
return Promise.reject(error);
|
|
189
273
|
}
|
|
190
274
|
|
|
275
|
+
// Handle network errors and connection issues
|
|
191
276
|
if (!error?.response?.status) {
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
277
|
+
if (
|
|
278
|
+
error?.code !== "ERR_CANCELED" &&
|
|
279
|
+
error?.message !== "canceled"
|
|
280
|
+
) {
|
|
281
|
+
const errorMessage =
|
|
282
|
+
error?.code === "ERR_NETWORK"
|
|
283
|
+
? "Unable to connect to the server. Please check your internet connection and try again."
|
|
284
|
+
: "The server is not responding, please reload or try again later.";
|
|
285
|
+
|
|
286
|
+
// Only show toast after multiple failures to avoid spam
|
|
287
|
+
const failureCount = parseInt(sessionStorage.getItem("connectionFailures") || "0");
|
|
288
|
+
sessionStorage.setItem("connectionFailures", (failureCount + 1).toString());
|
|
289
|
+
|
|
290
|
+
if (failureCount >= 3) {
|
|
291
|
+
toast.error(errorMessage, {
|
|
292
|
+
toastId: `${error?.code || "ERR_CONNECTION_REFUSED"}`,
|
|
293
|
+
icon: <GppBad />,
|
|
294
|
+
});
|
|
295
|
+
// Reset counter after showing toast
|
|
296
|
+
sessionStorage.setItem("connectionFailures", "0");
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return Promise.reject(error);
|
|
197
300
|
}
|
|
198
|
-
|
|
301
|
+
|
|
302
|
+
// For all other errors, just reject without throwing
|
|
303
|
+
return Promise.reject(error);
|
|
199
304
|
}
|
|
200
305
|
);
|
|
201
306
|
};
|
|
202
|
-
|
|
203
|
-
|
|
307
|
+
|
|
308
|
+
// Call the interceptor setup
|
|
309
|
+
customInterceptor();
|
|
310
|
+
|
|
311
|
+
// Cleanup function to remove interceptors
|
|
312
|
+
return () => {
|
|
313
|
+
if (AuthenticatedAxiosObject?.interceptors?.response) {
|
|
314
|
+
AuthenticatedAxiosObject.interceptors.response.clear();
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
}, [AuthenticatedAxiosObject, currentLoggin, setCurrentLoggedIn]);
|
|
204
318
|
|
|
205
319
|
// We will use the below to refresh our data about the user when ever we flag refreshData as true
|
|
206
320
|
const [refreshData, setRefreshFlag] = useState(false);
|
|
@@ -217,11 +331,20 @@ const SessionManagerProvider = ({
|
|
|
217
331
|
|
|
218
332
|
useEffect(() => {
|
|
219
333
|
if (refreshData) {
|
|
220
|
-
userLoader()
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
334
|
+
userLoader()
|
|
335
|
+
.then((res) => {
|
|
336
|
+
const data = res?.data;
|
|
337
|
+
if (data) {
|
|
338
|
+
setUserInfo(data.Info || {});
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
.catch((err) => {
|
|
342
|
+
console.error("User data refresh failed:", err);
|
|
343
|
+
// Silently fail for data refresh - don't overwhelm user with toasts
|
|
344
|
+
})
|
|
345
|
+
.finally(() => {
|
|
346
|
+
setRefreshData(false);
|
|
347
|
+
});
|
|
225
348
|
}
|
|
226
349
|
}, [refreshData, userLoader]);
|
|
227
350
|
|
|
@@ -230,10 +353,21 @@ const SessionManagerProvider = ({
|
|
|
230
353
|
};
|
|
231
354
|
|
|
232
355
|
// Check if user has specific role
|
|
233
|
-
|
|
234
356
|
const hasRole = (roles) => {
|
|
235
|
-
|
|
236
|
-
|
|
357
|
+
if (
|
|
358
|
+
!roles ||
|
|
359
|
+
!Array.isArray(roles) ||
|
|
360
|
+
!userInfo?.roles ||
|
|
361
|
+
!Array.isArray(userInfo.roles)
|
|
362
|
+
) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
return roles.some((r) => userInfo.roles.indexOf(r) >= 0);
|
|
367
|
+
} catch (err) {
|
|
368
|
+
console.error("Error checking user roles:", err);
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
237
371
|
};
|
|
238
372
|
|
|
239
373
|
const contextValue = {
|
|
@@ -248,46 +382,234 @@ const SessionManagerProvider = ({
|
|
|
248
382
|
hasRole: hasRole,
|
|
249
383
|
deviceUID: deviceUID,
|
|
250
384
|
loadingUser: loadingUser,
|
|
385
|
+
authError: authError,
|
|
251
386
|
};
|
|
252
387
|
|
|
388
|
+
// Show loading state while initializing
|
|
389
|
+
if (loadingUser) {
|
|
390
|
+
return (
|
|
391
|
+
<ThemeWrapper>
|
|
392
|
+
<ErrorBoundary>
|
|
393
|
+
<SessionManager.Provider value={contextValue}>
|
|
394
|
+
<div style={{
|
|
395
|
+
display: 'flex',
|
|
396
|
+
flexDirection: 'column',
|
|
397
|
+
alignItems: 'center',
|
|
398
|
+
justifyContent: 'center',
|
|
399
|
+
minHeight: '100vh',
|
|
400
|
+
padding: '20px',
|
|
401
|
+
backgroundColor: '#f5f5f5',
|
|
402
|
+
fontFamily: 'Arial, sans-serif'
|
|
403
|
+
}}>
|
|
404
|
+
<div style={{
|
|
405
|
+
backgroundColor: 'white',
|
|
406
|
+
padding: '40px',
|
|
407
|
+
borderRadius: '8px',
|
|
408
|
+
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
|
|
409
|
+
textAlign: 'center',
|
|
410
|
+
maxWidth: '400px'
|
|
411
|
+
}}>
|
|
412
|
+
<div style={{
|
|
413
|
+
width: '40px',
|
|
414
|
+
height: '40px',
|
|
415
|
+
border: '4px solid #f3f3f3',
|
|
416
|
+
borderTop: '4px solid #1976d2',
|
|
417
|
+
borderRadius: '50%',
|
|
418
|
+
animation: 'spin 1s linear infinite',
|
|
419
|
+
margin: '0 auto 16px'
|
|
420
|
+
}} />
|
|
421
|
+
<h3 style={{ color: '#333', marginBottom: '8px' }}>Loading...</h3>
|
|
422
|
+
<p style={{ color: '#666', margin: 0 }}>
|
|
423
|
+
Initializing session manager
|
|
424
|
+
</p>
|
|
425
|
+
</div>
|
|
426
|
+
<style>{`
|
|
427
|
+
@keyframes spin {
|
|
428
|
+
0% { transform: rotate(0deg); }
|
|
429
|
+
100% { transform: rotate(360deg); }
|
|
430
|
+
}
|
|
431
|
+
`}</style>
|
|
432
|
+
</div>
|
|
433
|
+
</SessionManager.Provider>
|
|
434
|
+
</ErrorBoundary>
|
|
435
|
+
</ThemeWrapper>
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
return (
|
|
439
|
+
<ThemeWrapper>
|
|
440
|
+
<ErrorBoundary>
|
|
441
|
+
<SessionManager.Provider value={contextValue}>
|
|
442
|
+
<ToastContainer
|
|
443
|
+
position="top-left"
|
|
444
|
+
autoClose={5000}
|
|
445
|
+
closeOnClick
|
|
446
|
+
pauseOnFocusLoss
|
|
447
|
+
pauseOnHover
|
|
448
|
+
newestOnTop={false}
|
|
449
|
+
toastClassName={"custToast materialToast"}
|
|
450
|
+
{...toastOptions}
|
|
451
|
+
/>
|
|
452
|
+
<div style={{
|
|
453
|
+
display: 'flex',
|
|
454
|
+
flexDirection: 'column',
|
|
455
|
+
alignItems: 'center',
|
|
456
|
+
justifyContent: 'center',
|
|
457
|
+
minHeight: '100vh',
|
|
458
|
+
padding: '20px',
|
|
459
|
+
backgroundColor: '#f5f5f5',
|
|
460
|
+
fontFamily: 'Arial, sans-serif'
|
|
461
|
+
}}>
|
|
462
|
+
<div style={{
|
|
463
|
+
backgroundColor: 'white',
|
|
464
|
+
padding: '40px',
|
|
465
|
+
borderRadius: '8px',
|
|
466
|
+
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
|
|
467
|
+
textAlign: 'center',
|
|
468
|
+
maxWidth: '400px'
|
|
469
|
+
}}>
|
|
470
|
+
<div style={{ fontSize: '48px', marginBottom: '16px' }}>⚠️</div>
|
|
471
|
+
<h2 style={{ color: '#d32f2f', marginBottom: '16px' }}>Connection Error</h2>
|
|
472
|
+
<p style={{ color: '#666', marginBottom: '24px' }}>
|
|
473
|
+
Unable to connect to the server. Please check your internet connection and try again.
|
|
474
|
+
</p>
|
|
475
|
+
<button
|
|
476
|
+
onClick={() => window.location.reload()}
|
|
477
|
+
style={{
|
|
478
|
+
backgroundColor: '#1976d2',
|
|
479
|
+
color: 'white',
|
|
480
|
+
border: 'none',
|
|
481
|
+
padding: '12px 24px',
|
|
482
|
+
borderRadius: '4px',
|
|
483
|
+
cursor: 'pointer',
|
|
484
|
+
fontSize: '16px'
|
|
485
|
+
}}
|
|
486
|
+
>
|
|
487
|
+
Retry
|
|
488
|
+
</button>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
</SessionManager.Provider>
|
|
492
|
+
</ErrorBoundary>
|
|
493
|
+
</ThemeWrapper>
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// If there's a persistent auth error, render a minimal error state
|
|
498
|
+
if (authError && !loadingUser) {
|
|
499
|
+
return (
|
|
500
|
+
<ThemeWrapper>
|
|
501
|
+
<ErrorBoundary>
|
|
502
|
+
<SessionManager.Provider value={contextValue}>
|
|
503
|
+
<ToastContainer
|
|
504
|
+
position="top-left"
|
|
505
|
+
autoClose={5000}
|
|
506
|
+
closeOnClick
|
|
507
|
+
pauseOnFocusLoss
|
|
508
|
+
pauseOnHover
|
|
509
|
+
newestOnTop={false}
|
|
510
|
+
toastClassName={"custToast materialToast"}
|
|
511
|
+
{...toastOptions}
|
|
512
|
+
/>
|
|
513
|
+
<div style={{
|
|
514
|
+
display: 'flex',
|
|
515
|
+
flexDirection: 'column',
|
|
516
|
+
alignItems: 'center',
|
|
517
|
+
justifyContent: 'center',
|
|
518
|
+
minHeight: '100vh',
|
|
519
|
+
padding: '20px',
|
|
520
|
+
backgroundColor: '#f5f5f5',
|
|
521
|
+
fontFamily: 'Arial, sans-serif'
|
|
522
|
+
}}>
|
|
523
|
+
<div style={{
|
|
524
|
+
backgroundColor: 'white',
|
|
525
|
+
padding: '40px',
|
|
526
|
+
borderRadius: '8px',
|
|
527
|
+
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
|
|
528
|
+
textAlign: 'center',
|
|
529
|
+
maxWidth: '400px'
|
|
530
|
+
}}>
|
|
531
|
+
<div style={{ fontSize: '48px', marginBottom: '16px' }}>⚠️</div>
|
|
532
|
+
<h2 style={{ color: '#d32f2f', marginBottom: '16px' }}>Connection Error</h2>
|
|
533
|
+
<p style={{ color: '#666', marginBottom: '24px' }}>
|
|
534
|
+
Unable to connect to the server. Please check your internet connection and try again.
|
|
535
|
+
</p>
|
|
536
|
+
<button
|
|
537
|
+
onClick={() => window.location.reload()}
|
|
538
|
+
style={{
|
|
539
|
+
backgroundColor: '#1976d2',
|
|
540
|
+
color: 'white',
|
|
541
|
+
border: 'none',
|
|
542
|
+
padding: '12px 24px',
|
|
543
|
+
borderRadius: '4px',
|
|
544
|
+
cursor: 'pointer',
|
|
545
|
+
fontSize: '16px'
|
|
546
|
+
}}
|
|
547
|
+
>
|
|
548
|
+
Retry
|
|
549
|
+
</button>
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
</SessionManager.Provider>
|
|
553
|
+
</ErrorBoundary>
|
|
554
|
+
</ThemeWrapper>
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
|
|
253
558
|
return (
|
|
254
|
-
<
|
|
255
|
-
<
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
559
|
+
<ThemeWrapper>
|
|
560
|
+
<ErrorBoundary>
|
|
561
|
+
<SessionManager.Provider value={contextValue}>
|
|
562
|
+
<ToastContainer
|
|
563
|
+
position="top-left"
|
|
564
|
+
autoClose={5000}
|
|
565
|
+
closeOnClick
|
|
566
|
+
pauseOnFocusLoss
|
|
567
|
+
pauseOnHover
|
|
568
|
+
newestOnTop={false}
|
|
569
|
+
toastClassName={"custToast materialToast"}
|
|
570
|
+
{...toastOptions}
|
|
571
|
+
/>
|
|
572
|
+
<VersionProtection appVersion={appVersion} />
|
|
573
|
+
{children}
|
|
574
|
+
</SessionManager.Provider>
|
|
575
|
+
</ErrorBoundary>
|
|
576
|
+
</ThemeWrapper>
|
|
268
577
|
);
|
|
269
578
|
};
|
|
270
579
|
|
|
271
580
|
function VersionProtection({ appVersion }) {
|
|
272
|
-
const oldVersion =
|
|
581
|
+
const [oldVersion, setOldVersion] = useState(false);
|
|
582
|
+
|
|
273
583
|
useEffect(() => {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
584
|
+
try {
|
|
585
|
+
const isOldVersion = sessionStorage.getItem("appVersionOld") === "true";
|
|
586
|
+
setOldVersion(isOldVersion);
|
|
587
|
+
|
|
588
|
+
if (
|
|
589
|
+
isOldVersion &&
|
|
590
|
+
sessionStorage.getItem("requiredVersion") &&
|
|
591
|
+
appVersion &&
|
|
592
|
+
versionCompare(appVersion, sessionStorage.getItem("requiredVersion"))
|
|
593
|
+
) {
|
|
594
|
+
console.log("Update Success Toast");
|
|
595
|
+
toast.success("Your application has been updated", {
|
|
596
|
+
toastId: "appReload",
|
|
597
|
+
icon: <BrowserUpdated />,
|
|
598
|
+
onClose: () => {
|
|
599
|
+
try {
|
|
600
|
+
sessionStorage.removeItem("appVersionOld");
|
|
601
|
+
sessionStorage.removeItem("requiredVersion");
|
|
602
|
+
sessionStorage.removeItem("appReloads");
|
|
603
|
+
} catch (err) {
|
|
604
|
+
console.error("Error cleaning up version storage:", err);
|
|
605
|
+
}
|
|
606
|
+
},
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
} catch (err) {
|
|
610
|
+
console.error("Error in version protection:", err);
|
|
289
611
|
}
|
|
290
|
-
}, [
|
|
612
|
+
}, [appVersion]);
|
|
291
613
|
|
|
292
614
|
return <></>;
|
|
293
615
|
}
|