@equinor/fusion-framework-react-app 5.3.1 → 5.4.0

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,3 @@
1
+ export { useAppSetting } from './useAppSetting';
2
+ export { useAppSettings } from './useAppSettings';
3
+ export type { AppSettings } from '@equinor/fusion-framework-module-app';
@@ -0,0 +1,42 @@
1
+ import { type AppSettingsStatusHooks } from './useAppSettingsStatus';
2
+ import type { AppSettings } from '@equinor/fusion-framework-module-app';
3
+ type UpdateSettingFunction<T, O = T> = (currentSetting: T | undefined) => O;
4
+ /**
5
+ * Custom hook to manage application settings.
6
+ *
7
+ * @template TSettings - The type of the settings object. Defaults to `AppSettings`.
8
+ * @template TProp - The type of the property key in the settings object. Defaults to `keyof TSettings`.
9
+ *
10
+ * @param {TProp} prop - The property key in the settings object to manage.
11
+ * @param {TSettings[TProp]} [defaultValue] - The default value for the setting.
12
+ * @param hooks - Optional hooks to handle the status changes and errors.
13
+ *
14
+ * @returns {Array} An array containing:
15
+ * - `setting`: The current setting value or undefined.
16
+ * - `setSetting`: A function to update the setting.
17
+ *
18
+ * @example
19
+ * const { setting, setSetting } = useAppSetting('theme');
20
+ *
21
+ * @example
22
+ * // with default value
23
+ * const { setting, setSetting } = useAppSetting('theme', 'dark');
24
+ *
25
+ * @example
26
+ * // with hooks
27
+ * const [isLoading, setIsLoading] = useState(false);
28
+ * const [isUpdating, setIsUpdating] = useState(false);
29
+ * const [error, setError] = useState<Error | null>(null);
30
+ *
31
+ * const { setting, setSetting } = useAppSetting('theme', 'dark', {
32
+ * onLoading: setIsLoading,
33
+ * onUpdating: setIsUpdating,
34
+ * onError: setError,
35
+ * onUpdated: useCallback(() => console.log('Settings updated'), [])
36
+ * });
37
+ */
38
+ export declare const useAppSetting: <TSettings extends Record<string, unknown> = AppSettings, TProp extends keyof TSettings = keyof TSettings>(prop: TProp, defaultValue?: TSettings[TProp], hooks?: AppSettingsStatusHooks & {
39
+ onError?: (error: Error | null) => void;
40
+ onUpdated?: () => void;
41
+ }) => [TSettings[TProp] | undefined, (update: TSettings[TProp] | UpdateSettingFunction<TSettings[TProp]>) => void];
42
+ export default useAppSetting;
@@ -0,0 +1,44 @@
1
+ import { type AppSettings } from '@equinor/fusion-framework-module-app';
2
+ import { type AppSettingsStatusHooks } from './useAppSettingsStatus';
3
+ type UpdateSettingsFunction<T, O = T> = (currentSettings: T | undefined) => O;
4
+ /**
5
+ * Custom hook to manage application settings.
6
+ *
7
+ * @template TSettings - The type of the settings object, extending Record<string, any>. Defaults to AppSettings.
8
+ *
9
+ * @param {TSettings} [defaultValue] - The default value for the settings.
10
+ * @param hooks - Optional hooks to handle the status changes and errors.
11
+ *
12
+ * @note
13
+ * `defaultValue` will only be used on the first render.
14
+ * `hooks`must be memoized to avoid unnecessary re-renders.
15
+ *
16
+ * @returns {Array} An array containing:
17
+ * - `settings`: The current settings object.
18
+ * - `setSettings`: A function to update the settings.
19
+ *
20
+ * @example
21
+ * const [settings, setSettings] = useAppSettings();
22
+ *
23
+ * @example
24
+ * const [settings, setSettings] = useAppSettings({ theme: 'dark' });
25
+ *
26
+ * @example
27
+ * const [isLoading, setIsLoading] = useState(false);
28
+ * const [isUpdating, setIsUpdating] = useState(false);
29
+ * const [error, setError] = useState<Error | null>(null);
30
+ *
31
+ * const onUpdated = useCallback(() => console.log('Settings updated'), []);
32
+ *
33
+ * const [settings, setSettings] = useAppSettings({ theme: 'dark' }, {
34
+ * onLoading: setIsLoading,
35
+ * onUpdating: setIsUpdating,
36
+ * onError: setError,
37
+ * onUpdated,
38
+ * });
39
+ */
40
+ export declare const useAppSettings: <TSettings extends Record<string, unknown> = AppSettings>(defaultValue?: TSettings, hooks?: AppSettingsStatusHooks & {
41
+ onError?: (error: Error | null) => void;
42
+ onUpdated?: () => void;
43
+ }) => [TSettings, (settings: TSettings | UpdateSettingsFunction<TSettings>) => void];
44
+ export default useAppSettings;
@@ -0,0 +1,23 @@
1
+ import type { IApp } from '@equinor/fusion-framework-module-app';
2
+ export type AppSettingsStatusHooks = {
3
+ onLoading?: (isLoading: boolean) => void;
4
+ onUpdating?: (isUpdating: boolean) => void;
5
+ };
6
+ /**
7
+ * Custom hook to handle app settings status updates.
8
+ *
9
+ * @param {IApp | null} app - The app instance to monitor settings status.
10
+ * @param {AppSettingsStatusHooks} [hooks] - Optional hooks to handle loading and updating status.
11
+ * @param {function} [hooks.onLoading] - Callback function to handle loading status.
12
+ * @param {function} [hooks.onUpdating] - Callback function to handle updating status.
13
+ *
14
+ * @returns {void}
15
+ *
16
+ * @example
17
+ * const hooks = useMemo(() => ({
18
+ * onLoading: (isLoading) => console.log('Loading:', isLoading),
19
+ * onUpdating: (isUpdating) => console.log('Updating:', isUpdating),
20
+ * }, []);
21
+ * useAppSettingsStatus(app, hooks);
22
+ */
23
+ export declare const useAppSettingsStatus: (app: IApp | null, hooks?: AppSettingsStatusHooks) => void;
@@ -1 +1 @@
1
- export declare const version = "5.3.1";
1
+ export declare const version = "5.4.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-react-app",
3
- "version": "5.3.1",
3
+ "version": "5.4.0",
4
4
  "description": "",
5
5
  "main": "./dist/esm/index.js",
6
6
  "types": "./dist/types/index.d.ts",
@@ -37,6 +37,10 @@
37
37
  "types": "./dist/types/navigation/index.d.ts",
38
38
  "import": "./dist/esm/navigation/index.js"
39
39
  },
40
+ "./settings": {
41
+ "types": "./dist/types/settings/index.d.ts",
42
+ "import": "./dist/esm/settings/index.js"
43
+ },
40
44
  "./widget": {
41
45
  "types": "./dist/types/widget/index.d.ts",
42
46
  "import": "./dist/esm/widget/index.js"
@@ -65,6 +69,9 @@
65
69
  "navigation": [
66
70
  "dist/types/navigation/index.d.ts"
67
71
  ],
72
+ "settings": [
73
+ "dist/types/settings/index.d.ts"
74
+ ],
68
75
  "widget": [
69
76
  "dist/types/widget/index.d.ts"
70
77
  ]
@@ -82,13 +89,13 @@
82
89
  "directory": "packages/react"
83
90
  },
84
91
  "dependencies": {
85
- "@equinor/fusion-framework-app": "^9.1.14",
86
- "@equinor/fusion-framework-module-navigation": "^4.0.7",
92
+ "@equinor/fusion-framework-app": "^9.1.15",
87
93
  "@equinor/fusion-framework-module": "^4.3.5",
94
+ "@equinor/fusion-framework-module-app": "^6.1.0",
95
+ "@equinor/fusion-framework-module-navigation": "^4.0.7",
88
96
  "@equinor/fusion-framework-react": "^7.3.3",
89
- "@equinor/fusion-framework-react-module": "^3.1.6",
90
- "@equinor/fusion-framework-module-app": "^6.0.3",
91
- "@equinor/fusion-framework-react-module-http": "^8.0.0"
97
+ "@equinor/fusion-framework-react-module-http": "^8.0.0",
98
+ "@equinor/fusion-framework-react-module": "^3.1.6"
92
99
  },
93
100
  "devDependencies": {
94
101
  "@types/react": "^18.2.50",
@@ -97,13 +104,13 @@
97
104
  "react-dom": "^18.2.0",
98
105
  "rxjs": "^7.8.1",
99
106
  "typescript": "^5.5.4",
100
- "@equinor/fusion-framework-module-event": "^4.2.4",
101
107
  "@equinor/fusion-framework-module-feature-flag": "^1.1.11",
108
+ "@equinor/fusion-framework-module-event": "^4.2.4",
102
109
  "@equinor/fusion-framework-module-msal": "^3.1.5",
103
110
  "@equinor/fusion-framework-react-module-bookmark": "^2.2.2",
104
- "@equinor/fusion-framework-react-module-context": "^6.2.15",
105
111
  "@equinor/fusion-framework-react-widget": "^1.1.21",
106
- "@equinor/fusion-observable": "^8.4.3"
112
+ "@equinor/fusion-observable": "^8.4.3",
113
+ "@equinor/fusion-framework-react-module-context": "^6.2.15"
107
114
  },
108
115
  "peerDependencies": {
109
116
  "@types/react": "^17.0.0 || ^18.0.0",
@@ -0,0 +1,123 @@
1
+ ## Portal Settings
2
+
3
+ > TBD
4
+
5
+ ## App Settings
6
+
7
+ App settings are a way to store and retrieve settings that are shared across the app. The settings are stored in the configured service of the app module.
8
+
9
+ ```ts
10
+ declare module '@equinor/fusion-framework-react-app/settings' {
11
+ interface AppSettings {
12
+ theme: 'default' | 'light' | 'dark';
13
+ mode: 'simple' | 'advanced';
14
+ }
15
+ }
16
+ useAppSetting('theme', 'default');
17
+
18
+ // Explicit type the setting
19
+ useAppSetting<{notDefined: string}>('notDefined', 'not registered');
20
+ ```
21
+
22
+ ### Example
23
+
24
+ ```tsx
25
+ const MyApp = () => {
26
+ const [ theme, setTheme ] = useAppSetting('theme', 'default');
27
+ const [ mode, setMode ] = useAppSetting('mode', 'simple');
28
+
29
+ // using the setter as a callback
30
+ const toggleMode = useCallback(() => {
31
+ setMode(mode => mode === 'simple' ? 'advanced' : 'simple')
32
+ }, [setMode]);
33
+
34
+ return (
35
+ <MyThemeProvider theme={theme} onChange={setTheme}>
36
+ <Button onClick={toggleMode}>Toggle mode</Button>
37
+ {mode === 'simple' ? <SimpleView /> : <AdvancedView />}
38
+ </MyThemeProvider>
39
+ );
40
+ }
41
+ ```
42
+
43
+ ### Using all settings
44
+
45
+ > [!WARNING]
46
+ > **Using the `setSettings` must include all settings, not just the ones you want to change.**
47
+ > prefer using `setSettings` with a callback function.
48
+
49
+ > [!IMPORTANT]
50
+ > This is not recommended for large apps, as it will cause re-renders on every setting change.
51
+
52
+ ```tsx
53
+ const MyApp = () => {
54
+ const [ settings, setSettings ] = useAppSettings();
55
+
56
+ const updateTheme = useCallback(
57
+ (theme: AppSettings['theme']) => setSettings(settings => ({...settings, theme})),
58
+ [updateSettings]
59
+ );
60
+
61
+ return (
62
+ <MyThemeProvider theme={settings.theme} onChange={updateTheme}>
63
+ {settings.mode === 'simple' ? <SimpleView /> : <AdvancedView />}
64
+ </MyThemeProvider>
65
+ );
66
+ }
67
+ ```
68
+
69
+ ### Using hook callbacks
70
+
71
+ The `useAppSettings` and `useAppSetting` hooks can take callbacks for loading, updating, updated and error handling.
72
+
73
+ > [!NOTE]
74
+ > These callbacks are optional and can be used to show loading spinners, error dialogs or other UI elements.
75
+ >
76
+ > We have chosen to use callbacks as parameters to the hooks, instead of returning them, to avoid unnecessary re-renders.
77
+
78
+ > [!NOTE]
79
+ > `onUpdating` and `onLoading` refers to the global state of the settings, not the individual settings. This means that if you have multiple settings that are being updated, the `onUpdating` and `onLoading` will be true until all settings are updated.
80
+ >
81
+ > Good practice is to disable UI elements that can trigger settings updates when `onUpdating` or `onLoading` is true.
82
+
83
+
84
+ > [!IMPORTANT]
85
+ > Hooks must be memoized to avoid re-renders on every render. Provided callbacks are not internally memoized, to allow consumers to control implementation of these callbacks.
86
+
87
+ ```tsx
88
+
89
+ // state and callback for loading settings
90
+ const [ loading, setLoading ] = useState(false);
91
+
92
+ // state and callback for updating settings
93
+ const [ updating, setUpdating ] = useState(false);
94
+
95
+ // state and callback for error handling
96
+ const [ error, setError ] = useState<Error | null>(null);
97
+
98
+ // callback for when settings are updated
99
+ const onUpdated = useCallback(() => {
100
+ showSnackbar('Settings updated');
101
+ }, [showSnackbar]);
102
+
103
+ const [ settings, setSettings ] = useAppSettings(defaultSettings, {
104
+ onLoading: setLoading,
105
+ onUpdating: setUpdating,
106
+ onError: setError,
107
+ });
108
+
109
+ const updateSettings = useCallback(() => {
110
+ setSettings(/* new settings */);
111
+ }, [setSettings, onUpdated]);
112
+
113
+ return (
114
+ <MyThemeProvider theme={settings.theme}>
115
+ {loading && <Loading />}
116
+ {updating && <Updating />}
117
+ {error && <ErrorDialog error={error} />}
118
+ <Button onClick={updateSettings} disabled={loading||updating}>
119
+ Update settings
120
+ </Button>
121
+ </MyThemeProvider>
122
+ );
123
+ ```
@@ -0,0 +1,4 @@
1
+ export { useAppSetting } from './useAppSetting';
2
+ export { useAppSettings } from './useAppSettings';
3
+
4
+ export type { AppSettings } from '@equinor/fusion-framework-module-app';
@@ -0,0 +1,112 @@
1
+ import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
2
+ import { BehaviorSubject, map } from 'rxjs';
3
+
4
+ import { useCurrentApp } from '@equinor/fusion-framework-react/app';
5
+
6
+ import { useAppSettingsStatus, type AppSettingsStatusHooks } from './useAppSettingsStatus';
7
+
8
+ import type { AppSettings } from '@equinor/fusion-framework-module-app';
9
+ import { useObservableState } from '@equinor/fusion-observable/react';
10
+
11
+ type UpdateSettingFunction<T, O = T> = (currentSetting: T | undefined) => O;
12
+
13
+ /**
14
+ * Custom hook to manage application settings.
15
+ *
16
+ * @template TSettings - The type of the settings object. Defaults to `AppSettings`.
17
+ * @template TProp - The type of the property key in the settings object. Defaults to `keyof TSettings`.
18
+ *
19
+ * @param {TProp} prop - The property key in the settings object to manage.
20
+ * @param {TSettings[TProp]} [defaultValue] - The default value for the setting.
21
+ * @param hooks - Optional hooks to handle the status changes and errors.
22
+ *
23
+ * @returns {Array} An array containing:
24
+ * - `setting`: The current setting value or undefined.
25
+ * - `setSetting`: A function to update the setting.
26
+ *
27
+ * @example
28
+ * const { setting, setSetting } = useAppSetting('theme');
29
+ *
30
+ * @example
31
+ * // with default value
32
+ * const { setting, setSetting } = useAppSetting('theme', 'dark');
33
+ *
34
+ * @example
35
+ * // with hooks
36
+ * const [isLoading, setIsLoading] = useState(false);
37
+ * const [isUpdating, setIsUpdating] = useState(false);
38
+ * const [error, setError] = useState<Error | null>(null);
39
+ *
40
+ * const { setting, setSetting } = useAppSetting('theme', 'dark', {
41
+ * onLoading: setIsLoading,
42
+ * onUpdating: setIsUpdating,
43
+ * onError: setError,
44
+ * onUpdated: useCallback(() => console.log('Settings updated'), [])
45
+ * });
46
+ */
47
+ export const useAppSetting = <
48
+ TSettings extends Record<string, unknown> = AppSettings,
49
+ TProp extends keyof TSettings = keyof TSettings,
50
+ >(
51
+ prop: TProp,
52
+ defaultValue?: TSettings[TProp],
53
+ hooks?: AppSettingsStatusHooks & {
54
+ onError?: (error: Error | null) => void;
55
+ onUpdated?: () => void;
56
+ },
57
+ ): [
58
+ TSettings[TProp] | undefined,
59
+ (update: TSettings[TProp] | UpdateSettingFunction<TSettings[TProp]>) => void,
60
+ ] => {
61
+ const [{ onError, onUpdated, onLoading, onUpdating }] = useState(() => hooks ?? {});
62
+
63
+ const { currentApp = null } = useCurrentApp();
64
+
65
+ // create a subject to manage the setting value
66
+ const subject = useMemo(() => {
67
+ return new BehaviorSubject<TSettings[TProp] | undefined>(defaultValue);
68
+ // Only create a new subject when the current app changes
69
+ // eslint-disable-next-line react-hooks/exhaustive-deps
70
+ }, [currentApp]);
71
+
72
+ useLayoutEffect(() => {
73
+ const sub = currentApp?.settings$
74
+ .pipe(map((settings) => (settings as TSettings)[prop]))
75
+ .subscribe(subject);
76
+ return () => sub?.unsubscribe();
77
+ }, [currentApp, subject, prop]);
78
+
79
+ // subscribe to the setting value
80
+ const { value: setting } = useObservableState(subject);
81
+
82
+ // update function
83
+ const setSetting = useCallback(
84
+ (update: TSettings[TProp] | UpdateSettingFunction<TSettings[TProp]>) => {
85
+ if (!currentApp) {
86
+ return onError?.(new Error('App is not available'));
87
+ }
88
+
89
+ // resolve setting value with the provided value or function
90
+ const value =
91
+ typeof update === 'function'
92
+ ? (update as UpdateSettingFunction<TSettings[TProp]>)(subject.value)
93
+ : update;
94
+
95
+ currentApp.updateSetting<TSettings, TProp>(prop, value).subscribe({
96
+ error: onError,
97
+ complete: onUpdated,
98
+ });
99
+ },
100
+ [currentApp, subject, prop, onError, onUpdated],
101
+ );
102
+
103
+ // status hooks
104
+ useAppSettingsStatus(currentApp, {
105
+ onLoading,
106
+ onUpdating,
107
+ });
108
+
109
+ return [setting, setSetting];
110
+ };
111
+
112
+ export default useAppSetting;
@@ -0,0 +1,99 @@
1
+ import { useCallback, useLayoutEffect, useMemo } from 'react';
2
+ import { BehaviorSubject, Observable } from 'rxjs';
3
+
4
+ import { type AppSettings } from '@equinor/fusion-framework-module-app';
5
+
6
+ import { useCurrentApp } from '@equinor/fusion-framework-react/app';
7
+ import { useObservableState } from '@equinor/fusion-observable/react';
8
+
9
+ import { useAppSettingsStatus, type AppSettingsStatusHooks } from './useAppSettingsStatus';
10
+
11
+ type UpdateSettingsFunction<T, O = T> = (currentSettings: T | undefined) => O;
12
+
13
+ /**
14
+ * Custom hook to manage application settings.
15
+ *
16
+ * @template TSettings - The type of the settings object, extending Record<string, any>. Defaults to AppSettings.
17
+ *
18
+ * @param {TSettings} [defaultValue] - The default value for the settings.
19
+ * @param hooks - Optional hooks to handle the status changes and errors.
20
+ *
21
+ * @note
22
+ * `defaultValue` will only be used on the first render.
23
+ * `hooks`must be memoized to avoid unnecessary re-renders.
24
+ *
25
+ * @returns {Array} An array containing:
26
+ * - `settings`: The current settings object.
27
+ * - `setSettings`: A function to update the settings.
28
+ *
29
+ * @example
30
+ * const [settings, setSettings] = useAppSettings();
31
+ *
32
+ * @example
33
+ * const [settings, setSettings] = useAppSettings({ theme: 'dark' });
34
+ *
35
+ * @example
36
+ * const [isLoading, setIsLoading] = useState(false);
37
+ * const [isUpdating, setIsUpdating] = useState(false);
38
+ * const [error, setError] = useState<Error | null>(null);
39
+ *
40
+ * const onUpdated = useCallback(() => console.log('Settings updated'), []);
41
+ *
42
+ * const [settings, setSettings] = useAppSettings({ theme: 'dark' }, {
43
+ * onLoading: setIsLoading,
44
+ * onUpdating: setIsUpdating,
45
+ * onError: setError,
46
+ * onUpdated,
47
+ * });
48
+ */
49
+ export const useAppSettings = <TSettings extends Record<string, unknown> = AppSettings>(
50
+ defaultValue?: TSettings,
51
+ hooks?: AppSettingsStatusHooks & {
52
+ onError?: (error: Error | null) => void;
53
+ onUpdated?: () => void;
54
+ },
55
+ ): [TSettings, (settings: TSettings | UpdateSettingsFunction<TSettings>) => void] => {
56
+ const { onError, onUpdated, onLoading, onUpdating } = hooks ?? {};
57
+ const { currentApp = null } = useCurrentApp();
58
+
59
+ const subject = useMemo(() => {
60
+ return new BehaviorSubject<TSettings>(defaultValue ?? ({} as TSettings));
61
+ // Only create a new subject when the current app changes
62
+ // eslint-disable-next-line react-hooks/exhaustive-deps
63
+ }, [currentApp]);
64
+
65
+ // connect the subject to the current app settings stream
66
+ useLayoutEffect(() => {
67
+ const sub = (currentApp?.settings$ as Observable<TSettings>).subscribe(subject);
68
+ return () => sub?.unsubscribe();
69
+ }, [currentApp, subject]);
70
+
71
+ // subscribe to the subject to get the latest settings
72
+ const { value: settings } = useObservableState(subject, { initial: defaultValue });
73
+
74
+ const setSettings = useCallback(
75
+ (update: TSettings | UpdateSettingsFunction<TSettings>) => {
76
+ if (!currentApp) {
77
+ return onError?.(new Error('App is not available'));
78
+ }
79
+
80
+ // resolve settings with the provided value or function
81
+ const settings = typeof update === 'function' ? update(subject.value) : update;
82
+
83
+ currentApp.updateSettings(settings).subscribe({
84
+ next: () => {
85
+ onUpdated?.();
86
+ onError?.(null);
87
+ },
88
+ error: onError,
89
+ });
90
+ },
91
+ [currentApp, subject, onError, onUpdated],
92
+ );
93
+
94
+ useAppSettingsStatus(currentApp, { onLoading, onUpdating });
95
+
96
+ return [settings, setSettings];
97
+ };
98
+
99
+ export default useAppSettings;
@@ -0,0 +1,48 @@
1
+ import { useLayoutEffect } from 'react';
2
+ import { map } from 'rxjs';
3
+
4
+ import type { IApp } from '@equinor/fusion-framework-module-app';
5
+
6
+ export type AppSettingsStatusHooks = {
7
+ onLoading?: (isLoading: boolean) => void;
8
+ onUpdating?: (isUpdating: boolean) => void;
9
+ };
10
+
11
+ /**
12
+ * Custom hook to handle app settings status updates.
13
+ *
14
+ * @param {IApp | null} app - The app instance to monitor settings status.
15
+ * @param {AppSettingsStatusHooks} [hooks] - Optional hooks to handle loading and updating status.
16
+ * @param {function} [hooks.onLoading] - Callback function to handle loading status.
17
+ * @param {function} [hooks.onUpdating] - Callback function to handle updating status.
18
+ *
19
+ * @returns {void}
20
+ *
21
+ * @example
22
+ * const hooks = useMemo(() => ({
23
+ * onLoading: (isLoading) => console.log('Loading:', isLoading),
24
+ * onUpdating: (isUpdating) => console.log('Updating:', isUpdating),
25
+ * }, []);
26
+ * useAppSettingsStatus(app, hooks);
27
+ */
28
+ export const useAppSettingsStatus = (app: IApp | null, hooks?: AppSettingsStatusHooks) => {
29
+ const { onLoading, onUpdating } = hooks ?? {};
30
+
31
+ useLayoutEffect(() => {
32
+ if (app && onLoading) {
33
+ const subscription = app.status$
34
+ .pipe(map((status) => status.has('fetch_settings')))
35
+ .subscribe(onLoading);
36
+ return () => subscription.unsubscribe();
37
+ }
38
+ }, [app, onLoading]);
39
+
40
+ useLayoutEffect(() => {
41
+ if (app && onUpdating) {
42
+ const subscription = app.status$
43
+ .pipe(map((status) => status.has('update_settings')))
44
+ .subscribe(onUpdating);
45
+ return () => subscription.unsubscribe();
46
+ }
47
+ }, [app, onUpdating]);
48
+ };
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '5.3.1';
2
+ export const version = '5.4.0';