@applicaster/zapp-react-native-utils 16.0.0-alpha.1701179921 → 16.0.0-alpha.1733304298
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/actionUtils/index.ts +3 -1
- package/actionsExecutor/__tests__/StorageActions.test.ts +98 -0
- package/actionsExecutor/__tests__/feedDecorator.test.ts +30 -0
- package/actionsExecutor/__tests__/screenToggleFlag.test.ts +90 -0
- package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +139 -5
- package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +24 -0
- package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +72 -0
- package/actionsExecutor/actions/navigateToScreen.ts +29 -45
- package/actionsExecutor/actions/openBottomSheet.ts +2 -0
- package/actionsExecutor/actions/screenToggleFlag.ts +1 -3
- package/actionsExecutor/feedDecorator.ts +6 -0
- package/analyticsUtils/__tests__/playerAnalyticsSafeguard.test.ts +62 -0
- package/analyticsUtils/playerAnalyticsTracker.ts +1 -0
- package/appUtils/contextKeysManager/__tests__/getKey/failure.test.ts +1 -22
- package/appUtils/contextKeysManager/__tests__/getKey/success/fullSearch.test.ts +1 -25
- package/appUtils/contextKeysManager/__tests__/getKey/success/searchInSecure.test.ts +1 -7
- package/appUtils/contextKeysManager/__tests__/removeKey/failure.test.ts +0 -25
- package/appUtils/contextKeysManager/__tests__/removeKey/success.test.ts +0 -6
- package/appUtils/contextKeysManager/__tests__/setKey/failure/failedToSaveInStorage.test.ts +0 -40
- package/appUtils/contextKeysManager/__tests__/setKey/{success/saveItemAndReference.test.ts → success.test.ts} +2 -42
- package/appUtils/contextKeysManager/index.ts +28 -129
- package/appUtils/playerManager/__tests__/playerDiagnostics.test.ts +62 -0
- package/appUtils/playerManager/__tests__/playerToolbox.test.ts +281 -0
- package/appUtils/playerManager/__tests__/usePlayerControllerSetup.test.tsx +62 -0
- package/appUtils/playerManager/index.ts +7 -0
- package/appUtils/playerManager/player.ts +15 -0
- package/appUtils/playerManager/playerToolbox.ts +196 -0
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +6 -0
- package/cellUtils/__tests__/getButtonsEnabled.test.ts +28 -0
- package/cellUtils/__tests__/getCellNavigationEnabled.test.ts +36 -0
- package/cellUtils/__tests__/getIsCellNavigationEnabled.test.ts +35 -0
- package/cellUtils/__tests__/isCellNavigationEnabled.test.ts +21 -0
- package/cellUtils/index.ts +40 -0
- package/configurationUtils/__tests__/modalBlocksParser.test.ts +134 -0
- package/configurationUtils/modalBlocksParser.ts +38 -0
- package/focusManager/FocusManager.ts +26 -0
- package/focusManager/__tests__/FocusManager.test.ts +9 -0
- package/localizationUtils/index.ts +12 -6
- package/logger/__tests__/xrayToolbox.test.ts +69 -0
- package/logger/xrayToolbox.ts +67 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +8 -0
- package/manifestUtils/keys.js +7 -2
- package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +21 -0
- package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +1 -1
- package/manifestUtils/mobileAction/groups/defaults.js +2 -1
- package/modalState/ModalOrchestrator.ts +1 -0
- package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +23 -0
- package/modalState/index.ts +2 -0
- package/modalState/types.ts +11 -0
- package/navigationUtils/xrayNavigator.ts +12 -0
- package/package.json +3 -2
- package/quickBrickAssets/__tests__/index.test.ts +18 -0
- package/quickBrickAssets/index.ts +35 -0
- package/reactHooks/__tests__/getPluginConfiguration.test.ts +82 -0
- package/reactHooks/actions/index.ts +29 -23
- package/reactHooks/connection/index.tsx +8 -5
- package/reactHooks/feed/__tests__/feedInvalidation.test.tsx +338 -0
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +8 -2
- package/reactHooks/feed/__tests__/useMarkPipesDataStale.test.tsx +243 -0
- package/reactHooks/feed/useFeedLoader.tsx +28 -1
- package/reactHooks/feed/useLoadPipesDataDispatch.ts +12 -11
- package/reactHooks/feed/useMarkPipesDataStale.ts +68 -23
- package/reactHooks/usePluginConfiguration.ts +84 -46
- package/riverComponetsMeasurementProvider/index.tsx +3 -1
- package/uiActionsRegistrator/__tests__/subscribe.test.ts +26 -0
- package/uiActionsRegistrator/registry.ts +16 -0
- package/appUtils/contextKeysManager/__tests__/getKey/success/byReference.test.ts +0 -56
- package/appUtils/contextKeysManager/__tests__/getKeyByReference/failure.test.ts +0 -265
- package/appUtils/contextKeysManager/__tests__/getKeyByReference/success.test.ts +0 -303
- package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/failure.test.ts +0 -82
- package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/success.test.ts +0 -41
- package/appUtils/contextKeysManager/__tests__/setKey/success/saveItemButNotReference.test.ts +0 -208
- package/appUtils/contextKeysManager/__tests__/setReferenceForKey/failure.test.ts +0 -93
- package/appUtils/contextKeysManager/__tests__/setReferenceForKey/success.test.ts +0 -45
|
@@ -19,14 +19,6 @@ export const useLoadPipesDataDispatch = () => {
|
|
|
19
19
|
(screenData as LegacyNavigationScreenData)?.targetScreen?.id ??
|
|
20
20
|
screenData?.id;
|
|
21
21
|
|
|
22
|
-
const onLoadCB = (options) => (data, _err) => {
|
|
23
|
-
options?.callback?.();
|
|
24
|
-
|
|
25
|
-
if (data) {
|
|
26
|
-
applyScreenRouteDefaults(data, screenStateStore, pathname);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
22
|
return React.useCallback(
|
|
31
23
|
(
|
|
32
24
|
url: string,
|
|
@@ -36,6 +28,9 @@ export const useLoadPipesDataDispatch = () => {
|
|
|
36
28
|
clearCache?: boolean;
|
|
37
29
|
silentRefresh?: boolean;
|
|
38
30
|
parentFeed?: string;
|
|
31
|
+
method?: "get" | "post";
|
|
32
|
+
bodyParams?: Record<string, unknown>;
|
|
33
|
+
loadLocalFavorites?: boolean;
|
|
39
34
|
} = {},
|
|
40
35
|
{
|
|
41
36
|
withResolvers = false,
|
|
@@ -46,18 +41,24 @@ export const useLoadPipesDataDispatch = () => {
|
|
|
46
41
|
} = {}
|
|
47
42
|
) => {
|
|
48
43
|
if (url) {
|
|
49
|
-
dispatch(
|
|
44
|
+
return dispatch(
|
|
50
45
|
ZappPipes.loadPipesData(url, {
|
|
51
46
|
riverId,
|
|
52
47
|
resolvers: withResolvers ? resolvers : undefined,
|
|
53
48
|
...options,
|
|
54
49
|
callback: withScreenRouteMapping
|
|
55
|
-
?
|
|
50
|
+
? (data, error) => {
|
|
51
|
+
options.callback?.(data, error);
|
|
52
|
+
|
|
53
|
+
if (data) {
|
|
54
|
+
applyScreenRouteDefaults(data, screenStateStore, pathname);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
56
57
|
: options?.callback,
|
|
57
58
|
})
|
|
58
59
|
);
|
|
59
60
|
}
|
|
60
61
|
},
|
|
61
|
-
[]
|
|
62
|
+
[dispatch, riverId, resolvers, screenStateStore, pathname]
|
|
62
63
|
);
|
|
63
64
|
};
|
|
@@ -3,46 +3,91 @@ import React from "react";
|
|
|
3
3
|
import { getDatasourceUrl } from "@applicaster/zapp-react-native-ui-components/Decorators/RiverFeedLoader/utils/getDatasourceUrl";
|
|
4
4
|
import { usePipesContexts } from "@applicaster/zapp-react-native-ui-components/Decorators/RiverFeedLoader/utils/usePipesContexts";
|
|
5
5
|
import { markPipesDataStale } from "@applicaster/zapp-react-native-redux/ZappPipes";
|
|
6
|
-
|
|
7
|
-
import { useRoute } from "../navigation";
|
|
8
6
|
import { useAppDispatch } from "@applicaster/zapp-react-native-redux";
|
|
7
|
+
import { isTrue } from "../../booleanUtils";
|
|
8
|
+
import { refreshCoordinator } from "../../refreshUtils/RefreshCoordinator";
|
|
9
|
+
import { useNavigation, useRoute } from "../navigation";
|
|
10
|
+
import { usePrevious } from "../utils";
|
|
11
|
+
|
|
12
|
+
const getRefreshableComponents = (components: ZappUIComponent[]) =>
|
|
13
|
+
components?.filter(
|
|
14
|
+
(c) => isTrue(c?.rules?.clear_cache_on_reload) && c?.data?.source
|
|
15
|
+
) || [];
|
|
9
16
|
|
|
10
17
|
/**
|
|
11
18
|
* Mark a river's `clear_cache_on_reload` feeds as stale when the screen is
|
|
12
19
|
* unmounted. The cached data is kept (so a screen sharing the same feed does
|
|
13
20
|
* not lose it mid-mount) and revalidated on next access.
|
|
21
|
+
*
|
|
22
|
+
* Also triggers refresh for mounted components and marks feeds stale when
|
|
23
|
+
* returning back to this screen from a deeper route in the Transitioner
|
|
24
|
+
* (where the screen was kept mounted in the background).
|
|
25
|
+
*
|
|
14
26
|
* @param {string} riverId screen id
|
|
15
27
|
* @param {Array} riverComponents list of UI components
|
|
16
28
|
*/
|
|
17
|
-
export const useMarkPipesDataStale = (
|
|
29
|
+
export const useMarkPipesDataStale = (
|
|
30
|
+
riverId: string,
|
|
31
|
+
riverComponents: ZappUIComponent[]
|
|
32
|
+
) => {
|
|
18
33
|
const dispatch = useAppDispatch();
|
|
19
34
|
const { screenData, pathname } = useRoute();
|
|
20
35
|
const pipesContexts = usePipesContexts(riverId, pathname);
|
|
36
|
+
const { currentRoute } = useNavigation();
|
|
37
|
+
const prevRoute = usePrevious(currentRoute);
|
|
21
38
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
component
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
if (url) {
|
|
40
|
-
dispatch(markPipesDataStale(url, { riverId }));
|
|
41
|
-
}
|
|
39
|
+
const isBackToScreen = Boolean(
|
|
40
|
+
prevRoute && prevRoute !== currentRoute && currentRoute === pathname
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const invalidateComponents = React.useCallback(
|
|
44
|
+
(components: ZappUIComponent[]) => {
|
|
45
|
+
components.forEach((component) => {
|
|
46
|
+
const url = getDatasourceUrl(
|
|
47
|
+
{ screenData, ...pipesContexts },
|
|
48
|
+
component
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (url) {
|
|
52
|
+
dispatch(markPipesDataStale(url, { riverId }));
|
|
42
53
|
}
|
|
43
54
|
});
|
|
55
|
+
},
|
|
56
|
+
[dispatch, screenData, pipesContexts, riverId]
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Stale invalidation on unmount (existing behavior)
|
|
60
|
+
/* eslint-disable @wogns3623/better-exhaustive-deps/exhaustive-deps */
|
|
61
|
+
React.useEffect(() => {
|
|
62
|
+
return () => {
|
|
63
|
+
if (pathname?.includes("videoModal")) return;
|
|
64
|
+
|
|
65
|
+
invalidateComponents(getRefreshableComponents(riverComponents));
|
|
44
66
|
};
|
|
45
67
|
}, []);
|
|
68
|
+
/* eslint-enable @wogns3623/better-exhaustive-deps/exhaustive-deps */
|
|
69
|
+
|
|
70
|
+
// Reload when returning back to this screen from another screen
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
if (!isBackToScreen || pathname?.includes("videoModal")) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const refreshable = getRefreshableComponents(riverComponents);
|
|
77
|
+
|
|
78
|
+
if (refreshable.length === 0) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
invalidateComponents(refreshable);
|
|
83
|
+
refreshCoordinator.triggerRefresh(refreshable, riverId);
|
|
84
|
+
}, [
|
|
85
|
+
isBackToScreen,
|
|
86
|
+
pathname,
|
|
87
|
+
riverComponents,
|
|
88
|
+
riverId,
|
|
89
|
+
invalidateComponents,
|
|
90
|
+
]);
|
|
46
91
|
};
|
|
47
92
|
|
|
48
93
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as R from "ramda";
|
|
2
1
|
import { useMemo } from "react";
|
|
3
2
|
|
|
4
3
|
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
@@ -6,7 +5,8 @@ import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
|
6
5
|
// importing this way prevents typescript packages importing from this
|
|
7
6
|
// file to go crazy reporting errors on JS
|
|
8
7
|
import { populateConfigurationValues } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
9
|
-
import { useLocalizedStrings } from "../localizationUtils";
|
|
8
|
+
import { getLocalizedStrings, useLocalizedStrings } from "../localizationUtils";
|
|
9
|
+
import { isNil, pathOr } from "../utils";
|
|
10
10
|
|
|
11
11
|
const populateFields = (
|
|
12
12
|
data,
|
|
@@ -14,16 +14,72 @@ const populateFields = (
|
|
|
14
14
|
pathToValues: string | string[] = "configuration",
|
|
15
15
|
skipDefaults = false
|
|
16
16
|
) =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
17
|
+
populateConfigurationValues(
|
|
18
|
+
manifestFields,
|
|
19
|
+
pathOr({}, pathToValues, data),
|
|
20
|
+
skipDefaults
|
|
21
|
+
);
|
|
22
22
|
|
|
23
23
|
export function parsePluginConfiguration(plugin, manifest) {
|
|
24
24
|
return populateFields(plugin, manifest.custom_configuration_fields);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
const getDefaultLocalizations = (manifest) =>
|
|
28
|
+
manifest?.localizations?.fields?.reduce(
|
|
29
|
+
(defaults, { key, initial_value }) => ({
|
|
30
|
+
en: { ...defaults.en, [key]: initial_value },
|
|
31
|
+
}),
|
|
32
|
+
{ en: {} }
|
|
33
|
+
) || {};
|
|
34
|
+
|
|
35
|
+
const isPlainObject = (value) =>
|
|
36
|
+
Object.prototype.toString.call(value) === "[object Object]";
|
|
37
|
+
|
|
38
|
+
// Deep merge where configured values win, except nil ones (defaults fill the gaps)
|
|
39
|
+
export const mergeLocalizations = (configured, defaults) => {
|
|
40
|
+
if (!isPlainObject(configured)) return { ...defaults };
|
|
41
|
+
if (!isPlainObject(defaults)) return { ...configured };
|
|
42
|
+
|
|
43
|
+
// Configured keys first, as R.mergeDeepWith: the first language is the fallback
|
|
44
|
+
return Object.keys({ ...configured, ...defaults }).reduce((merged, key) => {
|
|
45
|
+
const left = configured[key];
|
|
46
|
+
const right = defaults[key];
|
|
47
|
+
|
|
48
|
+
if (!(key in defaults)) {
|
|
49
|
+
merged[key] = left;
|
|
50
|
+
} else if (!(key in configured)) {
|
|
51
|
+
merged[key] = right;
|
|
52
|
+
} else if (isPlainObject(left) && isPlainObject(right)) {
|
|
53
|
+
merged[key] = mergeLocalizations(left, right);
|
|
54
|
+
} else {
|
|
55
|
+
merged[key] = isNil(left) ? right : left;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return merged;
|
|
59
|
+
}, {});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* usePluginConfiguration outside React: the same result, read once instead of subscribed.
|
|
64
|
+
*/
|
|
65
|
+
export function getPluginConfiguration<T = any, S = Record<string, any>>(
|
|
66
|
+
plugin,
|
|
67
|
+
manifest?: any
|
|
68
|
+
): { configurationFields: T; localizations: S } {
|
|
69
|
+
return {
|
|
70
|
+
configurationFields: populateFields(
|
|
71
|
+
plugin,
|
|
72
|
+
manifest?.custom_configuration_fields
|
|
73
|
+
),
|
|
74
|
+
localizations: getLocalizedStrings(
|
|
75
|
+
mergeLocalizations(
|
|
76
|
+
plugin?.configuration?.localizations,
|
|
77
|
+
getDefaultLocalizations(manifest)
|
|
78
|
+
)
|
|
79
|
+
),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
27
83
|
export function usePluginConfiguration<T = any, S = Record<string, any>>(
|
|
28
84
|
identifier: string,
|
|
29
85
|
manifest?: any
|
|
@@ -31,7 +87,7 @@ export function usePluginConfiguration<T = any, S = Record<string, any>>(
|
|
|
31
87
|
const plugins = usePlugins();
|
|
32
88
|
|
|
33
89
|
const plugin = useMemo(
|
|
34
|
-
() =>
|
|
90
|
+
() => plugins?.find((item) => item?.identifier === identifier),
|
|
35
91
|
[plugins, identifier]
|
|
36
92
|
);
|
|
37
93
|
|
|
@@ -40,29 +96,20 @@ export function usePluginConfiguration<T = any, S = Record<string, any>>(
|
|
|
40
96
|
[plugin, manifest]
|
|
41
97
|
);
|
|
42
98
|
|
|
43
|
-
const defaultLocalizations = useMemo(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
R.assocPath(["en", key], initial_value, defaults),
|
|
48
|
-
{ en: {} }
|
|
49
|
-
) || {}
|
|
50
|
-
);
|
|
51
|
-
}, [manifest]);
|
|
52
|
-
|
|
53
|
-
const localizationsParams = useMemo(() => {
|
|
54
|
-
return {
|
|
55
|
-
localizations: R.mergeDeepWith(
|
|
56
|
-
(left, right) => {
|
|
57
|
-
if (R.isNil(left)) return right;
|
|
99
|
+
const defaultLocalizations = useMemo(
|
|
100
|
+
() => getDefaultLocalizations(manifest),
|
|
101
|
+
[manifest]
|
|
102
|
+
);
|
|
58
103
|
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
const localizationsParams = useMemo(
|
|
105
|
+
() => ({
|
|
106
|
+
localizations: mergeLocalizations(
|
|
61
107
|
plugin?.configuration?.localizations,
|
|
62
108
|
defaultLocalizations
|
|
63
109
|
),
|
|
64
|
-
}
|
|
65
|
-
|
|
110
|
+
}),
|
|
111
|
+
[plugin?.configuration?.localizations, defaultLocalizations]
|
|
112
|
+
);
|
|
66
113
|
|
|
67
114
|
const localizations: S = useLocalizedStrings(localizationsParams);
|
|
68
115
|
|
|
@@ -105,29 +152,20 @@ export function useLayoutConfiguration<
|
|
|
105
152
|
"localizations",
|
|
106
153
|
];
|
|
107
154
|
|
|
108
|
-
const defaultLocalizations = useMemo(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
R.assocPath(["en", key], initial_value, defaults),
|
|
113
|
-
{ en: {} }
|
|
114
|
-
) || {}
|
|
115
|
-
);
|
|
116
|
-
}, [manifest]);
|
|
117
|
-
|
|
118
|
-
const localizationsParams = useMemo(() => {
|
|
119
|
-
return {
|
|
120
|
-
localizations: R.mergeDeepWith(
|
|
121
|
-
(left, right) => {
|
|
122
|
-
if (R.isNil(left)) return right;
|
|
155
|
+
const defaultLocalizations = useMemo(
|
|
156
|
+
() => getDefaultLocalizations(manifest),
|
|
157
|
+
[manifest]
|
|
158
|
+
);
|
|
123
159
|
|
|
124
|
-
|
|
125
|
-
|
|
160
|
+
const localizationsParams = useMemo(
|
|
161
|
+
() => ({
|
|
162
|
+
localizations: mergeLocalizations(
|
|
126
163
|
layoutData?.localizations,
|
|
127
164
|
defaultLocalizations
|
|
128
165
|
),
|
|
129
|
-
}
|
|
130
|
-
|
|
166
|
+
}),
|
|
167
|
+
[layoutData?.localizations, defaultLocalizations]
|
|
168
|
+
);
|
|
131
169
|
|
|
132
170
|
// Always call useLocalizedStrings regardless of whether localizations is in the keys
|
|
133
171
|
const localizations: L = useLocalizedStrings(localizationsParams);
|
|
@@ -34,6 +34,7 @@ type Props = {
|
|
|
34
34
|
type ItemProps = {
|
|
35
35
|
children: React.ReactNode;
|
|
36
36
|
index: number;
|
|
37
|
+
testID?: string;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
type MeasurementContext = {
|
|
@@ -153,11 +154,12 @@ export const ScreenLoadingMeasurements = ({
|
|
|
153
154
|
export const ScreenLoadingMeasurementsListItemWrapper = ({
|
|
154
155
|
children,
|
|
155
156
|
index,
|
|
157
|
+
testID,
|
|
156
158
|
}: ItemProps) => {
|
|
157
159
|
const { onLayout } = React.useContext(MeasurementsSettersContext);
|
|
158
160
|
|
|
159
161
|
return (
|
|
160
|
-
<View style={styles.container} onLayout={onLayout?.(index)}>
|
|
162
|
+
<View style={styles.container} onLayout={onLayout?.(index)} testID={testID}>
|
|
161
163
|
{children}
|
|
162
164
|
</View>
|
|
163
165
|
);
|
|
@@ -105,3 +105,29 @@ describe("isActionAvailableFor", () => {
|
|
|
105
105
|
expect(isActionAvailableFor(undefined, entry)).toBe(true);
|
|
106
106
|
});
|
|
107
107
|
});
|
|
108
|
+
|
|
109
|
+
describe("uiActionsRegistry.notifyProviderChanged", () => {
|
|
110
|
+
it("notifies subscribers for a registered provider", () => {
|
|
111
|
+
const unregister = uiActionsRegistry.registerAction("live-action", value);
|
|
112
|
+
const listener = jest.fn();
|
|
113
|
+
const unsubscribe = uiActionsRegistry.subscribe(listener);
|
|
114
|
+
|
|
115
|
+
uiActionsRegistry.notifyProviderChanged("live-action");
|
|
116
|
+
|
|
117
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
118
|
+
|
|
119
|
+
unsubscribe();
|
|
120
|
+
unregister();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("ignores names that are not registered", () => {
|
|
124
|
+
const listener = jest.fn();
|
|
125
|
+
const unsubscribe = uiActionsRegistry.subscribe(listener);
|
|
126
|
+
|
|
127
|
+
uiActionsRegistry.notifyProviderChanged("unknown-action");
|
|
128
|
+
|
|
129
|
+
expect(listener).not.toHaveBeenCalled();
|
|
130
|
+
|
|
131
|
+
unsubscribe();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -246,6 +246,22 @@ class UIActionsRegistry {
|
|
|
246
246
|
this.changeListeners.slice().forEach((listener) => listener());
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Tells consumers that a registered provider now returns different actions.
|
|
251
|
+
*
|
|
252
|
+
* Resolved actions are snapshots: an action whose availability depends on
|
|
253
|
+
* live state (cast devices found, downloads allowed) hands out a new value
|
|
254
|
+
* when that state changes, and consumers that resolved the old one keep
|
|
255
|
+
* asking it until they re-resolve. Registration alone never tells them.
|
|
256
|
+
*/
|
|
257
|
+
notifyProviderChanged(name: string): void {
|
|
258
|
+
if (!this.registeredActions[name]) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.notifyChanged();
|
|
263
|
+
}
|
|
264
|
+
|
|
249
265
|
/**
|
|
250
266
|
* Register a dynamic action provider under a unique `name`.
|
|
251
267
|
* The provider can return one or more actions and may use the passed `entry`.
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { ContextKeysManager } from "../../..";
|
|
2
|
-
|
|
3
|
-
describe("Context Keys Manager - getKey", () => {
|
|
4
|
-
it("returns value if found by reference", async () => {
|
|
5
|
-
// setup
|
|
6
|
-
const key = "namespace.key";
|
|
7
|
-
const value = "value";
|
|
8
|
-
|
|
9
|
-
const mockedLogger = {
|
|
10
|
-
warn: jest.fn(),
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const mockedSessionStorage = {
|
|
14
|
-
getItem: jest.fn(),
|
|
15
|
-
setItem: jest.fn(),
|
|
16
|
-
removeItem: jest.fn(),
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const mockedLocalStorage = {
|
|
20
|
-
getItem: jest.fn(),
|
|
21
|
-
setItem: jest.fn(),
|
|
22
|
-
removeItem: jest.fn(),
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const mockedSecureStorage = {
|
|
26
|
-
getItem: jest.fn(),
|
|
27
|
-
setItem: jest.fn(),
|
|
28
|
-
removeItem: jest.fn(),
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const contextManager = new ContextKeysManager({
|
|
32
|
-
logger: mockedLogger,
|
|
33
|
-
sessionStorage: mockedSessionStorage,
|
|
34
|
-
localStorage: mockedLocalStorage,
|
|
35
|
-
secureStorage: mockedSecureStorage,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const getKeyByReference = jest
|
|
39
|
-
.spyOn(contextManager, "getKeyByReference")
|
|
40
|
-
.mockResolvedValueOnce(value);
|
|
41
|
-
|
|
42
|
-
// run
|
|
43
|
-
const result = await contextManager.getKey(key);
|
|
44
|
-
|
|
45
|
-
// verify
|
|
46
|
-
expect(result).toEqual(value);
|
|
47
|
-
|
|
48
|
-
expect(mockedLogger.warn).not.toHaveBeenCalled();
|
|
49
|
-
|
|
50
|
-
expect(getKeyByReference).toHaveBeenCalledWith("key", "namespace");
|
|
51
|
-
|
|
52
|
-
expect(mockedSessionStorage.getItem).not.toHaveBeenCalled();
|
|
53
|
-
expect(mockedLocalStorage.getItem).not.toHaveBeenCalled();
|
|
54
|
-
expect(mockedSecureStorage.getItem).not.toHaveBeenCalled();
|
|
55
|
-
});
|
|
56
|
-
});
|