@equinor/fusion-framework-dev-portal 8.0.0 → 8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-dev-portal",
3
- "version": "8.0.0",
3
+ "version": "8.0.2",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "module": "./dist/main.js",
@@ -35,47 +35,47 @@
35
35
  "rxjs": "^7.8.1",
36
36
  "styled-components": "^6.3.11",
37
37
  "tsx": "^4.19.3",
38
- "typescript": "^6.0.3",
38
+ "typescript": "^7.0.2",
39
39
  "vite": "^8.0.0",
40
- "@equinor/fusion-framework": "8.0.10",
41
- "@equinor/fusion-framework-app": "11.0.10",
42
- "@equinor/fusion-framework-module-ag-grid": "37.0.0",
43
- "@equinor/fusion-framework-dev-server": "2.0.14",
44
- "@equinor/fusion-framework-module-analytics": "3.0.1",
45
- "@equinor/fusion-framework-module-app": "8.0.2",
46
- "@equinor/fusion-framework-module-bookmark": "4.0.2",
47
- "@equinor/fusion-framework-module-context": "8.0.0",
48
- "@equinor/fusion-framework-module-feature-flag": "2.0.1",
49
- "@equinor/fusion-framework-module-services": "8.0.1",
50
- "@equinor/fusion-framework-module-navigation": "7.0.4",
51
- "@equinor/fusion-framework-react": "8.0.0",
52
- "@equinor/fusion-framework-module-telemetry": "7.0.0",
53
- "@equinor/fusion-framework-react-components-bookmark": "2.0.1",
54
- "@equinor/fusion-framework-react-components-people-provider": "2.0.3",
55
- "@equinor/fusion-observable": "9.1.0",
56
- "@equinor/fusion-query": "7.0.1",
57
- "@equinor/fusion-framework-react-router": "2.2.0",
58
- "@equinor/fusion-framework-react-module-bookmark": "6.0.0"
40
+ "@equinor/fusion-framework": "8.0.11",
41
+ "@equinor/fusion-framework-dev-server": "2.0.16",
42
+ "@equinor/fusion-framework-app": "11.0.11",
43
+ "@equinor/fusion-framework-module-ag-grid": "37.0.1",
44
+ "@equinor/fusion-framework-module-analytics": "3.0.3",
45
+ "@equinor/fusion-framework-module-app": "8.0.3",
46
+ "@equinor/fusion-framework-module-bookmark": "4.0.3",
47
+ "@equinor/fusion-framework-module-feature-flag": "2.0.2",
48
+ "@equinor/fusion-framework-module-navigation": "7.0.6",
49
+ "@equinor/fusion-framework-module-telemetry": "7.0.1",
50
+ "@equinor/fusion-framework-module-context": "8.0.1",
51
+ "@equinor/fusion-framework-react": "8.0.1",
52
+ "@equinor/fusion-framework-module-services": "8.0.2",
53
+ "@equinor/fusion-framework-react-components-bookmark": "2.0.3",
54
+ "@equinor/fusion-framework-react-components-people-provider": "2.0.4",
55
+ "@equinor/fusion-framework-react-router": "2.4.0",
56
+ "@equinor/fusion-observable": "9.1.1",
57
+ "@equinor/fusion-framework-react-module-bookmark": "6.0.1",
58
+ "@equinor/fusion-query": "7.0.2"
59
59
  },
60
60
  "peerDependencies": {
61
- "@equinor/fusion-framework": "8.0.10",
62
- "@equinor/fusion-framework-app": "11.0.10",
63
- "@equinor/fusion-framework-module-ag-grid": "37.0.0",
64
- "@equinor/fusion-framework-dev-server": "2.0.14",
65
- "@equinor/fusion-framework-module-analytics": "3.0.1",
66
- "@equinor/fusion-framework-module-app": "8.0.2",
67
- "@equinor/fusion-framework-module-bookmark": "4.0.2",
68
- "@equinor/fusion-framework-module-feature-flag": "2.0.1",
69
- "@equinor/fusion-framework-module-context": "8.0.0",
70
- "@equinor/fusion-framework-module-navigation": "7.0.4",
71
- "@equinor/fusion-framework-module-services": "8.0.1",
72
- "@equinor/fusion-framework-module-telemetry": "7.0.0",
73
- "@equinor/fusion-framework-react-components-bookmark": "2.0.1",
74
- "@equinor/fusion-framework-react": "8.0.0",
75
- "@equinor/fusion-framework-react-components-people-provider": "2.0.3",
76
- "@equinor/fusion-framework-react-module-bookmark": "6.0.0",
77
- "@equinor/fusion-query": "7.0.1",
78
- "@equinor/fusion-observable": "9.1.0"
61
+ "@equinor/fusion-framework": "8.0.11",
62
+ "@equinor/fusion-framework-app": "11.0.11",
63
+ "@equinor/fusion-framework-dev-server": "2.0.16",
64
+ "@equinor/fusion-framework-module-ag-grid": "37.0.1",
65
+ "@equinor/fusion-framework-module-analytics": "3.0.3",
66
+ "@equinor/fusion-framework-module-app": "8.0.3",
67
+ "@equinor/fusion-framework-module-bookmark": "4.0.3",
68
+ "@equinor/fusion-framework-module-context": "8.0.1",
69
+ "@equinor/fusion-framework-module-services": "8.0.2",
70
+ "@equinor/fusion-framework-module-feature-flag": "2.0.2",
71
+ "@equinor/fusion-framework-module-telemetry": "7.0.1",
72
+ "@equinor/fusion-framework-module-navigation": "7.0.6",
73
+ "@equinor/fusion-framework-react": "8.0.1",
74
+ "@equinor/fusion-framework-react-components-bookmark": "2.0.3",
75
+ "@equinor/fusion-framework-react-components-people-provider": "2.0.4",
76
+ "@equinor/fusion-query": "7.0.2",
77
+ "@equinor/fusion-observable": "9.1.1",
78
+ "@equinor/fusion-framework-react-module-bookmark": "6.0.1"
79
79
  },
80
80
  "peerDependenciesMeta": {
81
81
  "@equinor/fusion-framework": {
package/src/AppLoader.tsx CHANGED
@@ -12,24 +12,7 @@ import { AppManifestError } from '@equinor/fusion-framework-module-app/errors.js
12
12
  import { ErrorViewer } from './ErrorViewer';
13
13
  import type { AppModule } from '@equinor/fusion-framework-module-app';
14
14
  import EquinorLoader from './EquinorLoader';
15
-
16
- /**
17
- * URL search-parameter key used to specify an app version tag.
18
- *
19
- * When present in the URL as `?$tag=<value>`, the portal loads that
20
- * specific version of the application instead of the default.
21
- */
22
- export const TAG = '$tag';
23
-
24
- /**
25
- * Reads the application version tag from the current URL search parameters.
26
- *
27
- * @returns The tag string if the `$tag` search parameter is present, otherwise `null`.
28
- */
29
- export const getAppTagFromUrl = (): string | null => {
30
- const url = new URL(window.location.href);
31
- return url.searchParams.get(TAG);
32
- };
15
+ import { getAppTagFromUrl } from './get-app-tag-from-url';
33
16
 
34
17
  /**
35
18
  * Loads, initializes, and mounts a Fusion application by its key.
@@ -51,7 +34,7 @@ export const AppLoader = (props: { readonly appKey: string }) => {
51
34
  const [loading, setLoading] = useState<boolean>(false);
52
35
  const [error, setError] = useState<Error | undefined>();
53
36
 
54
- // TODO change to `useCurrentApp`
37
+ // TODO(#5087): change to `useCurrentApp`
55
38
  /** observe and use the current selected application from framework */
56
39
  const { value: currentApp } = useObservableState(
57
40
  useMemo(() => fusion.modules.app.current$, [fusion.modules.app]),
@@ -60,6 +43,7 @@ export const AppLoader = (props: { readonly appKey: string }) => {
60
43
  useEffect(() => {
61
44
  const tag = getAppTagFromUrl();
62
45
 
46
+ // A `$tag` URL param takes precedence over the plain appKey
63
47
  if (tag) {
64
48
  fusion.modules.app.setCurrentApp({ appKey, tag });
65
49
  return;
@@ -90,6 +74,7 @@ export const AppLoader = (props: { readonly appKey: string }) => {
90
74
 
91
75
  /** create a 'private' element for the application */
92
76
  const el = document.createElement('div');
77
+ // Guard against rendering before the mounting ref has attached
93
78
  if (!ref.current) {
94
79
  throw Error('Missing application mounting point');
95
80
  }
@@ -120,7 +105,9 @@ export const AppLoader = (props: { readonly appKey: string }) => {
120
105
  return () => subscription.unsubscribe();
121
106
  }, [fusion, currentApp]);
122
107
 
108
+ // Render an error view when initialization or manifest resolution failed
123
109
  if (error) {
110
+ // Render a dedicated view for manifest-resolution failures
124
111
  if (error.cause instanceof AppManifestError) {
125
112
  return (
126
113
  <div>
@@ -30,9 +30,14 @@ export const ContextSelector = (props: ContextSearchProps): ReactElement | null
30
30
  /** callback handler for context selector, when context is changed or cleared */
31
31
  const onContextSelect = useCallback(
32
32
  (e: Event | ContextSelectEvent) => {
33
+ // Only react to selection/clear events when a provider is available to update
33
34
  if (provider) {
35
+ // 'select' carries a chosen context item; anything else means the selector was cleared
34
36
  if (e.type === 'select') {
37
+ // The 'select' event's `Event` type doesn't reflect the CustomEvent detail payload
38
+ // dispatched by the underlying context-selector element, so a cast is required here.
35
39
  const ev = e as unknown as ContextSelectEvent;
40
+ // Only set a new context when the user actually chose an item
36
41
  if (ev.nativeEvent.detail.selected.length) {
37
42
  provider.contextClient.setCurrentContext(ev.nativeEvent.detail.selected[0].id);
38
43
  }
@@ -49,11 +54,13 @@ export const ContextSelector = (props: ContextSearchProps): ReactElement | null
49
54
  */
50
55
  const clearEvent = useMemo(() => new ContextClearEvent({ date: Date.now() }), []);
51
56
  useEffect(() => {
57
+ // Notify listeners only when the context was cleared outside this selector
52
58
  if (!selectedContextItem) {
53
59
  document.dispatchEvent(clearEvent);
54
60
  }
55
61
  }, [clearEvent, selectedContextItem]);
56
62
 
63
+ // Nothing to render until a context resolver has been resolved
57
64
  if (!resolver) return null;
58
65
 
59
66
  return (
@@ -43,19 +43,22 @@ function capitalizeFirstLetter(string: string): string {
43
43
  function convertGraphic(
44
44
  graphic: ContextItem['graphic'],
45
45
  ): Pick<ContextResultItem, 'graphic' | 'graphicType'> {
46
+ // No graphic provided means nothing to render
46
47
  if (graphic === undefined) {
47
48
  return {};
48
49
  }
49
50
 
51
+ // A string graphic is either an inline SVG/HTML markup or an EDS icon name
50
52
  if (typeof graphic === 'string') {
53
+ // EDS icon names aren't part of the ContextResultItem['graphicType'] union upstream, so cast explicitly
54
+ const edsGraphicType = 'eds' as unknown as ContextResultItem['graphicType'];
51
55
  return {
52
- graphicType: graphic.startsWith('<')
53
- ? 'inline-html'
54
- : ('eds' as unknown as ContextResultItem['graphicType']),
56
+ graphicType: graphic.startsWith('<') ? 'inline-html' : edsGraphicType,
55
57
  graphic: graphic,
56
58
  };
57
59
  }
58
60
 
61
+ // A structured SVG graphic object maps directly to the inline-svg type
59
62
  if (graphic.type === 'svg') {
60
63
  return {
61
64
  graphicType: 'inline-svg',
@@ -76,19 +79,22 @@ function convertGraphic(
76
79
  * @returns An object with `meta` and `metaType` properties, or an empty object.
77
80
  */
78
81
  function convertMeta(meta: ContextItem['meta']): Pick<ContextResultItem, 'metaType' | 'meta'> {
82
+ // No meta provided means nothing to render
79
83
  if (meta === undefined) {
80
84
  return {};
81
85
  }
82
86
 
87
+ // A string meta is either an inline SVG/HTML markup or an EDS icon name
83
88
  if (typeof meta === 'string') {
89
+ // EDS icon names aren't part of the ContextResultItem['metaType'] union upstream, so cast explicitly
90
+ const edsMetaType = 'eds' as unknown as ContextResultItem['metaType'];
84
91
  return {
85
- metaType: meta.startsWith('<')
86
- ? 'inline-html'
87
- : ('eds' as unknown as ContextResultItem['metaType']),
92
+ metaType: meta.startsWith('<') ? 'inline-html' : edsMetaType,
88
93
  meta: meta,
89
94
  };
90
95
  }
91
96
 
97
+ // A structured SVG meta object maps directly to the inline-svg type
92
98
  if (meta.type === 'svg') {
93
99
  return {
94
100
  metaType: 'inline-svg',
@@ -112,7 +118,9 @@ function convertMeta(meta: ContextItem['meta']): Pick<ContextResultItem, 'metaTy
112
118
  * @returns Mapped array of `ContextResultItem` objects for the selector UI.
113
119
  */
114
120
  const mapper = (src: ContextItem<{ taskState?: string; state?: string }>[]): ContextResult => {
121
+ // Map each raw context item to its ContextResultItem shape, applying per-type overrides
115
122
  return src.map((i) => {
123
+ // Layer the base fields with graphic and meta overrides derived from the item
116
124
  const baseResult = {
117
125
  id: i.id,
118
126
  title: i.title,
@@ -125,18 +133,22 @@ const mapper = (src: ContextItem<{ taskState?: string; state?: string }>[]): Con
125
133
  const isEquinorTaskInactive = !!(
126
134
  i.value.taskState && i.value.taskState.toLowerCase() !== 'active'
127
135
  );
136
+ // Only override the meta chip for EquinorTask items that are inactive
128
137
  if (i.type.id === 'EquinorTask' && isEquinorTaskInactive) {
129
138
  baseResult.meta = `<fwc-chip disabled variant="outlined" value="${i.value.taskState}" />`;
130
139
  baseResult.metaType = 'inline-html';
131
140
  }
132
141
 
142
+ // OrgChart items always get a fixed icon plus an optional inactive-state chip
133
143
  if (i.type.id === 'OrgChart') {
134
144
  // Org charts should always have 'list' icon
135
145
  baseResult.graphic = 'list';
146
+ // 'eds' isn't part of the ContextResultItem['graphicType'] union upstream, so cast explicitly
136
147
  baseResult.graphicType = 'eds' as unknown as ContextResultItem['graphicType'];
137
148
 
138
149
  // Displays the org chart status if it is not 'active'
139
150
  const isOrgChartInactive = !!(i.value.state && i.value.state.toLowerCase() !== 'active');
151
+ // Only override the meta chip when the org chart is inactive
140
152
  if (isOrgChartInactive) {
141
153
  baseResult.meta = `<fwc-chip disabled variant="outlined" value="${capitalizeFirstLetter(i.value.state ?? '')}" />`;
142
154
  baseResult.metaType = 'inline-html';
@@ -156,6 +168,7 @@ const mapper = (src: ContextItem<{ taskState?: string; state?: string }>[]): Con
156
168
  * @returns A complete `ContextResultItem` with defaults for `id` and `title`.
157
169
  */
158
170
  const singleItem = (props: Partial<ContextResultItem>): ContextResultItem => {
171
+ // Layer the caller-supplied props on top of the placeholder defaults
159
172
  return Object.assign({ id: 'no-such-item', title: 'Change me' }, props);
160
173
  };
161
174
 
@@ -200,6 +213,7 @@ export const useContextResolver = (): {
200
213
  const onContextProviderChange = useCallback((modules: AppModulesInstance) => {
201
214
  /** try to get the context module from the app module instance */
202
215
  const contextProvider = (modules as AppModulesInstance<[ContextModule]>).context;
216
+ // Only set a provider when the loaded app actually exposes a context module
203
217
  if (contextProvider) {
204
218
  setProvider(contextProvider);
205
219
  } else {
@@ -224,10 +238,12 @@ export const useContextResolver = (): {
224
238
  );
225
239
 
226
240
  const processError = useCallback((err: Error): ContextResult => {
241
+ // Unwrap query-client errors to get at the underlying cause
227
242
  if (err.name === 'QueryClientError') {
228
243
  return processError((err as QueryClientError).cause as Error);
229
244
  }
230
245
 
246
+ // Render context-search errors with their own title/description
231
247
  if (err.name === 'FusionContextSearchError') {
232
248
  const error = err as FusionContextSearchError;
233
249
  return [
@@ -260,10 +276,11 @@ export const useContextResolver = (): {
260
276
  (): ContextResolver | null =>
261
277
  provider && {
262
278
  searchQuery: async (search: string): Promise<ContextResult> => {
279
+ // Avoid firing a search query until the minimum character threshold is met
263
280
  if (search.length < minLength) {
264
281
  return [
265
282
  singleItem({
266
- // TODO - make as enum if used for checks, or type
283
+ // TODO(#5064): make as enum if used for checks, or type
267
284
  id: 'min-length',
268
285
  title: `Type ${minLength - search.length} more chars to search`,
269
286
  isDisabled: true,
@@ -271,34 +288,37 @@ export const useContextResolver = (): {
271
288
  ];
272
289
  }
273
290
  try {
291
+ const query$ = provider.queryContext(search);
274
292
  return lastValueFrom(
275
- provider.queryContext(search).pipe(
276
- map(mapper),
277
- map((x) =>
278
- x.length
279
- ? x
280
- : [
281
- singleItem({
282
- // TODO - make as enum if used for checks, or type
283
- id: 'no-results',
284
- title: 'No results found',
285
- graphic: 'info_circle',
286
- isDisabled: true,
287
- }),
288
- ],
293
+ query$
294
+ // Run the raw query results through the mapper, falling back to a no-results placeholder
295
+ .pipe(
296
+ map(mapper),
297
+ map((x) =>
298
+ x.length
299
+ ? x
300
+ : [
301
+ singleItem({
302
+ // TODO(#5064): make as enum if used for checks, or type
303
+ id: 'no-results',
304
+ title: 'No results found',
305
+ graphic: 'info_circle',
306
+ isDisabled: true,
307
+ }),
308
+ ],
309
+ ),
310
+ /** handle failures */
311
+ catchError((err) => {
312
+ console.error(
313
+ 'PORTAL::ContextResolver',
314
+ `failed to resolve context for query ${search}`,
315
+ err,
316
+ err.cause,
317
+ );
318
+
319
+ return of(processError(err));
320
+ }),
289
321
  ),
290
- /** handle failures */
291
- catchError((err) => {
292
- console.error(
293
- 'PORTAL::ContextResolver',
294
- `failed to resolve context for query ${search}`,
295
- err,
296
- err.cause,
297
- );
298
-
299
- return of(processError(err));
300
- }),
301
- ),
302
322
  );
303
323
  /** this should NEVER happen! */
304
324
  } catch (e) {
package/src/Header.tsx CHANGED
@@ -19,9 +19,9 @@ PersonAvatarElement; // Register the custom element - prevent tree-shaking
19
19
 
20
20
  import { PersonSideSheet } from './PersonSideSheet';
21
21
 
22
- import { BookmarkSideSheet } from './BookMarkSideSheet';
22
+ import { BookmarkSideSheet } from './BookmarkSideSheet';
23
23
 
24
- import { HeaderActions } from './Header.Actions';
24
+ import { HeaderActions } from './HeaderActions';
25
25
 
26
26
  const Styled = {
27
27
  Title: styled.div`
@@ -29,6 +29,7 @@ export const PersonSideSheet = ({ azureId, isOpen, onClose }: PersonSideSheetPro
29
29
  const [currentSheet, setCurrentSheet] = useState<string>('landing');
30
30
 
31
31
  const Component = useMemo(() => {
32
+ // Pick the sub-sheet component matching the currently navigated-to sheet
32
33
  switch (currentSheet) {
33
34
  case 'features':
34
35
  return FeatureSheetContent;
@@ -2,7 +2,7 @@ import { useCurrentAppFeatures } from '@equinor/fusion-framework-react/feature-f
2
2
 
3
3
  import { Typography, Switch } from '@equinor/eds-core-react';
4
4
 
5
- import { Styled } from './Styled';
5
+ import { Styled } from './styled';
6
6
 
7
7
  /**
8
8
  * Feature toggle list for application-level feature flags.
@@ -2,7 +2,7 @@ import { useFrameworkFeatures } from '@equinor/fusion-framework-react/feature-fl
2
2
 
3
3
  import { Typography, Switch } from '@equinor/eds-core-react';
4
4
 
5
- import { Styled } from './Styled';
5
+ import { Styled } from './styled';
6
6
 
7
7
  /**
8
8
  * Feature toggle list for portal-level feature flags.
package/src/Router.tsx CHANGED
@@ -51,7 +51,6 @@ const Root = () => {
51
51
  /**
52
52
  * Route component that extracts the `appKey` parameter and delegates to {@link AppLoader}.
53
53
  */
54
- // eslint-disable-next-line react/no-multi-comp
55
54
  const AppRoute = () => {
56
55
  const { appKey } = useParams();
57
56
  return appKey ? <AppLoader appKey={appKey} /> : null;
@@ -77,7 +76,6 @@ const routes = [
77
76
  * Renders the application via `FusionRouter`. Observes context changes through
78
77
  * {@link useAppContextNavigation} to keep the URL in sync.
79
78
  */
80
- // eslint-disable-next-line react/no-multi-comp
81
79
  export const Router = () => {
82
80
  // observe the context changes and navigate when the context changes
83
81
  useAppContextNavigation();
@@ -69,6 +69,7 @@ export const configure = async (config: FrameworkConfigurator) => {
69
69
  enableNavigation(config, {
70
70
  configure: (config) => {
71
71
  config.setTelemetry(async (args) => {
72
+ // Only provide telemetry when the telemetry module was actually enabled
72
73
  if (args.hasModule('telemetry')) {
73
74
  return await args.requireInstance('telemetry');
74
75
  }
@@ -85,8 +86,10 @@ export const configure = async (config: FrameworkConfigurator) => {
85
86
 
86
87
  enableAnalytics(config, (builder) => {
87
88
  builder.setAdapter('console', async (args) => {
89
+ // Only resolve the feature-flag-gated adapter when the featureFlag module is enabled
88
90
  if (args.hasModule('featureFlag')) {
89
91
  const featureFlagProvider = await args.requireInstance('featureFlag');
92
+ // Only log analytics to the console when the feature flag is explicitly enabled
90
93
  if (featureFlagProvider.getFeature('fusionLogAnalytics')?.enabled) {
91
94
  return new ConsoleAnalyticsAdapter();
92
95
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * URL search-parameter key used to specify an app version tag.
3
+ *
4
+ * When present in the URL as `?$tag=<value>`, the portal loads that
5
+ * specific version of the application instead of the default.
6
+ */
7
+ const TAG = '$tag';
8
+
9
+ /**
10
+ * Reads the application version tag from the current URL search parameters.
11
+ *
12
+ * @returns The tag string if the `$tag` search parameter is present, otherwise `null`.
13
+ */
14
+ export const getAppTagFromUrl = (): string | null => {
15
+ const url = new URL(window.location.href);
16
+ return url.searchParams.get(TAG);
17
+ };
package/src/main.tsx CHANGED
@@ -6,10 +6,10 @@ import { ThemeProvider, theme } from '@equinor/fusion-react-styles';
6
6
  import { PeopleResolverProvider } from '@equinor/fusion-framework-react-components-people-provider';
7
7
 
8
8
  import { EquinorLoader } from './EquinorLoader';
9
- import { configure } from './config';
9
+ import { configure } from './configure';
10
10
  import { Router } from './Router';
11
11
 
12
- import fallbackSvg from './resources/fallback-photo.svg';
12
+ import fallbackSvg from './resources/svg';
13
13
 
14
14
  /** Fallback avatar image used when a person photo cannot be loaded. */
15
15
  const fallbackImage = new Blob([fallbackSvg], { type: 'image/svg+xml' });
@@ -37,6 +37,7 @@ const generatePathname = (
37
37
  context?: IContextProvider,
38
38
  pathContextId?: string,
39
39
  ) => {
40
+ // Replace an existing context id segment in the url when one is present
40
41
  if (pathContextId) {
41
42
  // context id exists in the url, replace it with the new context id
42
43
  const pathname =
@@ -110,6 +111,7 @@ export const useAppContextNavigation = () => {
110
111
  /** context was cleared */
111
112
  if (item === null) {
112
113
  console.debug('🌍 Portal:', 'current context was cleared, navigating to root');
114
+ // Prefer the app's own navigation instance when it exists
113
115
  if (appNavigation) {
114
116
  appNavigation.replace('/');
115
117
  } else {
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '8.0.0';
2
+ export const version = '8.0.2';