@applicaster/zapp-react-native-utils 13.0.0-alpha.2957047383 → 13.0.0-alpha.3003916059
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 +36 -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/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 +31 -0
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +11 -7
- package/cellUtils/index.ts +9 -5
- package/componentsUtils/__tests__/isFirstComponentGallery.test.ts +33 -0
- package/componentsUtils/__tests__/isFirstComponentScreenPicker.test.ts +33 -0
- package/componentsUtils/__tests__/isGallery.test.ts +33 -0
- package/componentsUtils/__tests__/isScreenPicker.test.ts +33 -0
- package/componentsUtils/index.ts +39 -0
- package/dateUtils/index.ts +17 -0
- package/localizationUtils/index.ts +8 -13
- 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/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +0 -16
- 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/utils/index.ts +1 -1
- package/zappFrameworkUtils/localStorageHelper.ts +11 -3
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isScreenPicker } from "..";
|
|
2
|
+
|
|
3
|
+
describe("isScreenPicker", () => {
|
|
4
|
+
it("should return true if the component type is 'screen-picker-qb-tv'", () => {
|
|
5
|
+
const item = { component_type: "screen-picker-qb-tv" };
|
|
6
|
+
expect(isScreenPicker(item)).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("should return false if the component type is not 'screen-picker-qb-tv'", () => {
|
|
10
|
+
const item = { component_type: "other-component" };
|
|
11
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return false if the component type is undefined", () => {
|
|
15
|
+
const item = { component_type: undefined };
|
|
16
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should return false if the item is null", () => {
|
|
20
|
+
const item = null;
|
|
21
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should return false if the item is undefined", () => {
|
|
25
|
+
const item = undefined;
|
|
26
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should return false if the item is an empty object", () => {
|
|
30
|
+
const item = {};
|
|
31
|
+
expect(isScreenPicker(item)).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const GROUP = "group-qb";
|
|
2
|
+
const GROUP_INFO = "group-info-qb";
|
|
3
|
+
const GROUP_INFO_OLD = "group-info";
|
|
4
|
+
const EMPTY_GROUP_COMPONENT = "empty_group_component";
|
|
5
|
+
|
|
6
|
+
const GALLERY = "gallery-qb";
|
|
7
|
+
|
|
8
|
+
const SCREEN_PICKER = "screen-picker-qb-tv";
|
|
9
|
+
|
|
10
|
+
const HORIZONTAL_LIST = "horizontal_list_qb";
|
|
11
|
+
|
|
12
|
+
export const isGallery = (item): boolean => item?.component_type === GALLERY;
|
|
13
|
+
|
|
14
|
+
export const isHorizontalList = (item): boolean =>
|
|
15
|
+
item?.component_type === HORIZONTAL_LIST;
|
|
16
|
+
|
|
17
|
+
export const isScreenPicker = (item): boolean =>
|
|
18
|
+
item?.component_type === SCREEN_PICKER;
|
|
19
|
+
|
|
20
|
+
export const isFirstComponentScreenPicker = (
|
|
21
|
+
components: ZappUIComponent[]
|
|
22
|
+
): boolean => {
|
|
23
|
+
return isScreenPicker(components?.[0]);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const isFirstComponentGallery = (
|
|
27
|
+
components: ZappUIComponent[]
|
|
28
|
+
): boolean => {
|
|
29
|
+
return isGallery(components?.[0]);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const isGroup = (item): boolean => item?.component_type === GROUP;
|
|
33
|
+
|
|
34
|
+
export const isEmptyGroup = (item): boolean =>
|
|
35
|
+
item?.component_type === EMPTY_GROUP_COMPONENT;
|
|
36
|
+
|
|
37
|
+
export const isGroupInfo = (item): boolean =>
|
|
38
|
+
item?.component_type === GROUP_INFO ||
|
|
39
|
+
item?.component_type === GROUP_INFO_OLD;
|
package/dateUtils/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import utc from "dayjs/plugin/utc";
|
|
|
3
3
|
import timezone from "dayjs/plugin/timezone";
|
|
4
4
|
import isoWeek from "dayjs/plugin/isoWeek";
|
|
5
5
|
import duration from "dayjs/plugin/duration";
|
|
6
|
+
import relativeTime from "dayjs/plugin/relativeTime";
|
|
6
7
|
|
|
7
8
|
// Extend dayjs with plugins
|
|
8
9
|
dayjs.extend(utc);
|
|
@@ -10,4 +11,20 @@ dayjs.extend(timezone);
|
|
|
10
11
|
dayjs.extend(isoWeek);
|
|
11
12
|
dayjs.extend(duration);
|
|
12
13
|
|
|
14
|
+
dayjs.extend(relativeTime, {
|
|
15
|
+
thresholds: [
|
|
16
|
+
{ l: "s", r: 1 },
|
|
17
|
+
{ l: "m", r: 1 },
|
|
18
|
+
{ l: "mm", r: 59, d: "minute" },
|
|
19
|
+
{ l: "h", r: 1 },
|
|
20
|
+
{ l: "hh", r: 23, d: "hour" },
|
|
21
|
+
{ l: "d", r: 1 },
|
|
22
|
+
{ l: "dd", r: 29, d: "day" },
|
|
23
|
+
{ l: "M", r: 1 },
|
|
24
|
+
{ l: "MM", r: 11, d: "month" },
|
|
25
|
+
{ l: "y", r: 1 },
|
|
26
|
+
{ l: "yy", d: "year" },
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
|
|
13
30
|
export { dayjs };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import * as R from "ramda";
|
|
2
1
|
import { ViewStyle } from "react-native";
|
|
3
2
|
|
|
4
3
|
import { useMemo } from "react";
|
|
5
|
-
import { getUILanguage } from "
|
|
6
|
-
import { utilsLogger } from "
|
|
4
|
+
import { getUILanguage } from "@applicaster/zapp-react-native-utils/appUtils/localizationsHelper";
|
|
5
|
+
import { utilsLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
7
6
|
|
|
8
7
|
export {
|
|
9
8
|
getLocale,
|
|
@@ -11,7 +10,7 @@ export {
|
|
|
11
10
|
getCountryCode,
|
|
12
11
|
getLocalizations,
|
|
13
12
|
getUILanguage,
|
|
14
|
-
} from "
|
|
13
|
+
} from "@applicaster/zapp-react-native-utils/appUtils/localizationsHelper";
|
|
15
14
|
|
|
16
15
|
const RTL_LOCALES = [
|
|
17
16
|
"ar", // Arabic
|
|
@@ -29,14 +28,16 @@ const RTL_LOCALES = [
|
|
|
29
28
|
"yi", // Yiddish
|
|
30
29
|
];
|
|
31
30
|
|
|
32
|
-
export function useLocalizedStrings({ localizations }) {
|
|
31
|
+
export function useLocalizedStrings({ localizations = {} }) {
|
|
33
32
|
const languageCode = getUILanguage();
|
|
34
33
|
|
|
35
34
|
// TODO: probably we can call getLocalizations here from localizationsHelper.js
|
|
36
35
|
const result = useMemo(
|
|
37
36
|
() =>
|
|
38
37
|
localizations?.[languageCode] ||
|
|
39
|
-
|
|
38
|
+
Object.values(localizations).find(
|
|
39
|
+
(value) => typeof value === "object" && value !== null
|
|
40
|
+
) ||
|
|
40
41
|
{},
|
|
41
42
|
[languageCode, localizations]
|
|
42
43
|
);
|
|
@@ -53,13 +54,7 @@ export function getIsRTL(): boolean {
|
|
|
53
54
|
return false;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const lowerCaseLanguageCode = languageCode.toLocaleLowerCase();
|
|
59
|
-
|
|
60
|
-
const isRTL = R.contains(lowerCaseLanguageCode, RTL_LOCALES);
|
|
61
|
-
|
|
62
|
-
return isRTL;
|
|
57
|
+
return RTL_LOCALES.includes(uiLanguage.split("-")[0].toLocaleLowerCase());
|
|
63
58
|
}
|
|
64
59
|
|
|
65
60
|
export function useIsRTL(): boolean {
|
|
@@ -251,6 +251,19 @@ const generalContent = () => ({
|
|
|
251
251
|
key: "screen_title",
|
|
252
252
|
initial_value: null,
|
|
253
253
|
},
|
|
254
|
+
{
|
|
255
|
+
type: "text_input",
|
|
256
|
+
label: "Maximum Selection Reached - Alert Description",
|
|
257
|
+
key: "msg_maximum_selection_reached_message",
|
|
258
|
+
initial_value:
|
|
259
|
+
"You’ve reached the maximum number of items you can select.",
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
type: "text_input",
|
|
263
|
+
label: "Maximum Selection Reached - Alert Button",
|
|
264
|
+
key: "msg_maximum_selection_reached_message_ok_button",
|
|
265
|
+
initial_value: "Got it",
|
|
266
|
+
},
|
|
254
267
|
],
|
|
255
268
|
},
|
|
256
269
|
advertising: {
|
|
@@ -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
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "13.0.0-alpha.
|
|
3
|
+
"version": "13.0.0-alpha.3003916059",
|
|
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": "13.0.0-alpha.
|
|
30
|
+
"@applicaster/applicaster-types": "13.0.0-alpha.3003916059",
|
|
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",
|
|
@@ -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
|
+
};
|