@equinor/fusion-framework-react-app 6.0.0 → 6.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/CHANGELOG.md +457 -433
- package/dist/esm/ag-grid/useTheme.js.map +1 -1
- package/dist/esm/bookmark/useBookmark.js.map +1 -1
- package/dist/esm/bookmark/useCurrentBookmark.js.map +1 -1
- package/dist/esm/create-component.js.map +1 -1
- package/dist/esm/create-legacy-app.js +1 -0
- package/dist/esm/create-legacy-app.js.map +1 -1
- package/dist/esm/feature-flag/enable-feature-flag.js.map +1 -1
- package/dist/esm/feature-flag/useFeature.js.map +1 -1
- package/dist/esm/framework/index.js.map +1 -1
- package/dist/esm/make-component.js.map +1 -1
- package/dist/esm/msal/useAccessToken.js.map +1 -1
- package/dist/esm/msal/useCurrentAccount.js.map +1 -1
- package/dist/esm/msal/useToken.js.map +1 -1
- package/dist/esm/navigation/useRouter.js.map +1 -1
- package/dist/esm/render-app.js.map +1 -1
- package/dist/esm/render-component.js.map +1 -1
- package/dist/esm/settings/useAppSetting.js +1 -2
- package/dist/esm/settings/useAppSetting.js.map +1 -1
- package/dist/esm/settings/useAppSettings.js +1 -2
- package/dist/esm/settings/useAppSettings.js.map +1 -1
- package/dist/esm/settings/useAppSettingsStatus.js.map +1 -1
- package/dist/esm/useAppEnvironmentVariables.js.map +1 -1
- package/dist/esm/useAppModule.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ag-grid/useTheme.d.ts +1 -1
- package/dist/types/bookmark/useCurrentBookmark.d.ts +1 -1
- package/dist/types/create-component.d.ts +2 -2
- package/dist/types/create-legacy-app.d.ts +2 -1
- package/dist/types/feature-flag/enable-feature-flag.d.ts +5 -17
- package/dist/types/feature-flag/useFeature.d.ts +1 -1
- package/dist/types/make-component.d.ts +2 -2
- package/dist/types/msal/useCurrentAccount.d.ts +1 -1
- package/dist/types/msal/useToken.d.ts +1 -1
- package/dist/types/navigation/useNavigationModule.d.ts +1 -1
- package/dist/types/navigation/useRouter.d.ts +1 -1
- package/dist/types/render-app.d.ts +2 -1
- package/dist/types/settings/useAppSettings.d.ts +1 -1
- package/dist/types/useAppEnvironmentVariables.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +18 -18
- package/src/ag-grid/useTheme.ts +7 -7
- package/src/bookmark/index.ts +5 -5
- package/src/bookmark/useBookmark.ts +2 -2
- package/src/bookmark/useCurrentBookmark.ts +17 -17
- package/src/context/useContextProvider.ts +1 -1
- package/src/create-component.tsx +40 -39
- package/src/create-legacy-app.tsx +25 -24
- package/src/feature-flag/enable-feature-flag.ts +42 -43
- package/src/feature-flag/index.ts +3 -3
- package/src/feature-flag/useFeature.ts +40 -40
- package/src/framework/index.ts +2 -2
- package/src/index.ts +7 -7
- package/src/make-component.tsx +34 -33
- package/src/msal/useAccessToken.ts +3 -3
- package/src/msal/useCurrentAccount.ts +3 -3
- package/src/msal/useToken.ts +16 -16
- package/src/navigation/useNavigationModule.ts +1 -1
- package/src/navigation/useRouter.ts +4 -4
- package/src/render-app.ts +5 -5
- package/src/render-component.tsx +16 -16
- package/src/settings/useAppSetting.ts +49 -50
- package/src/settings/useAppSettings.ts +38 -39
- package/src/settings/useAppSettingsStatus.ts +19 -19
- package/src/useAppEnvironmentVariables.ts +20 -20
- package/src/useAppModule.ts +15 -15
- package/src/useAppModules.ts +1 -1
- package/src/version.ts +1 -1
- package/src/widget/index.ts +4 -4
|
@@ -4,9 +4,9 @@ import { EMPTY, combineLatest, map } from 'rxjs';
|
|
|
4
4
|
|
|
5
5
|
import { useFrameworkModule } from '@equinor/fusion-framework-react';
|
|
6
6
|
import { useObservableState } from '@equinor/fusion-observable/react';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import type {
|
|
8
|
+
FeatureFlagModule,
|
|
9
|
+
IFeatureFlag,
|
|
10
10
|
} from '@equinor/fusion-framework-module-feature-flag';
|
|
11
11
|
import { findFeature } from '@equinor/fusion-framework-module-feature-flag/selectors';
|
|
12
12
|
|
|
@@ -19,48 +19,48 @@ import { useAppModule } from '../useAppModule';
|
|
|
19
19
|
* @returns An object containing the feature flag, toggle function, and error (if any).
|
|
20
20
|
*/
|
|
21
21
|
export const useFeature = <T = unknown>(
|
|
22
|
-
|
|
22
|
+
key: string,
|
|
23
23
|
): {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
feature?: IFeatureFlag<T>;
|
|
25
|
+
toggleFeature: (enabled?: boolean) => void;
|
|
26
|
+
error?: unknown;
|
|
27
27
|
} => {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const appProvider = useAppModule<FeatureFlagModule>('featureFlag');
|
|
29
|
+
const frameworkProvider = useFrameworkModule<FeatureFlagModule>('featureFlag');
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
31
|
+
/** get all available feature flags */
|
|
32
|
+
const features$ = useMemo(() => {
|
|
33
|
+
/** the framework does not have the feature flag module */
|
|
34
|
+
if (!frameworkProvider) {
|
|
35
|
+
return appProvider.features$;
|
|
36
|
+
}
|
|
37
|
+
/** merge feature flags from framework and application */
|
|
38
|
+
return combineLatest({
|
|
39
|
+
framework: frameworkProvider.features$,
|
|
40
|
+
app: appProvider.features$,
|
|
41
|
+
}).pipe(
|
|
42
|
+
map(({ framework, app }) => {
|
|
43
|
+
return { ...framework, ...app };
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
}, [appProvider, frameworkProvider]);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
/** find feature flag by the provided key */
|
|
49
|
+
const feature$ = useMemo(() => features$.pipe(findFeature<T>(key)), [features$, key]);
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
const { value: feature, error } = useObservableState(feature$ ?? EMPTY);
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
const toggleFeature = useCallback(
|
|
54
|
+
(enable?: boolean) => {
|
|
55
|
+
/** if no value provided, invert the current value */
|
|
56
|
+
const enabled = enable === undefined ? !appProvider.getFeature(key)?.enabled : enable;
|
|
57
|
+
appProvider.toggleFeature({
|
|
58
|
+
key,
|
|
59
|
+
enabled,
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
[appProvider, key],
|
|
63
|
+
);
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
return { feature, toggleFeature, error };
|
|
66
66
|
};
|
package/src/framework/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
AppConfig,
|
|
3
|
+
AppEnv,
|
|
4
|
+
AppModuleInitiator,
|
|
5
|
+
AppModules,
|
|
6
|
+
AppModulesInstance,
|
|
7
|
+
AppRenderFn,
|
|
8
|
+
IAppConfigurator,
|
|
9
9
|
} from '@equinor/fusion-framework-app';
|
|
10
10
|
|
|
11
11
|
export { AppManifest } from '@equinor/fusion-framework-module-app';
|
package/src/make-component.tsx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { lazy } from 'react';
|
|
2
3
|
|
|
3
4
|
import { FrameworkProvider } from '@equinor/fusion-framework-react';
|
|
4
5
|
import type { Fusion } from '@equinor/fusion-framework-react';
|
|
5
6
|
|
|
6
|
-
import { AppEnv, configureModules } from '@equinor/fusion-framework-app';
|
|
7
|
+
import { type AppEnv, configureModules } from '@equinor/fusion-framework-app';
|
|
7
8
|
import type { AppModuleInitiator, AppModulesInstance } from '@equinor/fusion-framework-app';
|
|
8
9
|
|
|
9
10
|
import type { AnyModule } from '@equinor/fusion-framework-module';
|
|
@@ -13,13 +14,13 @@ import type { FrameworkEvent, FrameworkEventInit } from '@equinor/fusion-framewo
|
|
|
13
14
|
import { ModuleProvider as AppModuleProvider } from '@equinor/fusion-framework-react-module';
|
|
14
15
|
|
|
15
16
|
export type ComponentRenderArgs<TFusion extends Fusion = Fusion, TEnv = AppEnv> = {
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
fusion: TFusion;
|
|
18
|
+
env: TEnv;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
export type ComponentRenderer<TFusion extends Fusion = Fusion, TEnv = AppEnv> = (
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
fusion: TFusion,
|
|
23
|
+
env: TEnv,
|
|
23
24
|
) => React.LazyExoticComponent<React.ComponentType>;
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -44,40 +45,40 @@ export type ComponentRenderer<TFusion extends Fusion = Fusion, TEnv = AppEnv> =
|
|
|
44
45
|
* initializes the specified modules and provides the necessary Fusion and module context.
|
|
45
46
|
*/
|
|
46
47
|
export const makeComponent = <
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
TModules extends Array<AnyModule>,
|
|
49
|
+
TRef extends Fusion = Fusion,
|
|
50
|
+
TEnv extends AppEnv = AppEnv,
|
|
50
51
|
>(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
Component: React.ReactNode,
|
|
53
|
+
args: { fusion: TRef; env: TEnv },
|
|
54
|
+
configure?: AppModuleInitiator<TModules, TRef, TEnv>,
|
|
54
55
|
): React.LazyExoticComponent<React.ComponentType> =>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
lazy(async () => {
|
|
57
|
+
const init = configureModules<TModules, TRef, TEnv>(configure);
|
|
58
|
+
const modules = (await init(args)) as unknown as AppModulesInstance;
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
const { fusion } = args;
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
default: () => (
|
|
68
|
-
<FrameworkProvider value={fusion}>
|
|
69
|
-
<AppModuleProvider value={modules}>{Component}</AppModuleProvider>
|
|
70
|
-
</FrameworkProvider>
|
|
71
|
-
),
|
|
72
|
-
};
|
|
62
|
+
modules.event.dispatchEvent('onReactAppLoaded', {
|
|
63
|
+
detail: { modules, fusion },
|
|
64
|
+
source: Component,
|
|
73
65
|
});
|
|
74
66
|
|
|
67
|
+
return {
|
|
68
|
+
default: () => (
|
|
69
|
+
<FrameworkProvider value={fusion}>
|
|
70
|
+
<AppModuleProvider value={modules}>{Component}</AppModuleProvider>
|
|
71
|
+
</FrameworkProvider>
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
|
|
75
76
|
declare module '@equinor/fusion-framework-module-event' {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
interface FrameworkEventMap {
|
|
78
|
+
onReactAppLoaded: FrameworkEvent<
|
|
79
|
+
FrameworkEventInit<{ modules: AppModulesInstance; fusion: Fusion }, React.ComponentType>
|
|
80
|
+
>;
|
|
81
|
+
}
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
export default makeComponent;
|
|
@@ -7,8 +7,8 @@ import { useToken } from './useToken';
|
|
|
7
7
|
* @returns An object containing the access token, pending state, and error.
|
|
8
8
|
*/
|
|
9
9
|
export const useAccessToken = (req: {
|
|
10
|
-
|
|
10
|
+
scopes: string[];
|
|
11
11
|
}): { token?: string; pending: boolean; error: unknown } => {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const { token, error, pending } = useToken(req);
|
|
13
|
+
return { token: token?.accessToken, pending, error };
|
|
14
14
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AccountInfo } from '@equinor/fusion-framework-module-msal';
|
|
1
|
+
import type { AccountInfo } from '@equinor/fusion-framework-module-msal';
|
|
2
2
|
import useAppModule from '../useAppModule';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -6,6 +6,6 @@ import useAppModule from '../useAppModule';
|
|
|
6
6
|
* @returns The current account information or undefined if no account is available.
|
|
7
7
|
*/
|
|
8
8
|
export const useCurrentAccount = (): AccountInfo | undefined => {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const msalProvider = useAppModule('auth');
|
|
10
|
+
return msalProvider.defaultAccount;
|
|
11
11
|
};
|
package/src/msal/useToken.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { AuthenticationResult } from '@equinor/fusion-framework-module-msal';
|
|
4
4
|
|
|
5
5
|
import useAppModule from '../useAppModule';
|
|
6
6
|
|
|
@@ -10,22 +10,22 @@ import useAppModule from '../useAppModule';
|
|
|
10
10
|
* @returns An object containing the acquired token, pending state, and error.
|
|
11
11
|
*/
|
|
12
12
|
export const useToken = (req: {
|
|
13
|
-
|
|
13
|
+
scopes: string[];
|
|
14
14
|
}): { token?: AuthenticationResult; pending: boolean; error: unknown } => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
const msalProvider = useAppModule('auth');
|
|
16
|
+
const [token, setToken] = useState<AuthenticationResult | undefined>(undefined);
|
|
17
|
+
const [pending, setPending] = useState<boolean>(false);
|
|
18
|
+
const [error, setError] = useState<unknown>(null);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setPending(true);
|
|
21
|
+
setToken(undefined);
|
|
22
|
+
msalProvider
|
|
23
|
+
.acquireToken(req)
|
|
24
|
+
.then((token) => token && setToken(token))
|
|
25
|
+
.catch(setError)
|
|
26
|
+
.finally(() => setPending(false));
|
|
27
|
+
}, [msalProvider, req]);
|
|
28
|
+
return { token, pending, error };
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
export default useToken;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import useAppModule from '../useAppModule';
|
|
2
|
-
import { INavigationProvider } from '@equinor/fusion-framework-module-navigation';
|
|
2
|
+
import type { INavigationProvider } from '@equinor/fusion-framework-module-navigation';
|
|
3
3
|
|
|
4
4
|
/** hook for getting the navigation provider (if enabled!) */
|
|
5
5
|
export const useNavigationModule = (): INavigationProvider => useAppModule('navigation');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { useNavigationModule } from './useNavigationModule';
|
|
3
|
-
import {
|
|
3
|
+
import type { INavigationProvider } from '@equinor/fusion-framework-module-navigation';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* create a router for react routing
|
|
@@ -9,8 +9,8 @@ import { type INavigationProvider } from '@equinor/fusion-framework-module-navig
|
|
|
9
9
|
* @param routes router objects __(must be static | memorized)__
|
|
10
10
|
*/
|
|
11
11
|
export const useRouter = (
|
|
12
|
-
|
|
12
|
+
routes: Parameters<INavigationProvider['createRouter']>[0],
|
|
13
13
|
): ReturnType<INavigationProvider['createRouter']> => {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const provider = useNavigationModule();
|
|
15
|
+
return useMemo(() => provider.createRouter(routes), [provider, routes]);
|
|
16
16
|
};
|
package/src/render-app.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { createComponent } from './create-component';
|
|
2
|
-
import { renderComponent, RenderTeardown } from './render-component';
|
|
2
|
+
import { renderComponent, type RenderTeardown } from './render-component';
|
|
3
3
|
|
|
4
4
|
import type { ComponentRenderArgs } from './create-component';
|
|
5
5
|
|
|
6
6
|
/** @deprecated */
|
|
7
7
|
export const renderApp = (...componentArgs: Parameters<typeof createComponent>) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const renderer = renderComponent(createComponent(...componentArgs));
|
|
9
|
+
return (el: HTMLElement, args: ComponentRenderArgs): RenderTeardown => {
|
|
10
|
+
return renderer(el, args);
|
|
11
|
+
};
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export default renderApp;
|
package/src/render-component.tsx
CHANGED
|
@@ -7,26 +7,26 @@ export type RenderTeardown = VoidFunction;
|
|
|
7
7
|
|
|
8
8
|
/** @deprecated */
|
|
9
9
|
export const renderComponent = (renderer: ComponentRenderer) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
return (el: HTMLElement, args: ComponentRenderArgs): RenderTeardown => {
|
|
11
|
+
const Component = renderer(args.fusion, args.env);
|
|
12
|
+
return render(el, Component);
|
|
13
|
+
};
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
const render = (el: Element, Component: FunctionComponent): RenderTeardown => {
|
|
17
|
+
// eslint-disable-next-line react/no-deprecated
|
|
18
|
+
ReactDOM.render(
|
|
19
|
+
<StrictMode>
|
|
20
|
+
<Suspense fallback={<p>loading app</p>}>
|
|
21
|
+
<Component />
|
|
22
|
+
</Suspense>
|
|
23
|
+
</StrictMode>,
|
|
24
|
+
el,
|
|
25
|
+
);
|
|
26
|
+
return () => {
|
|
17
27
|
// eslint-disable-next-line react/no-deprecated
|
|
18
|
-
ReactDOM.
|
|
19
|
-
|
|
20
|
-
<Suspense fallback={<p>loading app</p>}>
|
|
21
|
-
<Component />
|
|
22
|
-
</Suspense>
|
|
23
|
-
</StrictMode>,
|
|
24
|
-
el,
|
|
25
|
-
);
|
|
26
|
-
return () => {
|
|
27
|
-
// eslint-disable-next-line react/no-deprecated
|
|
28
|
-
ReactDOM.unmountComponentAtNode(el);
|
|
29
|
-
};
|
|
28
|
+
ReactDOM.unmountComponentAtNode(el);
|
|
29
|
+
};
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
// const render = (el: Element, Component: FunctionComponent): RenderTeardown => {
|
|
@@ -45,68 +45,67 @@ type UpdateSettingFunction<T, O = T> = (currentSetting: T | undefined) => O;
|
|
|
45
45
|
* });
|
|
46
46
|
*/
|
|
47
47
|
export const useAppSetting = <
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
TSettings extends Record<string, unknown> = AppSettings,
|
|
49
|
+
TProp extends keyof TSettings = keyof TSettings,
|
|
50
50
|
>(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
prop: TProp,
|
|
52
|
+
defaultValue?: TSettings[TProp],
|
|
53
|
+
hooks?: AppSettingsStatusHooks & {
|
|
54
|
+
onError?: (error: Error | null) => void;
|
|
55
|
+
onUpdated?: () => void;
|
|
56
|
+
},
|
|
57
57
|
): [
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
TSettings[TProp] | undefined,
|
|
59
|
+
(update: TSettings[TProp] | UpdateSettingFunction<TSettings[TProp]>) => void,
|
|
60
60
|
] => {
|
|
61
|
-
|
|
61
|
+
const [{ onError, onUpdated, onLoading, onUpdating }] = useState(() => hooks ?? {});
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
const { currentApp = null } = useCurrentApp();
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}, [currentApp]);
|
|
65
|
+
// create a subject to manage the setting value
|
|
66
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: new subject when app changes
|
|
67
|
+
const subject = useMemo(() => {
|
|
68
|
+
return new BehaviorSubject<TSettings[TProp] | undefined>(defaultValue);
|
|
69
|
+
}, [currentApp]);
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
useLayoutEffect(() => {
|
|
72
|
+
const sub = currentApp?.settings$
|
|
73
|
+
.pipe(map((settings) => (settings as TSettings)[prop]))
|
|
74
|
+
.subscribe(subject);
|
|
75
|
+
return () => sub?.unsubscribe();
|
|
76
|
+
}, [currentApp, subject, prop]);
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
// subscribe to the setting value
|
|
79
|
+
const { value: setting } = useObservableState(subject);
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
// update function
|
|
82
|
+
const setSetting = useCallback(
|
|
83
|
+
(update: TSettings[TProp] | UpdateSettingFunction<TSettings[TProp]>) => {
|
|
84
|
+
if (!currentApp) {
|
|
85
|
+
return onError?.(new Error('App is not available'));
|
|
86
|
+
}
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
// resolve setting value with the provided value or function
|
|
89
|
+
const value =
|
|
90
|
+
typeof update === 'function'
|
|
91
|
+
? (update as UpdateSettingFunction<TSettings[TProp]>)(subject.value)
|
|
92
|
+
: update;
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
currentApp.updateSetting<TSettings, TProp>(prop, value).subscribe({
|
|
95
|
+
error: onError,
|
|
96
|
+
complete: onUpdated,
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
[currentApp, subject, prop, onError, onUpdated],
|
|
100
|
+
);
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
// status hooks
|
|
103
|
+
useAppSettingsStatus(currentApp, {
|
|
104
|
+
onLoading,
|
|
105
|
+
onUpdating,
|
|
106
|
+
});
|
|
108
107
|
|
|
109
|
-
|
|
108
|
+
return [setting, setSetting];
|
|
110
109
|
};
|
|
111
110
|
|
|
112
111
|
export default useAppSetting;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useLayoutEffect, useMemo } from 'react';
|
|
2
|
-
import { BehaviorSubject, Observable } from 'rxjs';
|
|
2
|
+
import { BehaviorSubject, type Observable } from 'rxjs';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import type { AppSettings } from '@equinor/fusion-framework-module-app';
|
|
5
5
|
|
|
6
6
|
import { useCurrentApp } from '@equinor/fusion-framework-react/app';
|
|
7
7
|
import { useObservableState } from '@equinor/fusion-observable/react';
|
|
@@ -47,53 +47,52 @@ type UpdateSettingsFunction<T, O = T> = (currentSettings: T | undefined) => O;
|
|
|
47
47
|
* });
|
|
48
48
|
*/
|
|
49
49
|
export const useAppSettings = <TSettings extends Record<string, unknown> = AppSettings>(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
defaultValue?: TSettings,
|
|
51
|
+
hooks?: AppSettingsStatusHooks & {
|
|
52
|
+
onError?: (error: Error | null) => void;
|
|
53
|
+
onUpdated?: () => void;
|
|
54
|
+
},
|
|
55
55
|
): [TSettings, (settings: TSettings | UpdateSettingsFunction<TSettings>) => void] => {
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
const { onError, onUpdated, onLoading, onUpdating } = hooks ?? {};
|
|
57
|
+
const { currentApp = null } = useCurrentApp();
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}, [currentApp]);
|
|
59
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: create new subject when app changes
|
|
60
|
+
const subject = useMemo(() => {
|
|
61
|
+
return new BehaviorSubject<TSettings>(defaultValue ?? ({} as TSettings));
|
|
62
|
+
}, [currentApp]);
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
// connect the subject to the current app settings stream
|
|
65
|
+
useLayoutEffect(() => {
|
|
66
|
+
const sub = (currentApp?.settings$ as Observable<TSettings>).subscribe(subject);
|
|
67
|
+
return () => sub?.unsubscribe();
|
|
68
|
+
}, [currentApp, subject]);
|
|
70
69
|
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
// subscribe to the subject to get the latest settings
|
|
71
|
+
const { value: settings } = useObservableState(subject, { initial: defaultValue });
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
const setSettings = useCallback(
|
|
74
|
+
(update: TSettings | UpdateSettingsFunction<TSettings>) => {
|
|
75
|
+
if (!currentApp) {
|
|
76
|
+
return onError?.(new Error('App is not available'));
|
|
77
|
+
}
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
// resolve settings with the provided value or function
|
|
80
|
+
const settings = typeof update === 'function' ? update(subject.value) : update;
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
},
|
|
88
|
-
error: onError,
|
|
89
|
-
});
|
|
82
|
+
currentApp.updateSettings(settings).subscribe({
|
|
83
|
+
next: () => {
|
|
84
|
+
onUpdated?.();
|
|
85
|
+
onError?.(null);
|
|
90
86
|
},
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
error: onError,
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
[currentApp, subject, onError, onUpdated],
|
|
91
|
+
);
|
|
93
92
|
|
|
94
|
-
|
|
93
|
+
useAppSettingsStatus(currentApp, { onLoading, onUpdating });
|
|
95
94
|
|
|
96
|
-
|
|
95
|
+
return [settings, setSettings];
|
|
97
96
|
};
|
|
98
97
|
|
|
99
98
|
export default useAppSettings;
|