@applicaster/zapp-react-native-utils 13.0.0-rc.99 → 14.0.0-rc.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/actionsExecutor/ActionExecutorContext.tsx +55 -6
- package/actionsExecutor/consts.ts +4 -0
- package/appUtils/__tests__/__snapshots__/localizationsHelper.test.ts.snap +151 -0
- package/appUtils/__tests__/allZappLocales.ts +79 -0
- package/appUtils/__tests__/{localizationsHelper.test.js → localizationsHelper.test.ts} +11 -0
- package/appUtils/accessibilityManager/const.ts +18 -0
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -0
- package/appUtils/focusManager/index.ios.ts +14 -4
- package/appUtils/focusManager/utils/__tests__/findChild.test.ts +35 -0
- package/appUtils/focusManager/utils/index.ts +5 -0
- package/appUtils/localizationsHelper.ts +10 -2
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +11 -7
- package/cellUtils/index.ts +9 -5
- package/componentsUtils/index.ts +8 -1
- package/localizationUtils/index.ts +3 -3
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +13 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +0 -8
- package/manifestUtils/index.js +2 -0
- package/manifestUtils/keys.js +27 -2
- package/navigationUtils/__tests__/navigationUtils.test.js +0 -65
- package/navigationUtils/index.ts +0 -31
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +0 -16
- package/playerUtils/index.ts +17 -0
- package/reactHooks/app/useAppState.ts +2 -2
- package/reactHooks/feed/useBatchLoading.ts +10 -12
- package/reactHooks/navigation/{useGetTabBarHeight.ts → getTabBarHeight.ts} +1 -1
- package/reactHooks/navigation/useGetBottomTabBarHeight.ts +10 -3
- package/reactHooks/navigation/useNavigationPluginData.ts +8 -4
- package/reactHooks/navigation/useNavigationType.ts +4 -2
- package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +69 -0
- package/reactHooks/screen/useScreenBackgroundColor.ts +3 -15
- package/reactHooks/state/README.md +79 -0
- package/reactHooks/state/ZStoreProvider.tsx +71 -0
- package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +66 -0
- package/reactHooks/state/index.ts +2 -0
- package/reactHooks/useListenEventBusEvent.ts +1 -1
- package/reactUtils/index.ts +9 -0
- package/typeGuards/index.ts +3 -0
- package/utils/index.ts +1 -1
- package/zappFrameworkUtils/localStorageHelper.ts +32 -10
|
@@ -42,14 +42,6 @@ function getPlayerConfiguration({ platform }) {
|
|
|
42
42
|
label_tooltip:
|
|
43
43
|
"Enable if you want to have adaptive background gradient based on audio background image.",
|
|
44
44
|
},
|
|
45
|
-
{
|
|
46
|
-
key: "audio_player_background_image_query",
|
|
47
|
-
label: "Audio Player Image Query",
|
|
48
|
-
initial_value: "",
|
|
49
|
-
type: "text_input",
|
|
50
|
-
label_tooltip:
|
|
51
|
-
"Set the query params which control the size of the image e.g width, height. This will help to optimize the loading time, when magic background is enabled.",
|
|
52
|
-
},
|
|
53
45
|
{
|
|
54
46
|
key: "audio_player_background_image_default_color",
|
|
55
47
|
label: "Audio Player Background Image Default Color",
|
package/manifestUtils/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
|
|
9
9
|
const { tvActionButtonsContainer } = require("./tvAction/container");
|
|
10
10
|
const { tvActionButton } = require("./tvAction/button");
|
|
11
|
+
const { compact } = require("./_internals");
|
|
11
12
|
|
|
12
13
|
const { spacingKey, absolutePositionElement } = require("./containers");
|
|
13
14
|
|
|
@@ -46,4 +47,5 @@ module.exports = {
|
|
|
46
47
|
tvProgressBar,
|
|
47
48
|
secondaryImage,
|
|
48
49
|
getUpdatedSecondaryImageKeys,
|
|
50
|
+
compact,
|
|
49
51
|
};
|
package/manifestUtils/keys.js
CHANGED
|
@@ -21,7 +21,30 @@ const ZAPPIFEST_FIELDS = {
|
|
|
21
21
|
},
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const
|
|
24
|
+
const TV_PLATFORMS_FOR_FONTS = [
|
|
25
|
+
"tvOS",
|
|
26
|
+
"android",
|
|
27
|
+
"LG",
|
|
28
|
+
"samsung",
|
|
29
|
+
"roku",
|
|
30
|
+
"vizio",
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const MOBILE_PLATFORMS = [
|
|
34
|
+
"ios",
|
|
35
|
+
"ios_for_quickbrick",
|
|
36
|
+
"android",
|
|
37
|
+
"android_for_quickbrick",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const TV_PLATFORMS = [
|
|
41
|
+
"android_tv_for_quickbrick",
|
|
42
|
+
"amazon_fire_tv_for_quickbrick",
|
|
43
|
+
"tvos_for_quickbrick",
|
|
44
|
+
"samsung_tv",
|
|
45
|
+
"lg_tv",
|
|
46
|
+
"vizio",
|
|
47
|
+
];
|
|
25
48
|
|
|
26
49
|
const FontPropsForTVPlatform = (platform) => ({
|
|
27
50
|
[`${R.toLower(platform)}FontFamily`]: {
|
|
@@ -92,7 +115,7 @@ const TV_FONT_KEY_DATA = {
|
|
|
92
115
|
name: "focused font color",
|
|
93
116
|
type: ZAPPIFEST_FIELDS.color_picker,
|
|
94
117
|
},
|
|
95
|
-
...R.reduce(addTVFontsForPlatform, {},
|
|
118
|
+
...R.reduce(addTVFontsForPlatform, {}, TV_PLATFORMS_FOR_FONTS),
|
|
96
119
|
textAlignment: {
|
|
97
120
|
name: "text alignment",
|
|
98
121
|
type: ZAPPIFEST_FIELDS.select,
|
|
@@ -2083,4 +2106,6 @@ module.exports = {
|
|
|
2083
2106
|
TV_CELL_BADGE_FIELDS,
|
|
2084
2107
|
progressBarFields,
|
|
2085
2108
|
secondaryImageFields,
|
|
2109
|
+
TV_PLATFORMS,
|
|
2110
|
+
MOBILE_PLATFORMS,
|
|
2086
2111
|
};
|
|
@@ -319,68 +319,3 @@ describe("getRiverFromRoute", () => {
|
|
|
319
319
|
expect(getRiverFromRoute({ route, rivers })).toEqual(river);
|
|
320
320
|
});
|
|
321
321
|
});
|
|
322
|
-
|
|
323
|
-
describe("isPreviousRouteHook", () => {
|
|
324
|
-
const { isPreviousRouteHook } = navigationUtils;
|
|
325
|
-
|
|
326
|
-
const history = {
|
|
327
|
-
entries: [],
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
it("returns false if it's a root route", () => {
|
|
331
|
-
history.entries.push({ pathname: "/river/root" });
|
|
332
|
-
const currentResult = isPreviousRouteHook(history.entries);
|
|
333
|
-
expect(currentResult).toBe(false);
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
it("returns false if previous root is not a hook", () => {
|
|
337
|
-
history.entries.push({ pathname: "/almostHooks/secondLevel" });
|
|
338
|
-
const currentResult = isPreviousRouteHook(history.entries);
|
|
339
|
-
expect(currentResult).toBe(false);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it("returns true if previous root is a hook", () => {
|
|
343
|
-
history.entries.push({ pathname: "/hooks/ThirdLevel" });
|
|
344
|
-
history.entries.push({ pathname: "/almostHooks/forthLevel" });
|
|
345
|
-
const currentResult = isPreviousRouteHook(history.entries);
|
|
346
|
-
expect(currentResult).toBe(true);
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
describe("getPreviousHooksCount", () => {
|
|
351
|
-
const { getPreviousHooksCount } = navigationUtils;
|
|
352
|
-
|
|
353
|
-
const history = {
|
|
354
|
-
entries: [],
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
it("returns 0 if it's a root route", () => {
|
|
358
|
-
history.entries.push({ pathname: "/river/root" });
|
|
359
|
-
const currentResult = getPreviousHooksCount(history);
|
|
360
|
-
expect(currentResult).toBe(0);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it("returns 0 if previous root is not a hook", () => {
|
|
364
|
-
history.entries.push({ pathname: "/almostHooks/secondLevel" });
|
|
365
|
-
const currentResult = getPreviousHooksCount(history);
|
|
366
|
-
expect(currentResult).toBe(0);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it("returns 1 if previous root is a hook", () => {
|
|
370
|
-
history.entries = [];
|
|
371
|
-
history.entries.push({ pathname: "/hooks/ThirdLevel" });
|
|
372
|
-
history.entries.push({ pathname: "/almostHooks/forthLevel" });
|
|
373
|
-
const currentResult = getPreviousHooksCount(history);
|
|
374
|
-
expect(currentResult).toBe(1);
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it("returns 2 if 2 previous routes are a hooks", () => {
|
|
378
|
-
history.entries = [];
|
|
379
|
-
history.entries.push({ pathname: "/almostHooks/forthLevel" });
|
|
380
|
-
history.entries.push({ pathname: "/hooks/myHook" });
|
|
381
|
-
history.entries.push({ pathname: "/hooks/myHook" });
|
|
382
|
-
history.entries.push({ pathname: "/almostHooks/forthLevel" });
|
|
383
|
-
const currentResult = getPreviousHooksCount(history);
|
|
384
|
-
expect(currentResult).toBe(2);
|
|
385
|
-
});
|
|
386
|
-
});
|
package/navigationUtils/index.ts
CHANGED
|
@@ -363,37 +363,6 @@ export function getRiverFromRoute({
|
|
|
363
363
|
return screenType && screenId ? { screenType, screenId } : null;
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
/**
|
|
367
|
-
* Function returns true if the previous route (in the react-router history) is a hook plugin
|
|
368
|
-
* @param {*} entries - React-Router History Object
|
|
369
|
-
* @return boolean
|
|
370
|
-
*/
|
|
371
|
-
export function isPreviousRouteHook(entries: [{ pathname: string }]): boolean {
|
|
372
|
-
const previousRoute = entries[entries.length - 2];
|
|
373
|
-
if (!previousRoute) return false;
|
|
374
|
-
|
|
375
|
-
return R.test(/\/hooks\/.*/g, previousRoute.pathname);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Function returns number of consecutive hooks that are behind the current route
|
|
380
|
-
* @param {*} history - React-Router History Object
|
|
381
|
-
* @return true
|
|
382
|
-
*/
|
|
383
|
-
export function getPreviousHooksCount(history: {
|
|
384
|
-
entries: [{ pathname: string }];
|
|
385
|
-
}): number {
|
|
386
|
-
let hooksCount = 0;
|
|
387
|
-
let entries = R.clone(history.entries);
|
|
388
|
-
|
|
389
|
-
while (isPreviousRouteHook(entries)) {
|
|
390
|
-
hooksCount++;
|
|
391
|
-
entries = R.init(entries);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return hooksCount;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
366
|
export const usesVideoModal = (
|
|
398
367
|
item: ZappEntry,
|
|
399
368
|
rivers: Record<string, ZappRiver>,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "14.0.0-rc.2",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@applicaster/applicaster-types": "
|
|
30
|
+
"@applicaster/applicaster-types": "14.0.0-rc.2",
|
|
31
31
|
"buffer": "^5.2.1",
|
|
32
32
|
"camelize": "^1.0.0",
|
|
33
33
|
"dayjs": "^1.11.10",
|
|
@@ -39,22 +39,6 @@ function getPlayerConfiguration({ platform }) {
|
|
|
39
39
|
label_tooltip:
|
|
40
40
|
"Disable if you don't want the content to have a right orientation",
|
|
41
41
|
},
|
|
42
|
-
{
|
|
43
|
-
key: "magic_background",
|
|
44
|
-
label: "Enable Magic Background",
|
|
45
|
-
initial_value: false,
|
|
46
|
-
type: "switch",
|
|
47
|
-
label_tooltip:
|
|
48
|
-
"Enable if you want to have adaptive background gradient based on audio background image.",
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
key: "audio_player_background_image_query",
|
|
52
|
-
label: "Audio Player Image Query",
|
|
53
|
-
initial_value: "",
|
|
54
|
-
type: "text_input",
|
|
55
|
-
label_tooltip:
|
|
56
|
-
"Set the query params which control the size of the image e.g width, height. This will help to optimize the loading time, when magic background is enabled.",
|
|
57
|
-
},
|
|
58
42
|
{
|
|
59
43
|
key: "audio_player_background_image_default_color",
|
|
60
44
|
label: "Audio Player Background Image Default Color",
|
package/playerUtils/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
2
|
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
|
|
3
|
+
import { isString } from "@applicaster/zapp-react-native-utils/typeGuards";
|
|
4
|
+
|
|
3
5
|
import { getBoolFromConfigValue } from "../configurationUtils";
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -72,3 +74,18 @@ function isLiveByManager(): boolean {
|
|
|
72
74
|
export function isLive(entry: ZappEntry): boolean {
|
|
73
75
|
return isEntryLive(entry) || isLiveByManager();
|
|
74
76
|
}
|
|
77
|
+
|
|
78
|
+
export const isAudioItem = (item: Option<ZappEntry>) => {
|
|
79
|
+
if (
|
|
80
|
+
isString(item?.content?.type) &&
|
|
81
|
+
item?.content?.type?.includes?.("audio")
|
|
82
|
+
) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (isString(item?.type?.value) && item?.type?.value?.includes?.("audio")) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return false;
|
|
91
|
+
};
|
|
@@ -14,10 +14,10 @@ export function useAppState(returnIsActive) {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
React.useEffect(() => {
|
|
17
|
-
AppState.addEventListener("change", setAppState);
|
|
17
|
+
const subscription = AppState.addEventListener("change", setAppState);
|
|
18
18
|
|
|
19
19
|
return () => {
|
|
20
|
-
|
|
20
|
+
subscription.remove();
|
|
21
21
|
};
|
|
22
22
|
}, []);
|
|
23
23
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { complement, compose, isNil, map, min, prop, take, uniq } from "ramda";
|
|
2
2
|
import { useDispatch } from "react-redux";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks
|
|
4
|
+
import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks";
|
|
5
5
|
import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
|
|
6
6
|
import { isNilOrEmpty } from "../../reactUtils/helpers";
|
|
7
7
|
import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getInflatedDataSourceUrl,
|
|
10
10
|
getSearchContext,
|
|
11
11
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
12
|
+
import { isGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
|
|
12
13
|
import { useScreenContext } from "../screen/useScreenContext";
|
|
13
14
|
|
|
14
15
|
type Options = {
|
|
@@ -46,9 +47,7 @@ const filterEmptyData = (data) => {
|
|
|
46
47
|
};
|
|
47
48
|
|
|
48
49
|
const getData = (rawData) =>
|
|
49
|
-
rawData.
|
|
50
|
-
? rawData.ui_components[0].data
|
|
51
|
-
: rawData.data;
|
|
50
|
+
isGallery(rawData) ? rawData.ui_components[0].data : rawData.data;
|
|
52
51
|
|
|
53
52
|
const extractData = compose(uniq, map(getData));
|
|
54
53
|
|
|
@@ -70,13 +69,12 @@ export const useBatchLoading = (
|
|
|
70
69
|
const [hasEverBeenReady, setHasEverBeenReady] = React.useState(false);
|
|
71
70
|
|
|
72
71
|
// if first component is gallery-qb, take only one component for initial load
|
|
73
|
-
const takeSize =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
);
|
|
72
|
+
const takeSize = isGallery(componentsToRender?.[0])
|
|
73
|
+
? 1
|
|
74
|
+
: min(
|
|
75
|
+
options.initialBatchSize ?? DEFAULT_BATCH_SIZE,
|
|
76
|
+
componentsToRender.length
|
|
77
|
+
);
|
|
80
78
|
|
|
81
79
|
const takeBatchSize = React.useCallback(take(takeSize), [takeSize]);
|
|
82
80
|
|
|
@@ -3,7 +3,7 @@ import { platformSelect } from "../../reactUtils";
|
|
|
3
3
|
const TAB_BAR_HEIGHT_IOS = 49;
|
|
4
4
|
const TAB_BAR_HEIGHT_ANDROID = 56;
|
|
5
5
|
|
|
6
|
-
export const
|
|
6
|
+
export const getTabBarHeight = () =>
|
|
7
7
|
platformSelect({
|
|
8
8
|
ios: TAB_BAR_HEIGHT_IOS,
|
|
9
9
|
android: TAB_BAR_HEIGHT_ANDROID,
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { useGetNavBarTopBorderWidth } from "./useGetNavBarTopBorderWidth";
|
|
2
|
-
import {
|
|
2
|
+
import { getTabBarHeight } from "./getTabBarHeight";
|
|
3
|
+
import { useNavigation } from "./useNavigation";
|
|
3
4
|
import { MenuTypes, useNavigationType } from "./useNavigationType";
|
|
5
|
+
import { useNavigationPluginData } from "./useNavigationPluginData";
|
|
4
6
|
|
|
5
7
|
export const useGetBottomTabBarHeight = (): number => {
|
|
8
|
+
const { activeRiver } = useNavigation();
|
|
9
|
+
|
|
10
|
+
const navigationPluginData = useNavigationPluginData(activeRiver);
|
|
11
|
+
const navigationType = useNavigationType(navigationPluginData);
|
|
12
|
+
|
|
6
13
|
const topBorderWidth = useGetNavBarTopBorderWidth();
|
|
7
|
-
const tabBarHeight =
|
|
14
|
+
const tabBarHeight = getTabBarHeight();
|
|
8
15
|
|
|
9
|
-
const isBottomBarNavigation =
|
|
16
|
+
const isBottomBarNavigation = navigationType === MenuTypes.bottomTabBar;
|
|
10
17
|
|
|
11
18
|
return !isBottomBarNavigation ? 0 : tabBarHeight + topBorderWidth;
|
|
12
19
|
};
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { useRoute } from "./useRoute";
|
|
2
2
|
|
|
3
|
-
export const useNavigationPluginData = (
|
|
3
|
+
export const useNavigationPluginData = (
|
|
4
|
+
screenData?: LegacyNavigationScreenData | null
|
|
5
|
+
): ZappNavigation | undefined => {
|
|
4
6
|
const {
|
|
5
7
|
screenData: useRouteScreenData,
|
|
6
8
|
}: { screenData: QuickBrickNavigationData | null } = useRoute();
|
|
7
9
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
const activeScreenData = screenData ?? useRouteScreenData;
|
|
11
|
+
|
|
12
|
+
const navigations = activeScreenData?.targetScreen
|
|
13
|
+
? (activeScreenData.targetScreen as ZappRiver).navigations
|
|
14
|
+
: (activeScreenData as ZappRiver).navigations;
|
|
11
15
|
|
|
12
16
|
const navigationMenu = navigations?.find((nav) => nav.category === "menu");
|
|
13
17
|
|
|
@@ -5,8 +5,10 @@ export enum MenuTypes {
|
|
|
5
5
|
bottomTabBar = "BOTTOM_TAB_BAR",
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export const useNavigationType = (): MenuTypes => {
|
|
9
|
-
const
|
|
8
|
+
export const useNavigationType = (navigation?: ZappNavigation): MenuTypes => {
|
|
9
|
+
const navigationPluginData = useNavigationPluginData();
|
|
10
|
+
|
|
11
|
+
const navigationMenu = navigation ?? navigationPluginData;
|
|
10
12
|
|
|
11
13
|
return !navigationMenu ||
|
|
12
14
|
navigationMenu.navigation_type === "quick_brick_side_menu"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react-hooks";
|
|
2
|
+
|
|
3
|
+
jest.mock(
|
|
4
|
+
"@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration",
|
|
5
|
+
() => ({
|
|
6
|
+
useScreenConfiguration: jest.fn(),
|
|
7
|
+
})
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
useScreenConfiguration,
|
|
12
|
+
} = require("@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration");
|
|
13
|
+
|
|
14
|
+
const { useScreenBackgroundColor } = require("../useScreenBackgroundColor");
|
|
15
|
+
|
|
16
|
+
describe("useScreenBackgroundColor", () => {
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
jest.clearAllMocks();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should return the background color from screen configuration", () => {
|
|
22
|
+
useScreenConfiguration.mockReturnValue({
|
|
23
|
+
backgroundColor: "#FF0000",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Render the hook with a screen ID
|
|
27
|
+
const { result } = renderHook(() =>
|
|
28
|
+
useScreenBackgroundColor("test-screen-id")
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
expect(result.current).toBe("#FF0000");
|
|
32
|
+
|
|
33
|
+
expect(useScreenConfiguration).toHaveBeenCalledWith("test-screen-id");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should return 'transparent' when background color is empty", () => {
|
|
37
|
+
useScreenConfiguration.mockReturnValue({
|
|
38
|
+
backgroundColor: "",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const { result } = renderHook(() =>
|
|
42
|
+
useScreenBackgroundColor("test-screen-id")
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(result.current).toBe("transparent");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should return 'transparent' when background color is null", () => {
|
|
49
|
+
useScreenConfiguration.mockReturnValue({
|
|
50
|
+
backgroundColor: null,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const { result } = renderHook(() =>
|
|
54
|
+
useScreenBackgroundColor("test-screen-id")
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(result.current).toBe("transparent");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should return 'transparent' when background color is undefined", () => {
|
|
61
|
+
useScreenConfiguration.mockReturnValue({});
|
|
62
|
+
|
|
63
|
+
const { result } = renderHook(() =>
|
|
64
|
+
useScreenBackgroundColor("test-screen-id")
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(result.current).toBe("transparent");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -1,23 +1,11 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
3
1
|
import { useScreenConfiguration } from "@applicaster/zapp-react-native-ui-components/Components/River/useScreenConfiguration";
|
|
4
2
|
import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
5
3
|
|
|
4
|
+
const DEFAULT_BACKGROUND_FALLBACK = "transparent";
|
|
5
|
+
|
|
6
6
|
export const useScreenBackgroundColor = (screenId: string): string => {
|
|
7
7
|
const { backgroundColor: screenBackgroundColor } =
|
|
8
8
|
useScreenConfiguration(screenId);
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const themeBackgroundColor = React.useMemo(
|
|
13
|
-
() => theme.app_background_color,
|
|
14
|
-
[theme.app_background_color]
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
const backgroundColor = ifEmptyUseFallback(
|
|
18
|
-
screenBackgroundColor,
|
|
19
|
-
themeBackgroundColor
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
return backgroundColor;
|
|
10
|
+
return ifEmptyUseFallback(screenBackgroundColor, DEFAULT_BACKGROUND_FALLBACK);
|
|
23
11
|
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# ZStoreProvider and useZStore
|
|
2
|
+
|
|
3
|
+
This module provides a React context-based solution for managing Zustand stores with named access.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### ZStoreProvider
|
|
8
|
+
|
|
9
|
+
The `ZStoreProvider` component creates a Zustand store from the provided value and makes it available to child components.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { ZStoreProvider } from "@applicaster/zapp-react-native-utils/reactHooks/state";
|
|
13
|
+
|
|
14
|
+
// In your component
|
|
15
|
+
<ZStoreProvider name="playerConfiguration" value={controller?.config}>
|
|
16
|
+
<YourComponent />
|
|
17
|
+
</ZStoreProvider>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### useZStore
|
|
21
|
+
|
|
22
|
+
The `useZStore` hook allows you to access a Zustand store by name from within a `ZStoreProvider`.
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { useZStore } from "@applicaster/zapp-react-native-utils/reactHooks/state";
|
|
26
|
+
import { useStore } from "zustand";
|
|
27
|
+
|
|
28
|
+
// In your component
|
|
29
|
+
const MyComponent = () => {
|
|
30
|
+
const store = useZStore("playerConfiguration");
|
|
31
|
+
const config = useStore(store, (state) => state.someProperty);
|
|
32
|
+
|
|
33
|
+
return <Text>{config}</Text>;
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Example
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import React from "react";
|
|
41
|
+
import { ZStoreProvider, useZStore } from "@applicaster/zapp-react-native-utils/reactHooks/state";
|
|
42
|
+
import { useStore } from "zustand";
|
|
43
|
+
|
|
44
|
+
// Component that uses the store
|
|
45
|
+
const PlayerConfigDisplay = () => {
|
|
46
|
+
const store = useZStore("playerConfiguration");
|
|
47
|
+
const config = useStore(store, (state) => state);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<View>
|
|
51
|
+
<Text>Player Config: {JSON.stringify(config)}</Text>
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Main component that provides the store
|
|
57
|
+
const PlayerComponent = ({ controller }) => {
|
|
58
|
+
return (
|
|
59
|
+
<ZStoreProvider name="playerConfiguration" value={controller?.config}>
|
|
60
|
+
<PlayerConfigDisplay />
|
|
61
|
+
</ZStoreProvider>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Features
|
|
67
|
+
|
|
68
|
+
- **Named Stores**: Access stores by name instead of importing them directly
|
|
69
|
+
- **Context-based**: Uses React Context for store management
|
|
70
|
+
- **Zustand Integration**: Seamlessly works with existing Zustand stores
|
|
71
|
+
- **Type Safety**: Full TypeScript support
|
|
72
|
+
- **Error Handling**: Clear error messages when stores are not found or used outside providers
|
|
73
|
+
|
|
74
|
+
## Error Handling
|
|
75
|
+
|
|
76
|
+
The `useZStore` hook will throw errors in the following cases:
|
|
77
|
+
|
|
78
|
+
1. **Used outside provider**: "useZStore must be used within a ZStoreProvider"
|
|
79
|
+
2. **Store not found**: "Store with name 'storeName' not found. Make sure it's provided by a ZStoreProvider"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { createContext, useContext, ReactNode, useMemo } from "react";
|
|
2
|
+
import { create, UseBoundStore } from "zustand";
|
|
3
|
+
|
|
4
|
+
interface ZStoreContextType {
|
|
5
|
+
stores: Map<string, UseBoundStore<any>>;
|
|
6
|
+
registerStore: (name: string, store: UseBoundStore<any>) => void;
|
|
7
|
+
getStore: (name: string) => UseBoundStore<any> | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ZStoreContext = createContext<ZStoreContextType | null>(null);
|
|
11
|
+
|
|
12
|
+
interface ZStoreProviderProps {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
name: string;
|
|
15
|
+
value: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ZStoreProvider: React.FC<ZStoreProviderProps> = ({
|
|
19
|
+
children,
|
|
20
|
+
name,
|
|
21
|
+
value,
|
|
22
|
+
}) => {
|
|
23
|
+
const parentContext = useContext(ZStoreContext);
|
|
24
|
+
|
|
25
|
+
const context = useMemo(() => {
|
|
26
|
+
if (parentContext) {
|
|
27
|
+
// If parent context exists, create a new store and register it
|
|
28
|
+
const store = create(() => value);
|
|
29
|
+
parentContext.registerStore(name, store);
|
|
30
|
+
|
|
31
|
+
return parentContext;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Create a new context if none exists
|
|
35
|
+
const stores = new Map<string, UseBoundStore<any>>();
|
|
36
|
+
|
|
37
|
+
// Create a store from the provided value
|
|
38
|
+
const store = create(() => value);
|
|
39
|
+
stores.set(name, store);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
stores,
|
|
43
|
+
registerStore: (storeName: string, storeInstance: UseBoundStore<any>) => {
|
|
44
|
+
stores.set(storeName, storeInstance);
|
|
45
|
+
},
|
|
46
|
+
getStore: (storeName: string) => stores.get(storeName),
|
|
47
|
+
};
|
|
48
|
+
}, [parentContext, name, value]);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<ZStoreContext.Provider value={context}>{children}</ZStoreContext.Provider>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const useZStore = (name: string): UseBoundStore<any> => {
|
|
56
|
+
const context = useContext(ZStoreContext);
|
|
57
|
+
|
|
58
|
+
if (!context) {
|
|
59
|
+
throw new Error("useZStore must be used within a ZStoreProvider");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const store = context.getStore(name);
|
|
63
|
+
|
|
64
|
+
if (!store) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Store with name "${name}" not found. Make sure it's provided by a ZStoreProvider.`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return store;
|
|
71
|
+
};
|