dce-reactkit 3.2.8 → 3.3.1

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.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Type of CACCL's send request function
3
+ * @author Gabe Abrams
4
+ */
5
+ declare type SendRequestFunction = (opts: {
6
+ path: string;
7
+ method: ('GET' | 'POST' | 'DELETE' | 'PUT');
8
+ params?: {
9
+ [x: string]: any;
10
+ } | undefined;
11
+ headers?: {
12
+ [x: string]: any;
13
+ } | undefined;
14
+ numRetries?: number | undefined;
15
+ }) => Promise<{
16
+ body: any;
17
+ status: number;
18
+ headers: {
19
+ [x: string]: any;
20
+ };
21
+ }>;
22
+ /**
23
+ * Get the send request function
24
+ * @author Gabe Abrams
25
+ * @returns sendRequest function
26
+ */
27
+ export declare const getSendRequest: () => SendRequestFunction;
28
+ declare let dark: boolean | undefined;
29
+ /**
30
+ * Get the current dark/light theme
31
+ * @author Gabe Abrams
32
+ * @returns true if the app has a dark theme
33
+ */
34
+ export declare const darkModeIsOn: () => boolean;
35
+ declare let sessionExpiredMessage: string | undefined;
36
+ /**
37
+ * Get the custom session expired message
38
+ * @author Gabe Abrams
39
+ * @returns session expired message
40
+ */
41
+ export declare const getSessionExpiredMessage: () => string;
42
+ /**
43
+ * Initialize the client-side version of reactkit
44
+ * @author Gabe Abrams
45
+ * @param opts object containing all arguments
46
+ * @param opts.sendRequest caccl send request functions
47
+ * @param [opts.dark] if true, the app is a dark-themed app
48
+ * @param [opts.sessionExpiredMessage] a custom session expired message
49
+ */
50
+ declare const initClient: (opts: {
51
+ sendRequest: SendRequestFunction;
52
+ dark?: boolean;
53
+ sessionExpiredMessage?: string;
54
+ }) => void;
55
+ export default initClient;
@@ -7,34 +7,7 @@ import React from 'react';
7
7
  import Variant from '../types/Variant';
8
8
  declare type Props = {
9
9
  children: React.ReactNode;
10
- sendRequest: SendRequestFunction;
11
- dark?: boolean;
12
- sessionExpiredMessage?: string;
13
10
  };
14
- declare type SendRequestFunction = (opts: {
15
- path: string;
16
- method: ('GET' | 'POST' | 'DELETE' | 'PUT');
17
- params?: {
18
- [x: string]: any;
19
- } | undefined;
20
- headers?: {
21
- [x: string]: any;
22
- } | undefined;
23
- numRetries?: number | undefined;
24
- }) => Promise<{
25
- body: any;
26
- status: number;
27
- headers: {
28
- [x: string]: any;
29
- };
30
- }>;
31
- /**
32
- * Send a request using caccl's send request feature
33
- * @author Gabe Abrams
34
- * @param opts send request options
35
- * @returns send request response
36
- */
37
- export declare const cacclSendRequest: SendRequestFunction;
38
11
  /**
39
12
  * Show an alert modal with an "Okay" button
40
13
  * @author Gabe Abrams
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Compare two arrays of objects by only comparing the values in a specific
3
+ * property (e.g. compare user arrays by comparing their user.id values)
4
+ * @author Gabe Abrams
5
+ * @param a the first array
6
+ * @param b the second array
7
+ * @param prop the property to compare with
8
+ * @returns true if the arrays contain the same objects as determined by
9
+ * the values associated with each object's prop
10
+ */
11
+ declare const compareArraysByProp: (a: any[], b: any[], prop: string) => boolean;
12
+ export default compareArraysByProp;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * For every element in an array, extract the value of a prop
3
+ * (e.g. for all user objects, extract their ages and put that into a new
4
+ * ages array)
5
+ * @author Gabe Abrams
6
+ * @param arr the array of objects to operate on
7
+ * @param prop the property to extract from each object
8
+ * @returns new array containing the corresponding values, in order, of each
9
+ * object in the original array
10
+ */
11
+ declare const extractProp: (arr: any[], prop: string) => any[];
12
+ export default extractProp;
@@ -21,6 +21,7 @@ import MINUTE_IN_MS from './constants/MINUTE_IN_MS';
21
21
  import HOUR_IN_MS from './constants/HOUR_IN_MS';
22
22
  import DAY_IN_MS from './constants/DAY_IN_MS';
23
23
  import DynamicWord from './dynamicConstants/DynamicWord';
24
+ import initClient from './client/initClient';
24
25
  import abbreviate from './helpers/abbreviate';
25
26
  import avg from './helpers/avg';
26
27
  import ceilToNumDecimals from './helpers/ceilToNumDecimals';
@@ -51,6 +52,8 @@ import getMonthName from './helpers/getMonthName';
51
52
  import genCSV from './helpers/genCSV';
52
53
  import canReviewLogs from './helpers/canReviewLogs';
53
54
  import isMobileOrTablet from './helpers/isMobileOrTablet';
55
+ import extractProp from './helpers/extractProp';
56
+ import compareArraysByProp from './helpers/compareArraysByProp';
54
57
  import ModalButtonType from './types/ModalButtonType';
55
58
  import ModalSize from './types/ModalSize';
56
59
  import ModalType from './types/ModalType';
@@ -66,4 +69,4 @@ import LogBuiltInMetadata from './types/LogBuiltInMetadata';
66
69
  import LogMetadataType from './types/LogMetadataType';
67
70
  import IntelliTableColumn from './types/IntelliTableColumn';
68
71
  import PickableItem from './components/ItemPicker/types/PickableItem';
69
- export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, alert, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, visitServerEndpoint, logClientEvent, initServer, genRouteHandler, handleError, handleSuccess, initLogCollection, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, IntelliTableColumn, PickableItem, ParamType, };
72
+ export { AppWrapper, LoadingSpinner, ErrorBox, Modal, TabBox, RadioButton, CheckboxButton, ButtonInputGroup, SimpleDateChooser, Drawer, PopSuccessMark, PopFailureMark, PopPendingMark, CopiableBox, ItemPicker, LogReviewer, IntelliTable, CSVDownloadButton, alert, confirm, showFatalError, ErrorWithCode, MINUTE_IN_MS, HOUR_IN_MS, DAY_IN_MS, DynamicWord, abbreviate, avg, ceilToNumDecimals, floorToNumDecimals, forceNumIntoBounds, padDecimalZeros, padZerosLeft, roundToNumDecimals, sum, waitMs, getOrdinal, getTimeInfoInET, stubServerEndpoint, startMinWait, getHumanReadableDate, getPartOfDay, stringsToHumanReadableList, onlyKeepLetters, parallelLimit, getMonthName, genCSV, canReviewLogs, isMobileOrTablet, extractProp, compareArraysByProp, initClient, visitServerEndpoint, logClientEvent, initServer, genRouteHandler, handleError, handleSuccess, initLogCollection, ModalButtonType, ModalSize, ModalType, ReactKitErrorCode, Variant, DayOfWeek, Log, LogType, LogSource, LogAction, LogBuiltInMetadata, LogMetadataType, IntelliTableColumn, PickableItem, ParamType, };
@@ -13,6 +13,8 @@ declare enum ReactKitErrorCode {
13
13
  NoCACCLGetLaunchInfoFunction = "DRK8",
14
14
  NotTTM = "DRK9",
15
15
  NotAdmin = "DRK10",
16
- NotAllowedToReviewLogs = "DRK11"
16
+ NotAllowedToReviewLogs = "DRK11",
17
+ ThemeCheckedBeforeReactKitReady = "DRK12",
18
+ SessionExpiredMessageGottenBeforeReactKitReady = "DRK13"
17
19
  }
18
20
  export default ReactKitErrorCode;
package/dist/index.d.ts CHANGED
@@ -24,27 +24,7 @@ declare enum Variant {
24
24
 
25
25
  declare type Props$g = {
26
26
  children: React.ReactNode;
27
- sendRequest: SendRequestFunction;
28
- dark?: boolean;
29
- sessionExpiredMessage?: string;
30
27
  };
31
- declare type SendRequestFunction = (opts: {
32
- path: string;
33
- method: ('GET' | 'POST' | 'DELETE' | 'PUT');
34
- params?: {
35
- [x: string]: any;
36
- } | undefined;
37
- headers?: {
38
- [x: string]: any;
39
- } | undefined;
40
- numRetries?: number | undefined;
41
- }) => Promise<{
42
- body: any;
43
- status: number;
44
- headers: {
45
- [x: string]: any;
46
- };
47
- }>;
48
28
  /**
49
29
  * Show an alert modal with an "Okay" button
50
30
  * @author Gabe Abrams
@@ -523,6 +503,41 @@ declare const DynamicWord: {
523
503
  DeviceCapitalized: string;
524
504
  };
525
505
 
506
+ /**
507
+ * Type of CACCL's send request function
508
+ * @author Gabe Abrams
509
+ */
510
+ declare type SendRequestFunction = (opts: {
511
+ path: string;
512
+ method: ('GET' | 'POST' | 'DELETE' | 'PUT');
513
+ params?: {
514
+ [x: string]: any;
515
+ } | undefined;
516
+ headers?: {
517
+ [x: string]: any;
518
+ } | undefined;
519
+ numRetries?: number | undefined;
520
+ }) => Promise<{
521
+ body: any;
522
+ status: number;
523
+ headers: {
524
+ [x: string]: any;
525
+ };
526
+ }>;
527
+ /**
528
+ * Initialize the client-side version of reactkit
529
+ * @author Gabe Abrams
530
+ * @param opts object containing all arguments
531
+ * @param opts.sendRequest caccl send request functions
532
+ * @param [opts.dark] if true, the app is a dark-themed app
533
+ * @param [opts.sessionExpiredMessage] a custom session expired message
534
+ */
535
+ declare const initClient: (opts: {
536
+ sendRequest: SendRequestFunction;
537
+ dark?: boolean;
538
+ sessionExpiredMessage?: string;
539
+ }) => void;
540
+
526
541
  /**
527
542
  * Shorten text so it fits into a certain number of chars
528
543
  * @author Gabe Abrams
@@ -1025,6 +1040,30 @@ declare const canReviewLogs: () => Promise<boolean>;
1025
1040
  */
1026
1041
  declare const isMobileOrTablet: () => boolean;
1027
1042
 
1043
+ /**
1044
+ * For every element in an array, extract the value of a prop
1045
+ * (e.g. for all user objects, extract their ages and put that into a new
1046
+ * ages array)
1047
+ * @author Gabe Abrams
1048
+ * @param arr the array of objects to operate on
1049
+ * @param prop the property to extract from each object
1050
+ * @returns new array containing the corresponding values, in order, of each
1051
+ * object in the original array
1052
+ */
1053
+ declare const extractProp: (arr: any[], prop: string) => any[];
1054
+
1055
+ /**
1056
+ * Compare two arrays of objects by only comparing the values in a specific
1057
+ * property (e.g. compare user arrays by comparing their user.id values)
1058
+ * @author Gabe Abrams
1059
+ * @param a the first array
1060
+ * @param b the second array
1061
+ * @param prop the property to compare with
1062
+ * @returns true if the arrays contain the same objects as determined by
1063
+ * the values associated with each object's prop
1064
+ */
1065
+ declare const compareArraysByProp: (a: any[], b: any[], prop: string) => boolean;
1066
+
1028
1067
  /**
1029
1068
  * List of error codes built into the react kit
1030
1069
  * @author Gabe Abrams
@@ -1040,7 +1079,9 @@ declare enum ReactKitErrorCode {
1040
1079
  NoCACCLGetLaunchInfoFunction = "DRK8",
1041
1080
  NotTTM = "DRK9",
1042
1081
  NotAdmin = "DRK10",
1043
- NotAllowedToReviewLogs = "DRK11"
1082
+ NotAllowedToReviewLogs = "DRK11",
1083
+ ThemeCheckedBeforeReactKitReady = "DRK12",
1084
+ SessionExpiredMessageGottenBeforeReactKitReady = "DRK13"
1044
1085
  }
1045
1086
 
1046
1087
  /**
@@ -1073,4 +1114,4 @@ declare const LogBuiltInMetadata: {
1073
1114
  };
1074
1115
  };
1075
1116
 
1076
- export { AppWrapper, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, IntelliTableColumn, ItemPicker, LoadingSpinner, Log, LogAction, LogBuiltInMetadata, LogMetadataType, LogReviewer, LogSource, LogType, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, alert, avg, canReviewLogs, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genCSV, genRouteHandler, getHumanReadableDate, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initLogCollection, initServer, isMobileOrTablet, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
1117
+ export { AppWrapper, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, IntelliTableColumn, ItemPicker, LoadingSpinner, Log, LogAction, LogBuiltInMetadata, LogMetadataType, LogReviewer, LogSource, LogType, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, alert, avg, canReviewLogs, ceilToNumDecimals, compareArraysByProp, confirm, extractProp, floorToNumDecimals, forceNumIntoBounds, genCSV, genRouteHandler, getHumanReadableDate, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initClient, initLogCollection, initServer, isMobileOrTablet, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "3.2.8",
3
+ "version": "3.3.1",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -0,0 +1,160 @@
1
+ // Import other components
2
+ import { showFatalError } from '../components/AppWrapper';
3
+
4
+ // Import shared types
5
+ import ErrorWithCode from '../errors/ErrorWithCode';
6
+ import ReactKitErrorCode from '../types/ReactKitErrorCode';
7
+
8
+
9
+ /*----------------------------------------*/
10
+ /* ---------------- Types --------------- */
11
+ /*----------------------------------------*/
12
+
13
+ /**
14
+ * Type of CACCL's send request function
15
+ * @author Gabe Abrams
16
+ */
17
+ type SendRequestFunction = (
18
+ opts: {
19
+ path: string;
20
+ method: ('GET' | 'POST' | 'DELETE' | 'PUT');
21
+ params?: {
22
+ [x: string]: any;
23
+ } | undefined;
24
+ headers?: {
25
+ [x: string]: any;
26
+ } | undefined;
27
+ numRetries?: number | undefined;
28
+ },
29
+ ) => Promise<{
30
+ body: any;
31
+ status: number;
32
+ headers: {
33
+ [x: string]: any;
34
+ };
35
+ }>;
36
+
37
+ /*----------------------------------------*/
38
+ /* ---- Static Variables and Getters ---- */
39
+ /*----------------------------------------*/
40
+
41
+ /* ----------- Initialized ---------- */
42
+
43
+ let initialized = false;
44
+
45
+ /* ---------- Send Request ---------- */
46
+
47
+ let storedSendRequest: SendRequestFunction;
48
+
49
+ /**
50
+ * Get the send request function
51
+ * @author Gabe Abrams
52
+ * @returns sendRequest function
53
+ */
54
+ export const getSendRequest = () => {
55
+ // Error if no send request function
56
+ if (!initialized) {
57
+ showFatalError(
58
+ new ErrorWithCode(
59
+ 'Could not send a request because the request needed to be sent before dce-reactkit was properly initialized. Perhaps dce-reactkit was not initialized with initClient.',
60
+ ReactKitErrorCode.NoCACCLSendRequestFunction,
61
+ ),
62
+ );
63
+
64
+ // Return dummy function that never resolves
65
+ return (
66
+ () => {
67
+ return new Promise(() => {});
68
+ }
69
+ ) as SendRequestFunction;
70
+ }
71
+
72
+ // Return
73
+ return storedSendRequest;
74
+ };
75
+
76
+ /* ------------ Dark Mode ----------- */
77
+
78
+ let dark: boolean | undefined;
79
+
80
+ /**
81
+ * Get the current dark/light theme
82
+ * @author Gabe Abrams
83
+ * @returns true if the app has a dark theme
84
+ */
85
+ export const darkModeIsOn = () => {
86
+ // Error if not set up yet
87
+ if (!initialized) {
88
+ showFatalError(
89
+ new ErrorWithCode(
90
+ 'Could not check theme color because dce-reactkit was properly initialized. Perhaps dce-reactkit was not initialized with initClient.',
91
+ ReactKitErrorCode.ThemeCheckedBeforeReactKitReady,
92
+ ),
93
+ );
94
+ return false;
95
+ }
96
+
97
+ // Return
98
+ return !!dark;
99
+ };
100
+
101
+ /* ----- Session Expired Message ---- */
102
+
103
+ let sessionExpiredMessage: string | undefined;
104
+
105
+ /**
106
+ * Get the custom session expired message
107
+ * @author Gabe Abrams
108
+ * @returns session expired message
109
+ */
110
+ export const getSessionExpiredMessage = () => {
111
+ // Error if not set up yet
112
+ if (!initialized) {
113
+ showFatalError(
114
+ new ErrorWithCode(
115
+ 'Could not get the session expired message because dce-reactkit was properly initialized. Perhaps dce-reactkit was not initialized with initClient.',
116
+ ReactKitErrorCode.SessionExpiredMessageGottenBeforeReactKitReady,
117
+ ),
118
+ );
119
+ return '';
120
+ }
121
+
122
+ // Return
123
+ return (
124
+ sessionExpiredMessage
125
+ ?? 'Your session has expired. Please go back to Canvas and start over.'
126
+ );
127
+ };
128
+
129
+ /*----------------------------------------*/
130
+ /* ---------------- Init ---------------- */
131
+ /*----------------------------------------*/
132
+
133
+ /**
134
+ * Initialize the client-side version of reactkit
135
+ * @author Gabe Abrams
136
+ * @param opts object containing all arguments
137
+ * @param opts.sendRequest caccl send request functions
138
+ * @param [opts.dark] if true, the app is a dark-themed app
139
+ * @param [opts.sessionExpiredMessage] a custom session expired message
140
+ */
141
+ const initClient = (
142
+ opts: {
143
+ // Copy of CACCL's send request function
144
+ sendRequest: SendRequestFunction,
145
+ // True if this app is a dark-themed app
146
+ dark?: boolean,
147
+ // Custom session expired message
148
+ sessionExpiredMessage?: string,
149
+ },
150
+ ) => {
151
+ // Store values
152
+ storedSendRequest = opts.sendRequest;
153
+ dark = !!opts.dark;
154
+ sessionExpiredMessage = opts.sessionExpiredMessage;
155
+
156
+ // Mark as initialized
157
+ initialized = true;
158
+ };
159
+
160
+ export default initClient;
@@ -15,14 +15,20 @@ import ReactKitErrorCode from '../types/ReactKitErrorCode';
15
15
  import ModalButtonType from '../types/ModalButtonType';
16
16
  import ModalType from '../types/ModalType';
17
17
  import Variant from '../types/Variant';
18
+ import LogBuiltInMetadata from '../types/LogBuiltInMetadata';
18
19
 
19
20
  // Import shared components
20
21
  import Modal from './Modal';
21
22
 
22
23
  // Import custom errors
23
24
  import ErrorWithCode from '../errors/ErrorWithCode';
25
+
26
+ // Import other helpers
24
27
  import logClientEvent from '../helpers/logClientEvent';
25
- import LogBuiltInMetadata from '../types/LogBuiltInMetadata';
28
+ import {
29
+ darkModeIsOn,
30
+ getSessionExpiredMessage,
31
+ } from '../client/initClient';
26
32
 
27
33
  /*------------------------------------------------------------------------*/
28
34
  /* Props */
@@ -31,64 +37,12 @@ import LogBuiltInMetadata from '../types/LogBuiltInMetadata';
31
37
  type Props = {
32
38
  // The entire app
33
39
  children: React.ReactNode,
34
- // Copy of CACCL's send request function
35
- sendRequest: SendRequestFunction,
36
- // True if this app is a dark-themed app
37
- dark?: boolean,
38
- // Custom session expired message
39
- sessionExpiredMessage?: string,
40
40
  };
41
41
 
42
- // Type of CACCL's send request function
43
- type SendRequestFunction = (
44
- opts: {
45
- path: string;
46
- method: ('GET' | 'POST' | 'DELETE' | 'PUT');
47
- params?: {
48
- [x: string]: any;
49
- } | undefined;
50
- headers?: {
51
- [x: string]: any;
52
- } | undefined;
53
- numRetries?: number | undefined;
54
- },
55
- ) => Promise<{
56
- body: any;
57
- status: number;
58
- headers: {
59
- [x: string]: any;
60
- };
61
- }>;
62
-
63
42
  /*------------------------------------------------------------------------*/
64
43
  /* Static Helpers */
65
44
  /*------------------------------------------------------------------------*/
66
45
 
67
- /*----------------------------------------*/
68
- /* Send Request */
69
- /*----------------------------------------*/
70
-
71
- // Store copy of caccl send request
72
- let _cacclSendRequest: SendRequestFunction;
73
-
74
- /**
75
- * Send a request using caccl's send request feature
76
- * @author Gabe Abrams
77
- * @param opts send request options
78
- * @returns send request response
79
- */
80
- export const cacclSendRequest: SendRequestFunction = async (opts) => {
81
- // Make sure send request has been passed in
82
- if (!_cacclSendRequest) {
83
- throw new ErrorWithCode(
84
- `\nThe request could not be sent because the AppWrapper component does not have a copy of sendRequest from CACCL.\nIf you are currently writing tests for your app, this means you did not properly stub the server response.\nMethod: ${opts.method}\nPath: ${opts.path}\n\n`,
85
- ReactKitErrorCode.NoCACCLSendRequestFunction,
86
- );
87
- }
88
-
89
- return _cacclSendRequest(opts);
90
- };
91
-
92
46
  /*----------------------------------------*/
93
47
  /* Alert */
94
48
  /*----------------------------------------*/
@@ -283,14 +237,8 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
283
237
 
284
238
  const {
285
239
  children,
286
- sendRequest,
287
- dark,
288
- sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.',
289
240
  } = props;
290
241
 
291
- // Store copy of send request
292
- _cacclSendRequest = sendRequest;
293
-
294
242
  /* -------------- State ------------- */
295
243
 
296
244
  // Fatal error
@@ -421,7 +369,7 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
421
369
  const error = (
422
370
  sessionHasExpired
423
371
  ? new ErrorWithCode(
424
- sessionExpiredMessage,
372
+ getSessionExpiredMessage(),
425
373
  ReactKitErrorCode.SessionExpired,
426
374
  )
427
375
  : new ErrorWithCode(
@@ -439,7 +387,7 @@ const AppWrapper: React.FC<Props> = (props: Props): React.ReactElement => {
439
387
  minHeight: '100vh',
440
388
  paddingTop: '2rem',
441
389
  backgroundColor: (
442
- dark
390
+ darkModeIsOn()
443
391
  ? '#222'
444
392
  : '#fff'
445
393
  ),
@@ -0,0 +1,37 @@
1
+ // Import shared helpers
2
+ import extractProp from './extractProp';
3
+
4
+ /**
5
+ * Compare two arrays of objects by only comparing the values in a specific
6
+ * property (e.g. compare user arrays by comparing their user.id values)
7
+ * @author Gabe Abrams
8
+ * @param a the first array
9
+ * @param b the second array
10
+ * @param prop the property to compare with
11
+ * @returns true if the arrays contain the same objects as determined by
12
+ * the values associated with each object's prop
13
+ */
14
+ const compareArraysByProp = (a: any[], b: any[], prop: string): boolean => {
15
+ // Extract values for comparison
16
+ const aVals = new Set(extractProp(a, prop));
17
+ const bVals = new Set(extractProp(b, prop));
18
+
19
+ // Compare sizes first
20
+ if (aVals.size !== bVals.size) {
21
+ return false;
22
+ }
23
+
24
+ // Same number of items. Make sure every object in aVals appears in bVals
25
+ // (if so, they should be equivalent since the sizes are the same)
26
+ // > Create map of items in bVals
27
+ const inBVals: { [k: string]: boolean } = {}; // item => true if in bVals
28
+ Array.from(bVals.values()).forEach((item) => {
29
+ inBVals[item] = true;
30
+ });
31
+ // > Loop through aVals and make sure every item is in bVals
32
+ return Array.from(aVals.values()).every((item) => {
33
+ return inBVals[item];
34
+ });
35
+ };
36
+
37
+ export default compareArraysByProp;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * For every element in an array, extract the value of a prop
3
+ * (e.g. for all user objects, extract their ages and put that into a new
4
+ * ages array)
5
+ * @author Gabe Abrams
6
+ * @param arr the array of objects to operate on
7
+ * @param prop the property to extract from each object
8
+ * @returns new array containing the corresponding values, in order, of each
9
+ * object in the original array
10
+ */
11
+ const extractProp = (arr: any[], prop: string) => {
12
+ return arr.map((item) => {
13
+ return item[prop];
14
+ });
15
+ };
16
+
17
+ export default extractProp;
@@ -2,8 +2,8 @@
2
2
  import ErrorWithCode from '../errors/ErrorWithCode';
3
3
  import ReactKitErrorCode from '../types/ReactKitErrorCode';
4
4
 
5
- // Import helpers from app wrapper
6
- import { cacclSendRequest } from '../components/AppWrapper';
5
+ // Import helpers
6
+ import { getSendRequest } from '../client/initClient';
7
7
 
8
8
  /*------------------------------------------------------------------------*/
9
9
  /* Listener */
@@ -138,7 +138,8 @@ const visitServerEndpoint = async (
138
138
  }
139
139
 
140
140
  // Send the request
141
- const response = await cacclSendRequest({
141
+ const sendRequest = getSendRequest();
142
+ const response = await sendRequest({
142
143
  path: opts.path,
143
144
  method: opts.method ?? 'GET',
144
145
  params: opts.params,
package/src/index.ts CHANGED
@@ -30,6 +30,7 @@ import DAY_IN_MS from './constants/DAY_IN_MS';
30
30
  import DynamicWord from './dynamicConstants/DynamicWord';
31
31
 
32
32
  // Import helpers
33
+ import initClient from './client/initClient';
33
34
  import abbreviate from './helpers/abbreviate';
34
35
  import avg from './helpers/avg';
35
36
  import ceilToNumDecimals from './helpers/ceilToNumDecimals';
@@ -60,6 +61,8 @@ import getMonthName from './helpers/getMonthName';
60
61
  import genCSV from './helpers/genCSV';
61
62
  import canReviewLogs from './helpers/canReviewLogs';
62
63
  import isMobileOrTablet from './helpers/isMobileOrTablet';
64
+ import extractProp from './helpers/extractProp';
65
+ import compareArraysByProp from './helpers/compareArraysByProp';
63
66
 
64
67
  // Import types
65
68
  import ModalButtonType from './types/ModalButtonType';
@@ -137,7 +140,10 @@ export {
137
140
  genCSV,
138
141
  canReviewLogs,
139
142
  isMobileOrTablet,
143
+ extractProp,
144
+ compareArraysByProp,
140
145
  // Client helpers
146
+ initClient,
141
147
  visitServerEndpoint,
142
148
  logClientEvent,
143
149
  // Server helpers
@@ -1,4 +1,4 @@
1
- // Highest error code = DRK11
1
+ // Highest error code = DRK13
2
2
 
3
3
  /**
4
4
  * List of error codes built into the react kit
@@ -16,6 +16,8 @@ enum ReactKitErrorCode {
16
16
  NotTTM = 'DRK9',
17
17
  NotAdmin = 'DRK10',
18
18
  NotAllowedToReviewLogs = 'DRK11',
19
+ ThemeCheckedBeforeReactKitReady = 'DRK12',
20
+ SessionExpiredMessageGottenBeforeReactKitReady = 'DRK13',
19
21
  }
20
22
 
21
23
  export default ReactKitErrorCode;