dce-reactkit 2.0.9 → 2.0.12
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/lib/components/AppWrapper.d.ts +16 -1
- package/lib/components/AppWrapper.js +157 -13
- package/lib/components/AppWrapper.js.map +1 -1
- package/lib/components/Modal.d.ts +2 -0
- package/lib/components/Modal.js +9 -3
- package/lib/components/Modal.js.map +1 -1
- package/lib/components/TabBox.d.ts +11 -0
- package/lib/components/TabBox.js +42 -0
- package/lib/components/TabBox.js.map +1 -0
- package/lib/helpers/alert.d.ts +2 -0
- package/lib/helpers/alert.js +6 -0
- package/lib/helpers/alert.js.map +1 -0
- package/lib/helpers/confirm.d.ts +2 -0
- package/lib/helpers/confirm.js +6 -0
- package/lib/helpers/confirm.js.map +1 -0
- package/lib/helpers/showFatalError.d.ts +1 -1
- package/lib/helpers/showFatalError.js +2 -1
- package/lib/helpers/showFatalError.js.map +1 -1
- package/package.json +1 -1
- package/src/components/AppWrapper.tsx +191 -11
- package/src/components/Modal.tsx +16 -2
- package/src/components/TabBox.tsx +136 -0
- package/src/helpers/alert.tsx +5 -0
- package/src/helpers/confirm.tsx +5 -0
- package/src/helpers/showFatalError.tsx +3 -1
- package/lib/components/AppWrapper.tsx +0 -181
- package/lib/components/ErrorBox.tsx +0 -124
- package/lib/components/LoadingSpinner.tsx +0 -105
- package/lib/components/Modal.tsx +0 -447
- package/lib/errors/ErrorWithCode.tsx +0 -15
- package/lib/helpers/abbreviate.tsx +0 -23
- package/lib/helpers/avg.tsx +0 -22
- package/lib/helpers/ceilToNumDecimals.tsx +0 -13
- package/lib/helpers/floorToNumDecimals.tsx +0 -13
- package/lib/helpers/forceNumIntoBounds.tsx +0 -19
- package/lib/helpers/handleError.ts +0 -65
- package/lib/helpers/handleSuccess.ts +0 -18
- package/lib/helpers/padDecimalZeros.tsx +0 -32
- package/lib/helpers/padZerosLeft.tsx +0 -21
- package/lib/helpers/parseRequest.ts +0 -299
- package/lib/helpers/roundToNumDecimals.tsx +0 -13
- package/lib/helpers/showFatalError.tsx +0 -3
- package/lib/helpers/sum.tsx +0 -16
- package/lib/helpers/visitServerEndpoint.tsx +0 -110
- package/lib/helpers/waitMs.tsx +0 -12
- package/lib/types/ParamType.ts +0 -18
- package/lib/types/ReactKitErrorCode.tsx +0 -16
- package/lib/types/Variant.tsx +0 -16
|
@@ -16,6 +16,9 @@ import { setSessionExpiryHandler } from '../helpers/visitServerEndpoint';
|
|
|
16
16
|
// Import shared types
|
|
17
17
|
import ReactKitErrorCode from '../types/ReactKitErrorCode';
|
|
18
18
|
|
|
19
|
+
// Import shared components
|
|
20
|
+
import Modal from './Modal';
|
|
21
|
+
|
|
19
22
|
// Import custom errors
|
|
20
23
|
import ErrorWithCode from '../errors/ErrorWithCode';
|
|
21
24
|
|
|
@@ -36,6 +39,84 @@ type Props = {
|
|
|
36
39
|
/* Static Helpers */
|
|
37
40
|
/*------------------------------------------------------------------------*/
|
|
38
41
|
|
|
42
|
+
/*----------------------------------------*/
|
|
43
|
+
/* Alert */
|
|
44
|
+
/*----------------------------------------*/
|
|
45
|
+
|
|
46
|
+
// Stored copies of setters
|
|
47
|
+
let setAlertInfo: (info: { title: string, text: string }) => void;
|
|
48
|
+
let onAlertClosed: () => void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Show an alert modal with an "Okay" button
|
|
52
|
+
* @author Gabe Abrams
|
|
53
|
+
* @param title the title text to display at the top of the alert
|
|
54
|
+
* @param text the text to display in the alert
|
|
55
|
+
*/
|
|
56
|
+
export const _alert = async (title: string, text: string) => {
|
|
57
|
+
// Fallback if alert not available
|
|
58
|
+
if (!setAlertInfo) {
|
|
59
|
+
return window.alert(`${title}\n\n${text}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Return promise that resolves when alert is closed
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
// Setup handler
|
|
65
|
+
onAlertClosed = () => {
|
|
66
|
+
resolve(null);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Show the alert
|
|
70
|
+
setAlertInfo({
|
|
71
|
+
title,
|
|
72
|
+
text,
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/*----------------------------------------*/
|
|
78
|
+
/* Confirm */
|
|
79
|
+
/*----------------------------------------*/
|
|
80
|
+
|
|
81
|
+
// Stored copies of setters
|
|
82
|
+
let setConfirmInfo: (info: { title: string, text: string }) => void;
|
|
83
|
+
let onConfirmClosed: (confirmed: boolean) => void;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Show a confirmation modal with an "Okay" and a "Cancel" button
|
|
87
|
+
* @author Gabe Abrams
|
|
88
|
+
* @param title the title text to display at the top of the alert
|
|
89
|
+
* @param text the text to display in the alert
|
|
90
|
+
* @returns true if the user confirmed
|
|
91
|
+
*/
|
|
92
|
+
export const _confirm = async (
|
|
93
|
+
title: string,
|
|
94
|
+
text: string,
|
|
95
|
+
): Promise<boolean> => {
|
|
96
|
+
// Fallback if confirm is not available
|
|
97
|
+
if (!setConfirmInfo) {
|
|
98
|
+
return window.confirm(`${title}\n\n${text}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Return promise that resolves with result of confirmation
|
|
102
|
+
return new Promise((resolve) => {
|
|
103
|
+
// Setup handler
|
|
104
|
+
onConfirmClosed = (confirmed: boolean) => {
|
|
105
|
+
resolve(confirmed)
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Show the confirm
|
|
109
|
+
setConfirmInfo({
|
|
110
|
+
title,
|
|
111
|
+
text,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/*----------------------------------------*/
|
|
117
|
+
/* Fatal Error */
|
|
118
|
+
/*----------------------------------------*/
|
|
119
|
+
|
|
39
120
|
// Stored copies of setters
|
|
40
121
|
let setFatalErrorMessage: (message: string) => void;
|
|
41
122
|
let setFatalErrorCode: (code: string) => void;
|
|
@@ -47,7 +128,7 @@ let setFatalErrorTitle: (title: string) => void;
|
|
|
47
128
|
* @param error the error to show
|
|
48
129
|
* @param [errorTitle] title of the error box
|
|
49
130
|
*/
|
|
50
|
-
export const
|
|
131
|
+
export const _showFatalError = (
|
|
51
132
|
error: any,
|
|
52
133
|
errorTitle: string = 'An Error Occurred',
|
|
53
134
|
) => {
|
|
@@ -65,7 +146,10 @@ export const showFatalError = (
|
|
|
65
146
|
|
|
66
147
|
// Handle case where app hasn't loaded
|
|
67
148
|
if (!setFatalErrorMessage || !setFatalErrorCode) {
|
|
68
|
-
return
|
|
149
|
+
return _alert(
|
|
150
|
+
errorTitle,
|
|
151
|
+
`${message} (code: ${code}). Please contact support.`,
|
|
152
|
+
);
|
|
69
153
|
}
|
|
70
154
|
|
|
71
155
|
// Use setters
|
|
@@ -94,15 +178,47 @@ const AppWrapper = (props: Props): React.ReactElement => {
|
|
|
94
178
|
/* -------------- State ------------- */
|
|
95
179
|
|
|
96
180
|
// Fatal error
|
|
97
|
-
const [
|
|
181
|
+
const [
|
|
182
|
+
fatalErrorMessage,
|
|
183
|
+
setFatalErrorMessageInner,
|
|
184
|
+
] = useState<string | undefined>();
|
|
98
185
|
setFatalErrorMessage = setFatalErrorMessageInner;
|
|
99
|
-
const [
|
|
186
|
+
const [
|
|
187
|
+
fatalErrorCode,
|
|
188
|
+
setFatalErrorCodeInner,
|
|
189
|
+
] = useState<string | undefined>();
|
|
100
190
|
setFatalErrorCode = setFatalErrorCodeInner;
|
|
101
|
-
const [
|
|
191
|
+
const [
|
|
192
|
+
fatalErrorTitle,
|
|
193
|
+
setFatalErrorTitleInner,
|
|
194
|
+
] = useState<string | undefined>();
|
|
102
195
|
setFatalErrorTitle = setFatalErrorTitleInner;
|
|
103
196
|
|
|
197
|
+
// Alert
|
|
198
|
+
const [
|
|
199
|
+
alertInfo,
|
|
200
|
+
setAlertInfoInner,
|
|
201
|
+
] = useState<{
|
|
202
|
+
title: string,
|
|
203
|
+
text: string
|
|
204
|
+
}>();
|
|
205
|
+
setAlertInfo = setAlertInfoInner;
|
|
206
|
+
|
|
207
|
+
// Confirm
|
|
208
|
+
const [
|
|
209
|
+
confirmInfo,
|
|
210
|
+
setConfirmInfoInner,
|
|
211
|
+
] = useState<{
|
|
212
|
+
title: string,
|
|
213
|
+
text: string
|
|
214
|
+
}>();
|
|
215
|
+
setConfirmInfo = setConfirmInfoInner;
|
|
216
|
+
|
|
104
217
|
// Session expired
|
|
105
|
-
const [
|
|
218
|
+
const [
|
|
219
|
+
sessionHasExpired,
|
|
220
|
+
setSessionHasExpired,
|
|
221
|
+
] = useState<boolean>(false);
|
|
106
222
|
|
|
107
223
|
/*------------------------------------------------------------------------*/
|
|
108
224
|
/* Lifecycle Functions */
|
|
@@ -128,10 +244,60 @@ const AppWrapper = (props: Props): React.ReactElement => {
|
|
|
128
244
|
/*------------------------------------------------------------------------*/
|
|
129
245
|
|
|
130
246
|
/*----------------------------------------*/
|
|
131
|
-
/*
|
|
247
|
+
/* Modal */
|
|
248
|
+
/*----------------------------------------*/
|
|
249
|
+
|
|
250
|
+
// Modal that may be defined
|
|
251
|
+
let modal: React.ReactNode;
|
|
252
|
+
|
|
253
|
+
/* -------------- Alert ------------- */
|
|
254
|
+
|
|
255
|
+
if (alertInfo) {
|
|
256
|
+
modal = (
|
|
257
|
+
<Modal
|
|
258
|
+
title={alertInfo.title}
|
|
259
|
+
type={Modal.ModalType.Okay}
|
|
260
|
+
onClose={() => {
|
|
261
|
+
// Alert closed
|
|
262
|
+
if (onAlertClosed) {
|
|
263
|
+
onAlertClosed();
|
|
264
|
+
}
|
|
265
|
+
}}
|
|
266
|
+
onTopOfOtherModals
|
|
267
|
+
>
|
|
268
|
+
{alertInfo.text}
|
|
269
|
+
</Modal>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* ------------- Confirm ------------ */
|
|
274
|
+
|
|
275
|
+
if (confirmInfo) {
|
|
276
|
+
modal = (
|
|
277
|
+
<Modal
|
|
278
|
+
title={confirmInfo.title}
|
|
279
|
+
type={Modal.ModalType.OkayCancel}
|
|
280
|
+
onClose={(buttonType) => {
|
|
281
|
+
if (onConfirmClosed) {
|
|
282
|
+
onConfirmClosed(buttonType === Modal.ButtonType.Okay);
|
|
283
|
+
}
|
|
284
|
+
}}
|
|
285
|
+
dontAllowBackdropExit
|
|
286
|
+
>
|
|
287
|
+
|
|
288
|
+
</Modal>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/*----------------------------------------*/
|
|
293
|
+
/* Views */
|
|
132
294
|
/*----------------------------------------*/
|
|
133
295
|
|
|
134
|
-
//
|
|
296
|
+
// Body that will be filled with the current view
|
|
297
|
+
let body: React.ReactNode;
|
|
298
|
+
|
|
299
|
+
/* ----------- Fatal Error ---------- */
|
|
300
|
+
|
|
135
301
|
if (fatalErrorMessage || fatalErrorCode || sessionHasExpired) {
|
|
136
302
|
// Re-encapsulate in an error
|
|
137
303
|
const error = (
|
|
@@ -147,7 +313,7 @@ const AppWrapper = (props: Props): React.ReactElement => {
|
|
|
147
313
|
);
|
|
148
314
|
|
|
149
315
|
// Build error screen
|
|
150
|
-
|
|
316
|
+
body = (
|
|
151
317
|
<div
|
|
152
318
|
style={{
|
|
153
319
|
display: 'block',
|
|
@@ -173,10 +339,24 @@ const AppWrapper = (props: Props): React.ReactElement => {
|
|
|
173
339
|
);
|
|
174
340
|
}
|
|
175
341
|
|
|
176
|
-
|
|
342
|
+
/* --------------- App -------------- */
|
|
343
|
+
|
|
344
|
+
if (!body) {
|
|
345
|
+
body = (
|
|
346
|
+
<>
|
|
347
|
+
{children}
|
|
348
|
+
</>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/*----------------------------------------*/
|
|
353
|
+
/* Main UI */
|
|
354
|
+
/*----------------------------------------*/
|
|
355
|
+
|
|
177
356
|
return (
|
|
178
357
|
<>
|
|
179
|
-
{
|
|
358
|
+
{modal}
|
|
359
|
+
{body}
|
|
180
360
|
</>
|
|
181
361
|
);
|
|
182
362
|
};
|
package/src/components/Modal.tsx
CHANGED
|
@@ -209,6 +209,8 @@ type Props = {
|
|
|
209
209
|
children?: React.ReactNode,
|
|
210
210
|
// Handler to call when modal is closed (if excluded, not closable)
|
|
211
211
|
onClose?: (type: ButtonType) => void,
|
|
212
|
+
// If true, don't allow the user to click the backdrop to exit
|
|
213
|
+
dontAllowBackdropExit?: boolean,
|
|
212
214
|
// Custom label for "okay" button
|
|
213
215
|
okayLabel?: string,
|
|
214
216
|
// Custom variant for "okay" button
|
|
@@ -249,6 +251,8 @@ type Props = {
|
|
|
249
251
|
confirmLabel?: string,
|
|
250
252
|
// Custom variant for "confirm" button
|
|
251
253
|
confirmVariant?: Variant,
|
|
254
|
+
// True if modal should be on top of other modals
|
|
255
|
+
onTopOfOtherModals?: boolean,
|
|
252
256
|
};
|
|
253
257
|
|
|
254
258
|
/*------------------------------------------------------------------------*/
|
|
@@ -276,6 +280,8 @@ const Modal: (
|
|
|
276
280
|
title,
|
|
277
281
|
children,
|
|
278
282
|
onClose,
|
|
283
|
+
dontAllowBackdropExit,
|
|
284
|
+
onTopOfOtherModals,
|
|
279
285
|
} = props;
|
|
280
286
|
|
|
281
287
|
/* -------------- State ------------- */
|
|
@@ -404,9 +410,17 @@ const Modal: (
|
|
|
404
410
|
show={visible}
|
|
405
411
|
size={size !== ModalSize.Medium ? size : undefined}
|
|
406
412
|
onHide={() => {
|
|
407
|
-
|
|
413
|
+
if (!dontAllowBackdropExit) {
|
|
414
|
+
handleClose(ButtonType.Cancel);
|
|
415
|
+
}
|
|
416
|
+
}}
|
|
417
|
+
style={{
|
|
418
|
+
zIndex: (
|
|
419
|
+
onTopOfOtherModals
|
|
420
|
+
? 6000000000
|
|
421
|
+
: 5000000000
|
|
422
|
+
),
|
|
408
423
|
}}
|
|
409
|
-
style={{ zIndex: 5000000000 }}
|
|
410
424
|
backdropClassName="Modal-backdrop"
|
|
411
425
|
centered
|
|
412
426
|
>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A box with a tab on the top that holds buttons and other content
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import React
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
/*------------------------------------------------------------------------*/
|
|
10
|
+
/* Types */
|
|
11
|
+
/*------------------------------------------------------------------------*/
|
|
12
|
+
|
|
13
|
+
type Props = {
|
|
14
|
+
// Title of the box
|
|
15
|
+
title: React.ReactNode,
|
|
16
|
+
// Children/contents inside the box
|
|
17
|
+
children: React.ReactNode,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/*------------------------------------------------------------------------*/
|
|
21
|
+
/* Style */
|
|
22
|
+
/*------------------------------------------------------------------------*/
|
|
23
|
+
|
|
24
|
+
const style = `
|
|
25
|
+
/* Tab Box */
|
|
26
|
+
.TabBox-box {
|
|
27
|
+
/* Light Border */
|
|
28
|
+
border: 2px solid #dedede;
|
|
29
|
+
|
|
30
|
+
/* Rounded Corners (except top-left) */
|
|
31
|
+
border-bottom-right-radius: 5px;
|
|
32
|
+
border-bottom-left-radius: 5px;
|
|
33
|
+
border-top-right-radius: 5px;
|
|
34
|
+
|
|
35
|
+
/* Very Light Gray Border */
|
|
36
|
+
background: #fdfdfd;
|
|
37
|
+
|
|
38
|
+
/* Align Contents on Left */
|
|
39
|
+
text-align: left;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Container for Title */
|
|
43
|
+
.TabBox-title-container {
|
|
44
|
+
/* Place on Left */
|
|
45
|
+
position: relative;
|
|
46
|
+
left: 0;
|
|
47
|
+
text-align: left;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Tab-style Title */
|
|
51
|
+
.TabBox-title {
|
|
52
|
+
/* Place so it Barely Overlaps the Box Border */
|
|
53
|
+
display: inline-block;
|
|
54
|
+
position: relative;
|
|
55
|
+
top: 2px; /* Gives Illusion that Border Doesn't Exist Below Tab */
|
|
56
|
+
|
|
57
|
+
/* Title-sized Font */
|
|
58
|
+
font-size: 25px;
|
|
59
|
+
|
|
60
|
+
/* Add Border on Top and Sides */
|
|
61
|
+
border-top: 2px solid #dedede;
|
|
62
|
+
border-left: 2px solid #dedede;
|
|
63
|
+
border-right: 2px solid #dedede;
|
|
64
|
+
|
|
65
|
+
/* Round the Top Corners */
|
|
66
|
+
border-top-left-radius: 5px;
|
|
67
|
+
border-top-right-radius: 5px;
|
|
68
|
+
|
|
69
|
+
/* Add Text Padding */
|
|
70
|
+
padding-left: 12px;
|
|
71
|
+
padding-right: 12px;
|
|
72
|
+
|
|
73
|
+
/* Match Background Color of Box */
|
|
74
|
+
background: #fdfdfd;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Make the TabBox's Children Appear Above Title if Overlap Occurs */
|
|
78
|
+
.TabBox-children {
|
|
79
|
+
position: relative;
|
|
80
|
+
z-index: 1;
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
/*------------------------------------------------------------------------*/
|
|
85
|
+
/* Component */
|
|
86
|
+
/*------------------------------------------------------------------------*/
|
|
87
|
+
|
|
88
|
+
const TabBox: React.FC<Props> = (props) => {
|
|
89
|
+
/*------------------------------------------------------------------------*/
|
|
90
|
+
/* Setup */
|
|
91
|
+
/*------------------------------------------------------------------------*/
|
|
92
|
+
|
|
93
|
+
/* -------------- Props ------------- */
|
|
94
|
+
|
|
95
|
+
const {
|
|
96
|
+
title,
|
|
97
|
+
children,
|
|
98
|
+
} = props;
|
|
99
|
+
|
|
100
|
+
/*------------------------------------------------------------------------*/
|
|
101
|
+
/* Render */
|
|
102
|
+
/*------------------------------------------------------------------------*/
|
|
103
|
+
|
|
104
|
+
/*----------------------------------------*/
|
|
105
|
+
/* Main UI */
|
|
106
|
+
/*----------------------------------------*/
|
|
107
|
+
|
|
108
|
+
// Full UI
|
|
109
|
+
return (
|
|
110
|
+
<div>
|
|
111
|
+
{/* Style */}
|
|
112
|
+
<style>{style}</style>
|
|
113
|
+
|
|
114
|
+
{/* Title */}
|
|
115
|
+
<div className="TabBox-title-container">
|
|
116
|
+
<div className="TabBox-title">
|
|
117
|
+
{title}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{/* Contents */}
|
|
122
|
+
<div className="TabBox-box p-2">
|
|
123
|
+
<div className="TabBox-children">
|
|
124
|
+
{children}
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/*------------------------------------------------------------------------*/
|
|
132
|
+
/* Wrap Up */
|
|
133
|
+
/*------------------------------------------------------------------------*/
|
|
134
|
+
|
|
135
|
+
// Export component
|
|
136
|
+
export default TabBox;
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A wrapper for the entire React app that adds global functionality like
|
|
3
|
-
* handling for fatal error messages
|
|
4
|
-
* @author Gabe Abrams
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Import React
|
|
8
|
-
import React, { useState, useEffect } from 'react';
|
|
9
|
-
|
|
10
|
-
// Import shared components
|
|
11
|
-
import ErrorBox from './ErrorBox';
|
|
12
|
-
|
|
13
|
-
// Import helpers
|
|
14
|
-
import { setSessionExpiryHandler } from '../helpers/visitServerEndpoint';
|
|
15
|
-
|
|
16
|
-
// Import shared types
|
|
17
|
-
import ReactKitErrorCode from '../types/ReactKitErrorCode';
|
|
18
|
-
|
|
19
|
-
// Import custom errors
|
|
20
|
-
import ErrorWithCode from '../errors/ErrorWithCode';
|
|
21
|
-
|
|
22
|
-
/*------------------------------------------------------------------------*/
|
|
23
|
-
/* Props */
|
|
24
|
-
/*------------------------------------------------------------------------*/
|
|
25
|
-
|
|
26
|
-
type Props = {
|
|
27
|
-
// The entire app
|
|
28
|
-
children: React.ReactNode,
|
|
29
|
-
// True if this app is a dark-themed app
|
|
30
|
-
dark?: boolean,
|
|
31
|
-
// Custom session expired message
|
|
32
|
-
sessionExpiredMessage?: string,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/*------------------------------------------------------------------------*/
|
|
36
|
-
/* Static Helpers */
|
|
37
|
-
/*------------------------------------------------------------------------*/
|
|
38
|
-
|
|
39
|
-
// Stored copies of setters
|
|
40
|
-
let setFatalErrorMessage: (message: string) => void;
|
|
41
|
-
let setFatalErrorCode: (code: string) => void;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Show a fatal error message
|
|
45
|
-
* @author Gabe Abrams
|
|
46
|
-
* @param error the error to show
|
|
47
|
-
*/
|
|
48
|
-
export const showFatalError = (error: any) => {
|
|
49
|
-
// Determine message and code
|
|
50
|
-
const message: string = (
|
|
51
|
-
typeof error === 'string'
|
|
52
|
-
? error.trim()
|
|
53
|
-
: String(error.message ?? 'An unknown error occurred.')
|
|
54
|
-
);
|
|
55
|
-
const code: string = (
|
|
56
|
-
typeof error === 'string'
|
|
57
|
-
? ReactKitErrorCode.NoCode
|
|
58
|
-
: String(error.code ?? ReactKitErrorCode.NoCode)
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
// Handle case where app hasn't loaded
|
|
62
|
-
if (!setFatalErrorMessage || !setFatalErrorCode) {
|
|
63
|
-
return alert(`An error has occurred: ${message} (code: ${code}). Please contact support.`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Use setters
|
|
67
|
-
setFatalErrorMessage(message);
|
|
68
|
-
setFatalErrorCode(code);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/*------------------------------------------------------------------------*/
|
|
72
|
-
/* Component */
|
|
73
|
-
/*------------------------------------------------------------------------*/
|
|
74
|
-
|
|
75
|
-
const AppWrapper = (props: Props): React.ReactElement => {
|
|
76
|
-
/*------------------------------------------------------------------------*/
|
|
77
|
-
/* Setup */
|
|
78
|
-
/*------------------------------------------------------------------------*/
|
|
79
|
-
|
|
80
|
-
/* -------------- Props ------------- */
|
|
81
|
-
|
|
82
|
-
const {
|
|
83
|
-
children,
|
|
84
|
-
dark,
|
|
85
|
-
sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.',
|
|
86
|
-
} = props;
|
|
87
|
-
|
|
88
|
-
/* -------------- State ------------- */
|
|
89
|
-
|
|
90
|
-
// Fatal error
|
|
91
|
-
const [fatalErrorMessage, setFatalErrorMessageInner] = useState(null);
|
|
92
|
-
setFatalErrorMessage = setFatalErrorMessageInner;
|
|
93
|
-
const [fatalErrorCode, setFatalErrorCodeInner] = useState(null);
|
|
94
|
-
setFatalErrorCode = setFatalErrorCodeInner;
|
|
95
|
-
|
|
96
|
-
// Session expired
|
|
97
|
-
const [sessionHasExpired, setSessionHasExpired] = useState(false);
|
|
98
|
-
|
|
99
|
-
/*------------------------------------------------------------------------*/
|
|
100
|
-
/* Lifecycle Functions */
|
|
101
|
-
/*------------------------------------------------------------------------*/
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Mount
|
|
105
|
-
* @author Gabe Abrams
|
|
106
|
-
*/
|
|
107
|
-
useEffect(
|
|
108
|
-
() => {
|
|
109
|
-
// Add session expired handler
|
|
110
|
-
setSessionExpiryHandler(() => {
|
|
111
|
-
// Session expired!
|
|
112
|
-
setSessionHasExpired(true);
|
|
113
|
-
});
|
|
114
|
-
},
|
|
115
|
-
[],
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
/*------------------------------------------------------------------------*/
|
|
119
|
-
/* Render */
|
|
120
|
-
/*------------------------------------------------------------------------*/
|
|
121
|
-
|
|
122
|
-
/*----------------------------------------*/
|
|
123
|
-
/* Main UI */
|
|
124
|
-
/*----------------------------------------*/
|
|
125
|
-
|
|
126
|
-
// Show error
|
|
127
|
-
if (fatalErrorMessage || fatalErrorCode || sessionHasExpired) {
|
|
128
|
-
// Re-encapsulate in an error
|
|
129
|
-
const error = (
|
|
130
|
-
sessionHasExpired
|
|
131
|
-
? new ErrorWithCode(
|
|
132
|
-
fatalErrorMessage,
|
|
133
|
-
fatalErrorCode,
|
|
134
|
-
)
|
|
135
|
-
: new ErrorWithCode(
|
|
136
|
-
sessionExpiredMessage,
|
|
137
|
-
ReactKitErrorCode.SessionExpired,
|
|
138
|
-
)
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
// Build error screen
|
|
142
|
-
return (
|
|
143
|
-
<div
|
|
144
|
-
style={{
|
|
145
|
-
display: 'block',
|
|
146
|
-
width: '100vw',
|
|
147
|
-
minHeight: '100vh',
|
|
148
|
-
paddingTop: '10rem',
|
|
149
|
-
backgroundColor: (
|
|
150
|
-
dark
|
|
151
|
-
? '#222'
|
|
152
|
-
: '#fff'
|
|
153
|
-
),
|
|
154
|
-
}}
|
|
155
|
-
>
|
|
156
|
-
<ErrorBox
|
|
157
|
-
title={(
|
|
158
|
-
sessionHasExpired
|
|
159
|
-
? 'Session Expired'
|
|
160
|
-
: undefined
|
|
161
|
-
)}
|
|
162
|
-
error={error}
|
|
163
|
-
/>
|
|
164
|
-
</div>
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Show the app itself
|
|
169
|
-
return (
|
|
170
|
-
<>
|
|
171
|
-
{children}
|
|
172
|
-
</>
|
|
173
|
-
);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
/*------------------------------------------------------------------------*/
|
|
177
|
-
/* Wrap Up */
|
|
178
|
-
/*------------------------------------------------------------------------*/
|
|
179
|
-
|
|
180
|
-
// Export component
|
|
181
|
-
export default AppWrapper;
|