@etsoo/react 1.3.52 → 1.3.56

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.
@@ -1,5 +1,4 @@
1
1
  import { CoreApp, createClient } from '@etsoo/appscript';
2
- import { Utils } from '@etsoo/shared';
3
2
  import React from 'react';
4
3
  import { NotifierMU } from '../mu/NotifierMU';
5
4
  import { ProgressCount } from '../mu/ProgressCount';
@@ -19,16 +18,29 @@ let globalApp;
19
18
  */
20
19
  export function ReactAppStateDetector(props) {
21
20
  // Destruct
22
- const { update } = props;
21
+ const { targetFields, update } = props;
23
22
  // Context
24
23
  const { state } = globalApp == null
25
24
  ? { state: {} }
26
25
  : React.useContext(globalApp.userState.context);
26
+ // Match fields
27
+ const changedFields = state.lastChangedFields;
28
+ let matchedFields;
29
+ if (targetFields == null || changedFields == null) {
30
+ matchedFields = changedFields;
31
+ }
32
+ else {
33
+ matchedFields = [];
34
+ targetFields.forEach((targetField) => {
35
+ if (changedFields.includes(targetField))
36
+ matchedFields === null || matchedFields === void 0 ? void 0 : matchedFields.push(targetField);
37
+ });
38
+ }
27
39
  // Ready
28
40
  React.useEffect(() => {
29
41
  // Callback
30
- update(state.authorized);
31
- }, [state.authorized]);
42
+ update(state.authorized, matchedFields);
43
+ }, [state.authorized, matchedFields]);
32
44
  // return
33
45
  return React.createElement(React.Fragment);
34
46
  }
@@ -198,15 +210,10 @@ export class ReactApp extends CoreApp {
198
210
  * @param keep Keep in local storage or not
199
211
  */
200
212
  userLogin(user, refreshToken, keep) {
201
- // Changed
202
- const dataChanged = !this.authorized ||
203
- this.userData == null ||
204
- !Utils.objectEqual(this.userData, user, ['seconds', 'token']);
205
213
  // Super call, set token
206
214
  super.userLogin(user, refreshToken, keep);
207
215
  // Dispatch action
208
- // Only when unauthorized or with changes except 'token' and 'seconds'
209
- if (this.userStateDispatch != null && dataChanged)
216
+ if (this.userStateDispatch != null)
210
217
  this.userStateDispatch({
211
218
  type: UserActionType.Login,
212
219
  user
@@ -22,23 +22,18 @@ export const CommonPageScrollContainer = global;
22
22
  */
23
23
  export function CommonPage(props) {
24
24
  // Destruct
25
- const { children, disableGutters = true, fabButtons, fabColumnDirection, fabPaddingAdjust = 1.5, fabSize = 'small', maxWidth = false, moreActions, onRefresh, onUpdate, onUpdateAll, paddings = MUGlobal.pagePaddings, scrollContainer, pullContainer, sx = {}, ...rest } = props;
25
+ const { children, disableGutters = true, fabButtons, fabColumnDirection, fabPaddingAdjust = 1.5, fabSize = 'small', maxWidth = false, moreActions, onRefresh, onUpdate, onUpdateAll, paddings = MUGlobal.pagePaddings, scrollContainer, targetFields, pullContainer, sx = {}, ...rest } = props;
26
26
  // Merge style
27
27
  Object.assign(sx, {
28
28
  padding: paddings
29
29
  });
30
- const [state] = React.useState({});
31
30
  // Fab padding
32
31
  const fabPadding = MUGlobal.increase(MUGlobal.pagePaddings, fabPaddingAdjust);
33
32
  // Labels
34
33
  const labels = Labels.CommonPage;
35
34
  // Update
36
35
  const update = onUpdateAll
37
- ? async (authorized) => {
38
- state.loading = true;
39
- await onUpdateAll(authorized);
40
- state.firstLoaded = true;
41
- }
36
+ ? onUpdateAll
42
37
  : onUpdate
43
38
  ? (authorized) => {
44
39
  if (authorized == null || authorized)
@@ -50,25 +45,9 @@ export function CommonPage(props) {
50
45
  onRefresh();
51
46
  }
52
47
  : undefined;
53
- React.useEffect(() => {
54
- return () => {
55
- state.loading = false;
56
- state.firstLoaded = false;
57
- };
58
- }, []);
59
- React.useEffect(() => {
60
- if (state.loading) {
61
- state.loading = false;
62
- return;
63
- }
64
- // onUpdateAll support to load after page loaded
65
- if (onUpdateAll != null && state.firstLoaded) {
66
- onUpdateAll();
67
- }
68
- });
69
48
  // Return the UI
70
49
  return (React.createElement(React.Fragment, null,
71
- update && React.createElement(ReactAppStateDetector, { update: update }),
50
+ update && (React.createElement(ReactAppStateDetector, { targetFields: targetFields, update: update })),
72
51
  React.createElement(Container, { disableGutters: disableGutters, maxWidth: maxWidth, sx: sx, id: "page-container", ...rest },
73
52
  React.createElement(FabBox, { sx: {
74
53
  zIndex: 1,
@@ -52,4 +52,8 @@ export interface CommonPageProps extends Omit<ContainerProps, 'id'> {
52
52
  * Scroll container
53
53
  */
54
54
  scrollContainer?: HTMLElement | object;
55
+ /**
56
+ * State last changed fields
57
+ */
58
+ targetFields?: string[];
55
59
  }
@@ -17,12 +17,19 @@ export interface IUpdate<S extends IState, A extends IAction> {
17
17
  * State update interface
18
18
  */
19
19
  export interface IStateUpdate {
20
- (authorized?: boolean): PromiseLike<void> | void;
20
+ (authorized?: boolean, matchedFields?: string[]): PromiseLike<void> | void;
21
21
  }
22
22
  /**
23
23
  * State update props
24
24
  */
25
25
  export interface IStateProps {
26
+ /**
27
+ * State last changed fields
28
+ */
29
+ targetFields?: string[];
30
+ /**
31
+ * State update callback
32
+ */
26
33
  update: IStateUpdate;
27
34
  }
28
35
  /**
@@ -53,4 +53,5 @@ export declare class UserState<D extends IUser> {
53
53
  * Constructor
54
54
  */
55
55
  constructor();
56
+ private getChangedFields;
56
57
  }
@@ -1,3 +1,4 @@
1
+ import { Utils } from '@etsoo/shared';
1
2
  import { State } from './State';
2
3
  /**
3
4
  * User action type
@@ -26,10 +27,18 @@ export class UserState {
26
27
  // User reducer
27
28
  switch (action.type) {
28
29
  case UserActionType.Login:
29
- return { ...action.user, authorized: true };
30
+ const lastChangedFields = state.authorized && action.user
31
+ ? this.getChangedFields(action.user, state)
32
+ : [];
33
+ return {
34
+ ...action.user,
35
+ authorized: true,
36
+ lastChangedFields
37
+ };
30
38
  case UserActionType.Logout:
31
39
  return {
32
40
  ...state,
41
+ lastChangedFields: undefined,
33
42
  token: undefined,
34
43
  authorized: false // Flag as unauthorized
35
44
  };
@@ -37,6 +46,7 @@ export class UserState {
37
46
  if (action.update) {
38
47
  var newState = { ...state };
39
48
  action.update(newState);
49
+ newState.lastChangedFields = this.getChangedFields(newState, state);
40
50
  return newState;
41
51
  }
42
52
  return state;
@@ -46,6 +56,7 @@ export class UserState {
46
56
  return state;
47
57
  return {
48
58
  ...state,
59
+ lastChangedFields: undefined,
49
60
  authorized: false // Flag as unauthorized
50
61
  };
51
62
  default:
@@ -55,4 +66,10 @@ export class UserState {
55
66
  this.context = context;
56
67
  this.provider = provider;
57
68
  }
69
+ getChangedFields(input, init) {
70
+ return Utils.objectUpdated(input, init, [
71
+ 'seconds',
72
+ 'lastChangedFields'
73
+ ]);
74
+ }
58
75
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/react",
3
- "version": "1.3.52",
3
+ "version": "1.3.56",
4
4
  "description": "TypeScript ReactJs framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,9 +50,9 @@
50
50
  "@emotion/react": "^11.7.1",
51
51
  "@emotion/style": "^0.8.0",
52
52
  "@emotion/styled": "^11.6.0",
53
- "@etsoo/appscript": "^1.1.77",
53
+ "@etsoo/appscript": "^1.1.79",
54
54
  "@etsoo/notificationbase": "^1.0.94",
55
- "@etsoo/shared": "^1.0.76",
55
+ "@etsoo/shared": "^1.0.78",
56
56
  "@mui/icons-material": "^5.2.4",
57
57
  "@mui/material": "^5.2.4",
58
58
  "@reach/router": "^1.3.4",
@@ -89,7 +89,7 @@
89
89
  "eslint-plugin-react": "^7.27.1",
90
90
  "jest": "^27.4.5",
91
91
  "react-test-renderer": "^17.0.2",
92
- "ts-jest": "^27.1.1",
92
+ "ts-jest": "^27.1.2",
93
93
  "typescript": "^4.5.4"
94
94
  }
95
95
  }
@@ -7,7 +7,7 @@ import {
7
7
  IUserData
8
8
  } from '@etsoo/appscript';
9
9
  import { NotificationRenderProps } from '@etsoo/notificationbase';
10
- import { DataTypes, Utils } from '@etsoo/shared';
10
+ import { DataTypes } from '@etsoo/shared';
11
11
  import React from 'react';
12
12
  import { NotifierMU } from '../mu/NotifierMU';
13
13
  import { ProgressCount } from '../mu/ProgressCount';
@@ -42,7 +42,7 @@ let globalApp: IReactApp<IAppSettings, any, IPageData>;
42
42
  */
43
43
  export function ReactAppStateDetector(props: IStateProps) {
44
44
  // Destruct
45
- const { update } = props;
45
+ const { targetFields, update } = props;
46
46
 
47
47
  // Context
48
48
  const { state } =
@@ -52,11 +52,24 @@ export function ReactAppStateDetector(props: IStateProps) {
52
52
  (globalApp.userState as UserState<IUser>).context
53
53
  );
54
54
 
55
+ // Match fields
56
+ const changedFields = state.lastChangedFields;
57
+ let matchedFields: string[] | undefined;
58
+ if (targetFields == null || changedFields == null) {
59
+ matchedFields = changedFields;
60
+ } else {
61
+ matchedFields = [];
62
+ targetFields.forEach((targetField) => {
63
+ if (changedFields.includes(targetField))
64
+ matchedFields?.push(targetField);
65
+ });
66
+ }
67
+
55
68
  // Ready
56
69
  React.useEffect(() => {
57
70
  // Callback
58
- update(state.authorized);
59
- }, [state.authorized]);
71
+ update(state.authorized, matchedFields);
72
+ }, [state.authorized, matchedFields]);
60
73
 
61
74
  // return
62
75
  return React.createElement(React.Fragment);
@@ -343,18 +356,11 @@ export class ReactApp<
343
356
  refreshToken: string,
344
357
  keep?: boolean
345
358
  ): void {
346
- // Changed
347
- const dataChanged =
348
- !this.authorized ||
349
- this.userData == null ||
350
- !Utils.objectEqual(this.userData, user, ['seconds', 'token']);
351
-
352
359
  // Super call, set token
353
360
  super.userLogin(user, refreshToken, keep);
354
361
 
355
362
  // Dispatch action
356
- // Only when unauthorized or with changes except 'token' and 'seconds'
357
- if (this.userStateDispatch != null && dataChanged)
363
+ if (this.userStateDispatch != null)
358
364
  this.userStateDispatch({
359
365
  type: UserActionType.Login,
360
366
  user
@@ -40,6 +40,7 @@ export function CommonPage(props: CommonPageProps) {
40
40
  onUpdateAll,
41
41
  paddings = MUGlobal.pagePaddings,
42
42
  scrollContainer,
43
+ targetFields,
43
44
  pullContainer,
44
45
  sx = {},
45
46
  ...rest
@@ -50,11 +51,6 @@ export function CommonPage(props: CommonPageProps) {
50
51
  padding: paddings
51
52
  });
52
53
 
53
- const [state] = React.useState<{
54
- firstLoaded?: boolean;
55
- loading?: boolean;
56
- }>({});
57
-
58
54
  // Fab padding
59
55
  const fabPadding = MUGlobal.increase(
60
56
  MUGlobal.pagePaddings,
@@ -66,11 +62,7 @@ export function CommonPage(props: CommonPageProps) {
66
62
 
67
63
  // Update
68
64
  const update = onUpdateAll
69
- ? async (authorized?: boolean) => {
70
- state.loading = true;
71
- await onUpdateAll(authorized);
72
- state.firstLoaded = true;
73
- }
65
+ ? onUpdateAll
74
66
  : onUpdate
75
67
  ? (authorized?: boolean) => {
76
68
  if (authorized == null || authorized) onUpdate();
@@ -81,29 +73,15 @@ export function CommonPage(props: CommonPageProps) {
81
73
  }
82
74
  : undefined;
83
75
 
84
- React.useEffect(() => {
85
- return () => {
86
- state.loading = false;
87
- state.firstLoaded = false;
88
- };
89
- }, []);
90
-
91
- React.useEffect(() => {
92
- if (state.loading) {
93
- state.loading = false;
94
- return;
95
- }
96
-
97
- // onUpdateAll support to load after page loaded
98
- if (onUpdateAll != null && state.firstLoaded) {
99
- onUpdateAll();
100
- }
101
- });
102
-
103
76
  // Return the UI
104
77
  return (
105
78
  <React.Fragment>
106
- {update && <ReactAppStateDetector update={update} />}
79
+ {update && (
80
+ <ReactAppStateDetector
81
+ targetFields={targetFields}
82
+ update={update}
83
+ />
84
+ )}
107
85
  <Container
108
86
  disableGutters={disableGutters}
109
87
  maxWidth={maxWidth}
@@ -62,4 +62,9 @@ export interface CommonPageProps extends Omit<ContainerProps, 'id'> {
62
62
  * Scroll container
63
63
  */
64
64
  scrollContainer?: HTMLElement | object;
65
+
66
+ /**
67
+ * State last changed fields
68
+ */
69
+ targetFields?: string[];
65
70
  }
@@ -20,13 +20,21 @@ export interface IUpdate<S extends IState, A extends IAction> {
20
20
  * State update interface
21
21
  */
22
22
  export interface IStateUpdate {
23
- (authorized?: boolean): PromiseLike<void> | void;
23
+ (authorized?: boolean, matchedFields?: string[]): PromiseLike<void> | void;
24
24
  }
25
25
 
26
26
  /**
27
27
  * State update props
28
28
  */
29
29
  export interface IStateProps {
30
+ /**
31
+ * State last changed fields
32
+ */
33
+ targetFields?: string[];
34
+
35
+ /**
36
+ * State update callback
37
+ */
30
38
  update: IStateUpdate;
31
39
  }
32
40
 
@@ -1,4 +1,5 @@
1
1
  import { IAction, IUser, IUserData } from '@etsoo/appscript';
2
+ import { Utils } from '@etsoo/shared';
2
3
  import { IProviderProps, IUpdate } from './IState';
3
4
  import { State } from './State';
4
5
 
@@ -73,10 +74,19 @@ export class UserState<D extends IUser> {
73
74
  // User reducer
74
75
  switch (action.type) {
75
76
  case UserActionType.Login:
76
- return { ...action.user!, authorized: true } as D;
77
+ const lastChangedFields =
78
+ state.authorized && action.user
79
+ ? this.getChangedFields(action.user, state)
80
+ : [];
81
+ return {
82
+ ...action.user!,
83
+ authorized: true,
84
+ lastChangedFields
85
+ } as D;
77
86
  case UserActionType.Logout:
78
87
  return {
79
88
  ...state, // Keep other user data
89
+ lastChangedFields: undefined,
80
90
  token: undefined, // Remove token
81
91
  authorized: false // Flag as unauthorized
82
92
  };
@@ -84,6 +94,10 @@ export class UserState<D extends IUser> {
84
94
  if (action.update) {
85
95
  var newState = { ...state };
86
96
  action.update(newState);
97
+ newState.lastChangedFields = this.getChangedFields(
98
+ newState,
99
+ state
100
+ );
87
101
  return newState;
88
102
  }
89
103
  return state;
@@ -93,6 +107,7 @@ export class UserState<D extends IUser> {
93
107
 
94
108
  return {
95
109
  ...state, // Keep other user data and token for refresh
110
+ lastChangedFields: undefined,
96
111
  authorized: false // Flag as unauthorized
97
112
  };
98
113
  default:
@@ -106,4 +121,11 @@ export class UserState<D extends IUser> {
106
121
  this.context = context;
107
122
  this.provider = provider;
108
123
  }
124
+
125
+ private getChangedFields(input: {}, init: {}) {
126
+ return Utils.objectUpdated(input, init, [
127
+ 'seconds',
128
+ 'lastChangedFields'
129
+ ]);
130
+ }
109
131
  }