@trackunit/react-core-hooks 1.17.58 → 1.17.59-alpha-df0e8d549b3.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.
- package/index.cjs.js +63 -185
- package/index.esm.js +65 -184
- package/package.json +4 -4
- package/src/index.d.ts +0 -4
- package/src/access/ExtensionRouterContext.d.ts +0 -6
- package/src/access/useValidateAccessAsync.d.ts +0 -25
- package/src/featureFlags/useHostFeatureFlag.d.ts +0 -34
package/index.cjs.js
CHANGED
|
@@ -1,162 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var react = require('react');
|
|
4
3
|
var reactCoreContextsApi = require('@trackunit/react-core-contexts-api');
|
|
4
|
+
var react = require('react');
|
|
5
5
|
var esToolkit = require('es-toolkit');
|
|
6
6
|
var irisAppRuntimeCore = require('@trackunit/iris-app-runtime-core');
|
|
7
7
|
var zod = require('zod');
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* This is a hook to use the FeatureFlagContext.
|
|
11
|
-
*
|
|
12
|
-
* @requires FeatureFlagContext
|
|
13
|
-
* @example
|
|
14
|
-
* import { useFeatureFlags } from "@trackunit/react-core-hooks";
|
|
15
|
-
* const { flags } = useFeatureFlags();
|
|
16
|
-
* // use flags to control visibility/access ...
|
|
17
|
-
* @see (@link FeatureFlagContext)
|
|
18
|
-
*/
|
|
19
|
-
const useFeatureFlags = () => {
|
|
20
|
-
const context = react.useContext(reactCoreContextsApi.FeatureFlagContext);
|
|
21
|
-
if (!context) {
|
|
22
|
-
throw new Error("useFeatureFlags must be used within a FeatureFlagContext");
|
|
23
|
-
}
|
|
24
|
-
return context;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* This is a hook to use the UserSubscriptionContext.
|
|
29
|
-
*
|
|
30
|
-
* @requires UserSubscriptionContext
|
|
31
|
-
* @example
|
|
32
|
-
* import { useUserSubscription } from "@trackunit/react-core-hooks";
|
|
33
|
-
* const { numberOfDaysWithAccessToHistoricalData, packageType } = useUserSubscription();
|
|
34
|
-
* // use it for something
|
|
35
|
-
* const data = fetchData(numberOfDaysWithAccessToHistoricalData)
|
|
36
|
-
* @see { UserSubscription}
|
|
37
|
-
*/
|
|
38
|
-
const useUserSubscription = () => {
|
|
39
|
-
const context = react.useContext(reactCoreContextsApi.UserSubscriptionContext);
|
|
40
|
-
if (!context) {
|
|
41
|
-
throw new Error("useUserSubscription must be used within the UserSubscriptionContext");
|
|
42
|
-
}
|
|
43
|
-
return context;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Hook providing the authenticated user's profile, account, and permission data
|
|
48
|
-
* from the CurrentUserContext.
|
|
49
|
-
*
|
|
50
|
-
* ### When to use
|
|
51
|
-
* Use `useCurrentUser` when you need the user's identity or account context — displaying
|
|
52
|
-
* the user's name, resolving the effective account ID, or branching logic based on
|
|
53
|
-
* `isAssuming` or `isTrackunitUser`.
|
|
54
|
-
*
|
|
55
|
-
* ### When not to use
|
|
56
|
-
* - If you only need a boolean check for a single permission — use `useUserPermission`.
|
|
57
|
-
* - To gate entire routes — prefer server-side access control or `hasAccessTo` from
|
|
58
|
-
* `useNavigateInHost`.
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* import { useCurrentUser } from "@trackunit/react-core-hooks";
|
|
62
|
-
* const { assumedUser, accountId } = useCurrentUser();
|
|
63
|
-
*
|
|
64
|
-
* const { data, loading } = useGetAccountByIdQuery({
|
|
65
|
-
* variables: { accountId: assumedUser?.accountId || accountId || "" },
|
|
66
|
-
* });
|
|
67
|
-
* @see { CurrentUserContext}
|
|
68
|
-
*/
|
|
69
|
-
const useCurrentUser = () => {
|
|
70
|
-
const context = react.useContext(reactCoreContextsApi.CurrentUserContext);
|
|
71
|
-
if (!context) {
|
|
72
|
-
throw new Error("useCurrentUser must be used within the CurrentUserProvider");
|
|
73
|
-
}
|
|
74
|
-
return context;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Async wrapper around {@link useValidateAccess}. Each helper awaits until the
|
|
79
|
-
* underlying user, subscription, and feature-flag data has finished loading
|
|
80
|
-
* before resolving, so callers such as router guards can check access without
|
|
81
|
-
* having to observe the synchronous `loading` flag themselves.
|
|
82
|
-
*/
|
|
83
|
-
const useValidateAccessAsync = () => {
|
|
84
|
-
const { hasAccessTo, hasFeatureFlag, hasPermission, loading } = useValidateAccess();
|
|
85
|
-
const hasAccessToRef = react.useRef(hasAccessTo);
|
|
86
|
-
hasAccessToRef.current = hasAccessTo;
|
|
87
|
-
const hasFeatureFlagRef = react.useRef(hasFeatureFlag);
|
|
88
|
-
hasFeatureFlagRef.current = hasFeatureFlag;
|
|
89
|
-
const hasPermissionRef = react.useRef(hasPermission);
|
|
90
|
-
hasPermissionRef.current = hasPermission;
|
|
91
|
-
const loadingRef = react.useRef(loading);
|
|
92
|
-
loadingRef.current = loading;
|
|
93
|
-
return react.useMemo(() => {
|
|
94
|
-
const waitForLoading = async () => {
|
|
95
|
-
if (loadingRef.current) {
|
|
96
|
-
await new Promise(resolve => {
|
|
97
|
-
const intervalId = setInterval(() => {
|
|
98
|
-
if (!loadingRef.current) {
|
|
99
|
-
clearInterval(intervalId);
|
|
100
|
-
resolve();
|
|
101
|
-
}
|
|
102
|
-
}, 10);
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
return {
|
|
107
|
-
hasAccessTo: async (requiredAccessTo) => {
|
|
108
|
-
await waitForLoading();
|
|
109
|
-
return hasAccessToRef.current(requiredAccessTo);
|
|
110
|
-
},
|
|
111
|
-
hasFeatureFlag: async (flag) => {
|
|
112
|
-
await waitForLoading();
|
|
113
|
-
return hasFeatureFlagRef.current(flag);
|
|
114
|
-
},
|
|
115
|
-
hasPermission: async (permission) => {
|
|
116
|
-
await waitForLoading();
|
|
117
|
-
return hasPermissionRef.current(permission);
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
}, [loadingRef, hasAccessToRef, hasFeatureFlagRef, hasPermissionRef]);
|
|
121
|
-
};
|
|
122
|
-
/**
|
|
123
|
-
* Synchronously evaluates the current user's access via subscription features,
|
|
124
|
-
* feature flags, and permissions. Returns predicate helpers plus a `loading`
|
|
125
|
-
* flag that stays true until user, subscription, and flag data are all present.
|
|
126
|
-
*/
|
|
127
|
-
const useValidateAccess = () => {
|
|
128
|
-
const { isAuthenticated, permissions: userPermissions } = useCurrentUser();
|
|
129
|
-
const { features, loading: featuresLoading } = useUserSubscription();
|
|
130
|
-
const { flags: allFlags } = useFeatureFlags();
|
|
131
|
-
const loading = react.useMemo(() => allFlags === null || featuresLoading || !userPermissions, [allFlags, featuresLoading, userPermissions]);
|
|
132
|
-
const hasFeatureFlag = react.useCallback((flag) => {
|
|
133
|
-
return allFlags?.find(({ key }) => key === flag)?.state ?? null;
|
|
134
|
-
}, [allFlags]);
|
|
135
|
-
const hasAccessTo = react.useCallback((requiredAccessTo) => {
|
|
136
|
-
if (isAuthenticated) {
|
|
137
|
-
return hasAccessToFeature(features, requiredAccessTo);
|
|
138
|
-
}
|
|
139
|
-
return false;
|
|
140
|
-
}, [features, isAuthenticated]);
|
|
141
|
-
const hasPermission = react.useCallback((permission) => {
|
|
142
|
-
if (isAuthenticated) {
|
|
143
|
-
return userPermissions?.includes(permission) ?? false;
|
|
144
|
-
}
|
|
145
|
-
return false;
|
|
146
|
-
}, [isAuthenticated, userPermissions]);
|
|
147
|
-
return react.useMemo(() => {
|
|
148
|
-
return {
|
|
149
|
-
hasAccessTo,
|
|
150
|
-
hasFeatureFlag,
|
|
151
|
-
hasPermission,
|
|
152
|
-
loading,
|
|
153
|
-
};
|
|
154
|
-
}, [hasAccessTo, hasFeatureFlag, hasPermission, loading]);
|
|
155
|
-
};
|
|
156
|
-
const hasAccessToFeature = (myFeatures, feature) => {
|
|
157
|
-
return myFeatures?.some(x => x.id === feature) ?? null;
|
|
158
|
-
};
|
|
159
|
-
|
|
160
9
|
/**
|
|
161
10
|
* Hook to get the analytics context.
|
|
162
11
|
*
|
|
@@ -370,39 +219,21 @@ const useExportDataContext = () => {
|
|
|
370
219
|
};
|
|
371
220
|
|
|
372
221
|
/**
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* Return values:
|
|
376
|
-
* - `null` while the host feature flags have not loaded yet.
|
|
377
|
-
* - the flag's boolean `state` when the flag exists in the loaded set.
|
|
378
|
-
* - `null` when the flags are loaded but the requested flag is not present,
|
|
379
|
-
* unless `options.defaultValueWhenMissing` is provided, in which case that
|
|
380
|
-
* value is returned.
|
|
381
|
-
*
|
|
382
|
-
* The hook is generic so callers can narrow the accepted key. Internal callers
|
|
383
|
-
* pass the strongly-typed `Flag` union for type safety, e.g.
|
|
384
|
-
* `useHostFeatureFlag<Flag>(Flags.SOME_FLAG, { defaultValueWhenMissing: true })`.
|
|
385
|
-
* External callers may pass any string.
|
|
386
|
-
*
|
|
387
|
-
* Must be used within a `FeatureFlagContext` (provided by `TrackunitProviders`).
|
|
222
|
+
* This is a hook to use the FeatureFlagContext.
|
|
388
223
|
*
|
|
224
|
+
* @requires FeatureFlagContext
|
|
389
225
|
* @example
|
|
390
|
-
* import {
|
|
391
|
-
* const
|
|
392
|
-
*
|
|
226
|
+
* import { useFeatureFlags } from "@trackunit/react-core-hooks";
|
|
227
|
+
* const { flags } = useFeatureFlags();
|
|
228
|
+
* // use flags to control visibility/access ...
|
|
229
|
+
* @see (@link FeatureFlagContext)
|
|
393
230
|
*/
|
|
394
|
-
const
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
if (flags && options.defaultValueWhenMissing !== undefined) {
|
|
402
|
-
return options.defaultValueWhenMissing;
|
|
403
|
-
}
|
|
404
|
-
return null;
|
|
405
|
-
}, [flags, flag, options.defaultValueWhenMissing]);
|
|
231
|
+
const useFeatureFlags = () => {
|
|
232
|
+
const context = react.useContext(reactCoreContextsApi.FeatureFlagContext);
|
|
233
|
+
if (!context) {
|
|
234
|
+
throw new Error("useFeatureFlags must be used within a FeatureFlagContext");
|
|
235
|
+
}
|
|
236
|
+
return context;
|
|
406
237
|
};
|
|
407
238
|
|
|
408
239
|
/**
|
|
@@ -1065,6 +896,25 @@ const useSiteRuntime = () => {
|
|
|
1065
896
|
return react.useMemo(() => ({ siteInfo, loading, error }), [siteInfo, loading, error]);
|
|
1066
897
|
};
|
|
1067
898
|
|
|
899
|
+
/**
|
|
900
|
+
* This is a hook to use the UserSubscriptionContext.
|
|
901
|
+
*
|
|
902
|
+
* @requires UserSubscriptionContext
|
|
903
|
+
* @example
|
|
904
|
+
* import { useUserSubscription } from "@trackunit/react-core-hooks";
|
|
905
|
+
* const { numberOfDaysWithAccessToHistoricalData, packageType } = useUserSubscription();
|
|
906
|
+
* // use it for something
|
|
907
|
+
* const data = fetchData(numberOfDaysWithAccessToHistoricalData)
|
|
908
|
+
* @see { UserSubscription}
|
|
909
|
+
*/
|
|
910
|
+
const useUserSubscription = () => {
|
|
911
|
+
const context = react.useContext(reactCoreContextsApi.UserSubscriptionContext);
|
|
912
|
+
if (!context) {
|
|
913
|
+
throw new Error("useUserSubscription must be used within the UserSubscriptionContext");
|
|
914
|
+
}
|
|
915
|
+
return context;
|
|
916
|
+
};
|
|
917
|
+
|
|
1068
918
|
/**
|
|
1069
919
|
* This is a hook to use the TimeRangeProvider.
|
|
1070
920
|
*
|
|
@@ -1110,6 +960,37 @@ const useToast = () => {
|
|
|
1110
960
|
return toastContext;
|
|
1111
961
|
};
|
|
1112
962
|
|
|
963
|
+
/**
|
|
964
|
+
* Hook providing the authenticated user's profile, account, and permission data
|
|
965
|
+
* from the CurrentUserContext.
|
|
966
|
+
*
|
|
967
|
+
* ### When to use
|
|
968
|
+
* Use `useCurrentUser` when you need the user's identity or account context — displaying
|
|
969
|
+
* the user's name, resolving the effective account ID, or branching logic based on
|
|
970
|
+
* `isAssuming` or `isTrackunitUser`.
|
|
971
|
+
*
|
|
972
|
+
* ### When not to use
|
|
973
|
+
* - If you only need a boolean check for a single permission — use `useUserPermission`.
|
|
974
|
+
* - To gate entire routes — prefer server-side access control or `hasAccessTo` from
|
|
975
|
+
* `useNavigateInHost`.
|
|
976
|
+
*
|
|
977
|
+
* @example
|
|
978
|
+
* import { useCurrentUser } from "@trackunit/react-core-hooks";
|
|
979
|
+
* const { assumedUser, accountId } = useCurrentUser();
|
|
980
|
+
*
|
|
981
|
+
* const { data, loading } = useGetAccountByIdQuery({
|
|
982
|
+
* variables: { accountId: assumedUser?.accountId || accountId || "" },
|
|
983
|
+
* });
|
|
984
|
+
* @see { CurrentUserContext}
|
|
985
|
+
*/
|
|
986
|
+
const useCurrentUser = () => {
|
|
987
|
+
const context = react.useContext(reactCoreContextsApi.CurrentUserContext);
|
|
988
|
+
if (!context) {
|
|
989
|
+
throw new Error("useCurrentUser must be used within the CurrentUserProvider");
|
|
990
|
+
}
|
|
991
|
+
return context;
|
|
992
|
+
};
|
|
993
|
+
|
|
1113
994
|
/**
|
|
1114
995
|
* This is a hook providing the Current User language.
|
|
1115
996
|
*
|
|
@@ -1548,7 +1429,6 @@ exports.useFeatureFlags = useFeatureFlags;
|
|
|
1548
1429
|
exports.useFilterBarContext = useFilterBarContext;
|
|
1549
1430
|
exports.useGeolocation = useGeolocation;
|
|
1550
1431
|
exports.useHasAccessTo = useHasAccessTo;
|
|
1551
|
-
exports.useHostFeatureFlag = useHostFeatureFlag;
|
|
1552
1432
|
exports.useImageUploader = useImageUploader;
|
|
1553
1433
|
exports.useIrisAppId = useIrisAppId;
|
|
1554
1434
|
exports.useIrisAppImage = useIrisAppImage;
|
|
@@ -1563,7 +1443,5 @@ exports.useToast = useToast;
|
|
|
1563
1443
|
exports.useToken = useToken;
|
|
1564
1444
|
exports.useUserPermission = useUserPermission;
|
|
1565
1445
|
exports.useUserSubscription = useUserSubscription;
|
|
1566
|
-
exports.useValidateAccess = useValidateAccess;
|
|
1567
|
-
exports.useValidateAccessAsync = useValidateAccessAsync;
|
|
1568
1446
|
exports.useWidgetConfig = useWidgetConfig;
|
|
1569
1447
|
exports.useWidgetConfigAsync = useWidgetConfigAsync;
|
package/index.esm.js
CHANGED
|
@@ -1,160 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { AnalyticsContext, AssetSortingContext, ConfirmationDialogContext, EnvironmentContext, ErrorHandlingContext, ExportDataContext, FeatureFlagContext, FilterBarContext, GeolocationContext, TokenContext, ModalDialogContext, NavigationContext, OemBrandingContext, UserSubscriptionContext, TimeRangeContext, ToastContext, CurrentUserContext, CurrentUserPreferenceContext, WidgetConfigContext } from '@trackunit/react-core-contexts-api';
|
|
2
|
+
import { useContext, useMemo, useState, useCallback, useEffect, useReducer, useRef } from 'react';
|
|
3
3
|
import { isEqual } from 'es-toolkit';
|
|
4
4
|
import { AssetRuntime, CustomerRuntime, EventRuntime, ParamsRuntime, SiteRuntime, WidgetConfigRuntime } from '@trackunit/iris-app-runtime-core';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* This is a hook to use the FeatureFlagContext.
|
|
9
|
-
*
|
|
10
|
-
* @requires FeatureFlagContext
|
|
11
|
-
* @example
|
|
12
|
-
* import { useFeatureFlags } from "@trackunit/react-core-hooks";
|
|
13
|
-
* const { flags } = useFeatureFlags();
|
|
14
|
-
* // use flags to control visibility/access ...
|
|
15
|
-
* @see (@link FeatureFlagContext)
|
|
16
|
-
*/
|
|
17
|
-
const useFeatureFlags = () => {
|
|
18
|
-
const context = useContext(FeatureFlagContext);
|
|
19
|
-
if (!context) {
|
|
20
|
-
throw new Error("useFeatureFlags must be used within a FeatureFlagContext");
|
|
21
|
-
}
|
|
22
|
-
return context;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* This is a hook to use the UserSubscriptionContext.
|
|
27
|
-
*
|
|
28
|
-
* @requires UserSubscriptionContext
|
|
29
|
-
* @example
|
|
30
|
-
* import { useUserSubscription } from "@trackunit/react-core-hooks";
|
|
31
|
-
* const { numberOfDaysWithAccessToHistoricalData, packageType } = useUserSubscription();
|
|
32
|
-
* // use it for something
|
|
33
|
-
* const data = fetchData(numberOfDaysWithAccessToHistoricalData)
|
|
34
|
-
* @see { UserSubscription}
|
|
35
|
-
*/
|
|
36
|
-
const useUserSubscription = () => {
|
|
37
|
-
const context = useContext(UserSubscriptionContext);
|
|
38
|
-
if (!context) {
|
|
39
|
-
throw new Error("useUserSubscription must be used within the UserSubscriptionContext");
|
|
40
|
-
}
|
|
41
|
-
return context;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Hook providing the authenticated user's profile, account, and permission data
|
|
46
|
-
* from the CurrentUserContext.
|
|
47
|
-
*
|
|
48
|
-
* ### When to use
|
|
49
|
-
* Use `useCurrentUser` when you need the user's identity or account context — displaying
|
|
50
|
-
* the user's name, resolving the effective account ID, or branching logic based on
|
|
51
|
-
* `isAssuming` or `isTrackunitUser`.
|
|
52
|
-
*
|
|
53
|
-
* ### When not to use
|
|
54
|
-
* - If you only need a boolean check for a single permission — use `useUserPermission`.
|
|
55
|
-
* - To gate entire routes — prefer server-side access control or `hasAccessTo` from
|
|
56
|
-
* `useNavigateInHost`.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* import { useCurrentUser } from "@trackunit/react-core-hooks";
|
|
60
|
-
* const { assumedUser, accountId } = useCurrentUser();
|
|
61
|
-
*
|
|
62
|
-
* const { data, loading } = useGetAccountByIdQuery({
|
|
63
|
-
* variables: { accountId: assumedUser?.accountId || accountId || "" },
|
|
64
|
-
* });
|
|
65
|
-
* @see { CurrentUserContext}
|
|
66
|
-
*/
|
|
67
|
-
const useCurrentUser = () => {
|
|
68
|
-
const context = useContext(CurrentUserContext);
|
|
69
|
-
if (!context) {
|
|
70
|
-
throw new Error("useCurrentUser must be used within the CurrentUserProvider");
|
|
71
|
-
}
|
|
72
|
-
return context;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Async wrapper around {@link useValidateAccess}. Each helper awaits until the
|
|
77
|
-
* underlying user, subscription, and feature-flag data has finished loading
|
|
78
|
-
* before resolving, so callers such as router guards can check access without
|
|
79
|
-
* having to observe the synchronous `loading` flag themselves.
|
|
80
|
-
*/
|
|
81
|
-
const useValidateAccessAsync = () => {
|
|
82
|
-
const { hasAccessTo, hasFeatureFlag, hasPermission, loading } = useValidateAccess();
|
|
83
|
-
const hasAccessToRef = useRef(hasAccessTo);
|
|
84
|
-
hasAccessToRef.current = hasAccessTo;
|
|
85
|
-
const hasFeatureFlagRef = useRef(hasFeatureFlag);
|
|
86
|
-
hasFeatureFlagRef.current = hasFeatureFlag;
|
|
87
|
-
const hasPermissionRef = useRef(hasPermission);
|
|
88
|
-
hasPermissionRef.current = hasPermission;
|
|
89
|
-
const loadingRef = useRef(loading);
|
|
90
|
-
loadingRef.current = loading;
|
|
91
|
-
return useMemo(() => {
|
|
92
|
-
const waitForLoading = async () => {
|
|
93
|
-
if (loadingRef.current) {
|
|
94
|
-
await new Promise(resolve => {
|
|
95
|
-
const intervalId = setInterval(() => {
|
|
96
|
-
if (!loadingRef.current) {
|
|
97
|
-
clearInterval(intervalId);
|
|
98
|
-
resolve();
|
|
99
|
-
}
|
|
100
|
-
}, 10);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
return {
|
|
105
|
-
hasAccessTo: async (requiredAccessTo) => {
|
|
106
|
-
await waitForLoading();
|
|
107
|
-
return hasAccessToRef.current(requiredAccessTo);
|
|
108
|
-
},
|
|
109
|
-
hasFeatureFlag: async (flag) => {
|
|
110
|
-
await waitForLoading();
|
|
111
|
-
return hasFeatureFlagRef.current(flag);
|
|
112
|
-
},
|
|
113
|
-
hasPermission: async (permission) => {
|
|
114
|
-
await waitForLoading();
|
|
115
|
-
return hasPermissionRef.current(permission);
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
}, [loadingRef, hasAccessToRef, hasFeatureFlagRef, hasPermissionRef]);
|
|
119
|
-
};
|
|
120
|
-
/**
|
|
121
|
-
* Synchronously evaluates the current user's access via subscription features,
|
|
122
|
-
* feature flags, and permissions. Returns predicate helpers plus a `loading`
|
|
123
|
-
* flag that stays true until user, subscription, and flag data are all present.
|
|
124
|
-
*/
|
|
125
|
-
const useValidateAccess = () => {
|
|
126
|
-
const { isAuthenticated, permissions: userPermissions } = useCurrentUser();
|
|
127
|
-
const { features, loading: featuresLoading } = useUserSubscription();
|
|
128
|
-
const { flags: allFlags } = useFeatureFlags();
|
|
129
|
-
const loading = useMemo(() => allFlags === null || featuresLoading || !userPermissions, [allFlags, featuresLoading, userPermissions]);
|
|
130
|
-
const hasFeatureFlag = useCallback((flag) => {
|
|
131
|
-
return allFlags?.find(({ key }) => key === flag)?.state ?? null;
|
|
132
|
-
}, [allFlags]);
|
|
133
|
-
const hasAccessTo = useCallback((requiredAccessTo) => {
|
|
134
|
-
if (isAuthenticated) {
|
|
135
|
-
return hasAccessToFeature(features, requiredAccessTo);
|
|
136
|
-
}
|
|
137
|
-
return false;
|
|
138
|
-
}, [features, isAuthenticated]);
|
|
139
|
-
const hasPermission = useCallback((permission) => {
|
|
140
|
-
if (isAuthenticated) {
|
|
141
|
-
return userPermissions?.includes(permission) ?? false;
|
|
142
|
-
}
|
|
143
|
-
return false;
|
|
144
|
-
}, [isAuthenticated, userPermissions]);
|
|
145
|
-
return useMemo(() => {
|
|
146
|
-
return {
|
|
147
|
-
hasAccessTo,
|
|
148
|
-
hasFeatureFlag,
|
|
149
|
-
hasPermission,
|
|
150
|
-
loading,
|
|
151
|
-
};
|
|
152
|
-
}, [hasAccessTo, hasFeatureFlag, hasPermission, loading]);
|
|
153
|
-
};
|
|
154
|
-
const hasAccessToFeature = (myFeatures, feature) => {
|
|
155
|
-
return myFeatures?.some(x => x.id === feature) ?? null;
|
|
156
|
-
};
|
|
157
|
-
|
|
158
7
|
/**
|
|
159
8
|
* Hook to get the analytics context.
|
|
160
9
|
*
|
|
@@ -368,39 +217,21 @@ const useExportDataContext = () => {
|
|
|
368
217
|
};
|
|
369
218
|
|
|
370
219
|
/**
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
* Return values:
|
|
374
|
-
* - `null` while the host feature flags have not loaded yet.
|
|
375
|
-
* - the flag's boolean `state` when the flag exists in the loaded set.
|
|
376
|
-
* - `null` when the flags are loaded but the requested flag is not present,
|
|
377
|
-
* unless `options.defaultValueWhenMissing` is provided, in which case that
|
|
378
|
-
* value is returned.
|
|
379
|
-
*
|
|
380
|
-
* The hook is generic so callers can narrow the accepted key. Internal callers
|
|
381
|
-
* pass the strongly-typed `Flag` union for type safety, e.g.
|
|
382
|
-
* `useHostFeatureFlag<Flag>(Flags.SOME_FLAG, { defaultValueWhenMissing: true })`.
|
|
383
|
-
* External callers may pass any string.
|
|
384
|
-
*
|
|
385
|
-
* Must be used within a `FeatureFlagContext` (provided by `TrackunitProviders`).
|
|
220
|
+
* This is a hook to use the FeatureFlagContext.
|
|
386
221
|
*
|
|
222
|
+
* @requires FeatureFlagContext
|
|
387
223
|
* @example
|
|
388
|
-
* import {
|
|
389
|
-
* const
|
|
390
|
-
*
|
|
224
|
+
* import { useFeatureFlags } from "@trackunit/react-core-hooks";
|
|
225
|
+
* const { flags } = useFeatureFlags();
|
|
226
|
+
* // use flags to control visibility/access ...
|
|
227
|
+
* @see (@link FeatureFlagContext)
|
|
391
228
|
*/
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
if (flags && options.defaultValueWhenMissing !== undefined) {
|
|
400
|
-
return options.defaultValueWhenMissing;
|
|
401
|
-
}
|
|
402
|
-
return null;
|
|
403
|
-
}, [flags, flag, options.defaultValueWhenMissing]);
|
|
229
|
+
const useFeatureFlags = () => {
|
|
230
|
+
const context = useContext(FeatureFlagContext);
|
|
231
|
+
if (!context) {
|
|
232
|
+
throw new Error("useFeatureFlags must be used within a FeatureFlagContext");
|
|
233
|
+
}
|
|
234
|
+
return context;
|
|
404
235
|
};
|
|
405
236
|
|
|
406
237
|
/**
|
|
@@ -1063,6 +894,25 @@ const useSiteRuntime = () => {
|
|
|
1063
894
|
return useMemo(() => ({ siteInfo, loading, error }), [siteInfo, loading, error]);
|
|
1064
895
|
};
|
|
1065
896
|
|
|
897
|
+
/**
|
|
898
|
+
* This is a hook to use the UserSubscriptionContext.
|
|
899
|
+
*
|
|
900
|
+
* @requires UserSubscriptionContext
|
|
901
|
+
* @example
|
|
902
|
+
* import { useUserSubscription } from "@trackunit/react-core-hooks";
|
|
903
|
+
* const { numberOfDaysWithAccessToHistoricalData, packageType } = useUserSubscription();
|
|
904
|
+
* // use it for something
|
|
905
|
+
* const data = fetchData(numberOfDaysWithAccessToHistoricalData)
|
|
906
|
+
* @see { UserSubscription}
|
|
907
|
+
*/
|
|
908
|
+
const useUserSubscription = () => {
|
|
909
|
+
const context = useContext(UserSubscriptionContext);
|
|
910
|
+
if (!context) {
|
|
911
|
+
throw new Error("useUserSubscription must be used within the UserSubscriptionContext");
|
|
912
|
+
}
|
|
913
|
+
return context;
|
|
914
|
+
};
|
|
915
|
+
|
|
1066
916
|
/**
|
|
1067
917
|
* This is a hook to use the TimeRangeProvider.
|
|
1068
918
|
*
|
|
@@ -1108,6 +958,37 @@ const useToast = () => {
|
|
|
1108
958
|
return toastContext;
|
|
1109
959
|
};
|
|
1110
960
|
|
|
961
|
+
/**
|
|
962
|
+
* Hook providing the authenticated user's profile, account, and permission data
|
|
963
|
+
* from the CurrentUserContext.
|
|
964
|
+
*
|
|
965
|
+
* ### When to use
|
|
966
|
+
* Use `useCurrentUser` when you need the user's identity or account context — displaying
|
|
967
|
+
* the user's name, resolving the effective account ID, or branching logic based on
|
|
968
|
+
* `isAssuming` or `isTrackunitUser`.
|
|
969
|
+
*
|
|
970
|
+
* ### When not to use
|
|
971
|
+
* - If you only need a boolean check for a single permission — use `useUserPermission`.
|
|
972
|
+
* - To gate entire routes — prefer server-side access control or `hasAccessTo` from
|
|
973
|
+
* `useNavigateInHost`.
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* import { useCurrentUser } from "@trackunit/react-core-hooks";
|
|
977
|
+
* const { assumedUser, accountId } = useCurrentUser();
|
|
978
|
+
*
|
|
979
|
+
* const { data, loading } = useGetAccountByIdQuery({
|
|
980
|
+
* variables: { accountId: assumedUser?.accountId || accountId || "" },
|
|
981
|
+
* });
|
|
982
|
+
* @see { CurrentUserContext}
|
|
983
|
+
*/
|
|
984
|
+
const useCurrentUser = () => {
|
|
985
|
+
const context = useContext(CurrentUserContext);
|
|
986
|
+
if (!context) {
|
|
987
|
+
throw new Error("useCurrentUser must be used within the CurrentUserProvider");
|
|
988
|
+
}
|
|
989
|
+
return context;
|
|
990
|
+
};
|
|
991
|
+
|
|
1111
992
|
/**
|
|
1112
993
|
* This is a hook providing the Current User language.
|
|
1113
994
|
*
|
|
@@ -1523,4 +1404,4 @@ const useWidgetConfig = () => {
|
|
|
1523
1404
|
return result;
|
|
1524
1405
|
};
|
|
1525
1406
|
|
|
1526
|
-
export { fetchAssetBlobUrl, useAnalytics, useAssetRuntime, useAssetSorting, useConfirmationDialog, useCurrentUser, useCurrentUserFavoriteAdvancedSensors, useCurrentUserFavoriteInsights, useCurrentUserLanguage, useCurrentUserSystemOfMeasurement, useCurrentUserTimeFormat, useCurrentUserTimeZonePreference, useCustomerRuntime, useEnvironment, useErrorHandler, useErrorHandlerOrNull, useEventRuntime, useExportDataContext, useFeatureBranchQueryString, useFeatureFlags, useFilterBarContext, useGeolocation, useHasAccessTo,
|
|
1407
|
+
export { fetchAssetBlobUrl, useAnalytics, useAssetRuntime, useAssetSorting, useConfirmationDialog, useCurrentUser, useCurrentUserFavoriteAdvancedSensors, useCurrentUserFavoriteInsights, useCurrentUserLanguage, useCurrentUserSystemOfMeasurement, useCurrentUserTimeFormat, useCurrentUserTimeZonePreference, useCustomerRuntime, useEnvironment, useErrorHandler, useErrorHandlerOrNull, useEventRuntime, useExportDataContext, useFeatureBranchQueryString, useFeatureFlags, useFilterBarContext, useGeolocation, useHasAccessTo, useImageUploader, useIrisAppId, useIrisAppImage, useIrisAppName, useModalDialogContext, useNavigateInHost, useOemBrandingContext, useSettingsUtils, useSiteRuntime, useTimeRange, useToast, useToken, useUserPermission, useUserSubscription, useWidgetConfig, useWidgetConfigAsync };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-core-hooks",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.59-alpha-df0e8d549b3.0",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"es-toolkit": "^1.39.10",
|
|
11
|
-
"@trackunit/iris-app-runtime-core": "1.17.
|
|
12
|
-
"@trackunit/iris-app-runtime-core-api": "1.16.
|
|
13
|
-
"@trackunit/react-core-contexts-api": "1.17.
|
|
11
|
+
"@trackunit/iris-app-runtime-core": "1.17.54-alpha-df0e8d549b3.0",
|
|
12
|
+
"@trackunit/iris-app-runtime-core-api": "1.16.53-alpha-df0e8d549b3.0",
|
|
13
|
+
"@trackunit/react-core-contexts-api": "1.17.53-alpha-df0e8d549b3.0",
|
|
14
14
|
"zod": "^3.25.76"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
package/src/index.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export * from "./access/ExtensionRouterContext";
|
|
2
|
-
export { useValidateAccess, useValidateAccessAsync } from "./access/useValidateAccessAsync";
|
|
3
|
-
export type { ValidateAccess } from "./access/useValidateAccessAsync";
|
|
4
1
|
export * from "./analytics/useAnalytics";
|
|
5
2
|
export * from "./assetSorting/useAssetSorting";
|
|
6
3
|
export * from "./confirmationDialog/useConfirmationDialog";
|
|
@@ -8,7 +5,6 @@ export * from "./environment/useEnvironment";
|
|
|
8
5
|
export * from "./errorHandling/useErrorHandler";
|
|
9
6
|
export * from "./exportData/useExportDataContext";
|
|
10
7
|
export * from "./featureFlags/useFeatureFlags";
|
|
11
|
-
export * from "./featureFlags/useHostFeatureFlag";
|
|
12
8
|
export * from "./fetchAssetBlobUrl";
|
|
13
9
|
export * from "./filterBar/useFilterBarContext";
|
|
14
10
|
export * from "./geolocation/useGeolocation";
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { HasAccess, ValidateAccess } from "./useValidateAccessAsync";
|
|
2
|
-
export interface ExtensionRouterContext {
|
|
3
|
-
hasAccessToRoute: (requiredAccessTo: ValidateAccess) => Promise<HasAccess>;
|
|
4
|
-
hasFeatureFlag: (flag: string) => Promise<HasAccess>;
|
|
5
|
-
hasPermission: (permission: string) => Promise<HasAccess>;
|
|
6
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { LegacyFeature } from "@trackunit/iris-app-runtime-core-api";
|
|
2
|
-
export type HasAccess = boolean | null;
|
|
3
|
-
export type ValidateAccess = LegacyFeature;
|
|
4
|
-
/**
|
|
5
|
-
* Async wrapper around {@link useValidateAccess}. Each helper awaits until the
|
|
6
|
-
* underlying user, subscription, and feature-flag data has finished loading
|
|
7
|
-
* before resolving, so callers such as router guards can check access without
|
|
8
|
-
* having to observe the synchronous `loading` flag themselves.
|
|
9
|
-
*/
|
|
10
|
-
export declare const useValidateAccessAsync: () => {
|
|
11
|
-
hasAccessTo: (requiredAccessTo: ValidateAccess) => Promise<HasAccess>;
|
|
12
|
-
hasFeatureFlag: (flag: string) => Promise<HasAccess>;
|
|
13
|
-
hasPermission: (permission: string) => Promise<HasAccess>;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Synchronously evaluates the current user's access via subscription features,
|
|
17
|
-
* feature flags, and permissions. Returns predicate helpers plus a `loading`
|
|
18
|
-
* flag that stays true until user, subscription, and flag data are all present.
|
|
19
|
-
*/
|
|
20
|
-
export declare const useValidateAccess: () => {
|
|
21
|
-
hasAccessTo: (requiredAccessTo: ValidateAccess) => HasAccess;
|
|
22
|
-
hasFeatureFlag: (flag: string) => HasAccess;
|
|
23
|
-
hasPermission: (permission: string) => HasAccess;
|
|
24
|
-
loading: boolean;
|
|
25
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export interface UseHostFeatureFlagOptions {
|
|
2
|
-
/**
|
|
3
|
-
* Value returned when the feature flags have loaded but the requested flag is
|
|
4
|
-
* not present in the loaded set. When omitted, a missing flag resolves to
|
|
5
|
-
* `null` (the historical behaviour).
|
|
6
|
-
*
|
|
7
|
-
* Note: this only applies once flags are loaded. While flags are still
|
|
8
|
-
* loading the hook always returns `null` regardless of this option.
|
|
9
|
-
*/
|
|
10
|
-
defaultValueWhenMissing?: boolean;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Reads a host feature flag by key.
|
|
14
|
-
*
|
|
15
|
-
* Return values:
|
|
16
|
-
* - `null` while the host feature flags have not loaded yet.
|
|
17
|
-
* - the flag's boolean `state` when the flag exists in the loaded set.
|
|
18
|
-
* - `null` when the flags are loaded but the requested flag is not present,
|
|
19
|
-
* unless `options.defaultValueWhenMissing` is provided, in which case that
|
|
20
|
-
* value is returned.
|
|
21
|
-
*
|
|
22
|
-
* The hook is generic so callers can narrow the accepted key. Internal callers
|
|
23
|
-
* pass the strongly-typed `Flag` union for type safety, e.g.
|
|
24
|
-
* `useHostFeatureFlag<Flag>(Flags.SOME_FLAG, { defaultValueWhenMissing: true })`.
|
|
25
|
-
* External callers may pass any string.
|
|
26
|
-
*
|
|
27
|
-
* Must be used within a `FeatureFlagContext` (provided by `TrackunitProviders`).
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* import { useHostFeatureFlag } from "@trackunit/react-core-hooks";
|
|
31
|
-
* const isEnabled = useHostFeatureFlag("my_flag", { defaultValueWhenMissing: true });
|
|
32
|
-
* @see useFeatureFlags
|
|
33
|
-
*/
|
|
34
|
-
export declare const useHostFeatureFlag: <TFlag extends string = string>(flag: TFlag, options: UseHostFeatureFlagOptions) => boolean | null;
|