@applicaster/zapp-react-native-utils 14.0.0-alpha.2332850672 → 14.0.0-alpha.2373044050

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.
Files changed (130) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +60 -84
  2. package/actionsExecutor/ScreenActions.ts +164 -0
  3. package/actionsExecutor/StorageActions.ts +110 -0
  4. package/actionsExecutor/feedDecorator.ts +171 -0
  5. package/actionsExecutor/screenResolver.ts +11 -0
  6. package/analyticsUtils/AnalyticsEvents/helper.ts +81 -0
  7. package/analyticsUtils/AnalyticsEvents/sendHeaderClickEvent.ts +1 -1
  8. package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -1
  9. package/analyticsUtils/AnalyticsEvents/sendOnClickEvent.ts +14 -4
  10. package/analyticsUtils/__tests__/analyticsUtils.test.js +3 -0
  11. package/analyticsUtils/events.ts +8 -0
  12. package/analyticsUtils/index.tsx +3 -4
  13. package/analyticsUtils/manager.ts +1 -1
  14. package/analyticsUtils/playerAnalyticsTracker.ts +2 -1
  15. package/appUtils/HooksManager/Hook.ts +4 -4
  16. package/appUtils/HooksManager/index.ts +11 -1
  17. package/appUtils/accessibilityManager/const.ts +13 -0
  18. package/appUtils/accessibilityManager/hooks.ts +35 -1
  19. package/appUtils/accessibilityManager/index.ts +154 -30
  20. package/appUtils/accessibilityManager/utils.ts +24 -0
  21. package/appUtils/contextKeysManager/contextResolver.ts +42 -1
  22. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +7 -0
  23. package/appUtils/focusManager/__tests__/focusManager.test.js +1 -1
  24. package/appUtils/focusManager/events.ts +2 -0
  25. package/appUtils/focusManager/index.ios.ts +10 -0
  26. package/appUtils/focusManager/index.ts +86 -11
  27. package/appUtils/focusManager/treeDataStructure/Tree/index.js +1 -1
  28. package/appUtils/focusManagerAux/utils/index.ts +94 -3
  29. package/appUtils/platform/platformUtils.ts +31 -1
  30. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +0 -15
  31. package/appUtils/playerManager/useChapterMarker.tsx +0 -1
  32. package/appUtils/playerManager/usePlayerControllerSetup.tsx +16 -0
  33. package/arrayUtils/__tests__/isEmptyArray.test.ts +63 -0
  34. package/arrayUtils/__tests__/isFilledArray.test.ts +1 -1
  35. package/arrayUtils/index.ts +8 -3
  36. package/audioPlayerUtils/__tests__/getArtworkImage.test.ts +144 -0
  37. package/audioPlayerUtils/__tests__/getImageFromEntry.test.ts +54 -0
  38. package/audioPlayerUtils/index.ts +168 -29
  39. package/componentsUtils/__tests__/isTabsScreen.test.ts +38 -0
  40. package/componentsUtils/index.ts +4 -1
  41. package/conf/player/__tests__/selectors.test.ts +34 -0
  42. package/conf/player/selectors.ts +10 -0
  43. package/configurationUtils/__tests__/configurationUtils.test.js +0 -31
  44. package/configurationUtils/__tests__/getMediaItems.test.ts +65 -0
  45. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +34 -0
  46. package/configurationUtils/__tests__/manifestKeyParser.test.ts +546 -0
  47. package/configurationUtils/index.ts +64 -35
  48. package/configurationUtils/manifestKeyParser.ts +57 -32
  49. package/focusManager/FocusManager.ts +104 -20
  50. package/focusManager/Tree.ts +25 -21
  51. package/focusManager/__tests__/FocusManager.test.ts +50 -8
  52. package/focusManager/aux/index.ts +98 -0
  53. package/focusManager/utils.ts +12 -6
  54. package/index.d.ts +1 -10
  55. package/manifestUtils/_internals/getDefaultConfiguration.js +28 -0
  56. package/manifestUtils/{_internals.js → _internals/index.js} +2 -25
  57. package/manifestUtils/createConfig.js +4 -1
  58. package/manifestUtils/defaultManifestConfigurations/player.js +2764 -1539
  59. package/manifestUtils/index.js +4 -0
  60. package/manifestUtils/keys.js +12 -0
  61. package/manifestUtils/progressBar/__tests__/mobileProgressBar.test.js +0 -30
  62. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +6 -0
  63. package/navigationUtils/__tests__/mapContentTypesToRivers.test.ts +130 -0
  64. package/navigationUtils/index.ts +7 -5
  65. package/package.json +2 -3
  66. package/playerUtils/PlayerTTS/PlayerTTS.ts +359 -0
  67. package/playerUtils/PlayerTTS/index.ts +1 -0
  68. package/playerUtils/__tests__/configurationUtils.test.ts +1 -65
  69. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +54 -0
  70. package/playerUtils/_internals/__tests__/utils.test.ts +71 -0
  71. package/playerUtils/_internals/index.ts +1 -0
  72. package/playerUtils/_internals/utils.ts +31 -0
  73. package/playerUtils/configurationUtils.ts +0 -44
  74. package/playerUtils/getPlayerActionButtons.ts +17 -0
  75. package/playerUtils/index.ts +53 -0
  76. package/playerUtils/usePlayerTTS.ts +21 -0
  77. package/playerUtils/useValidatePlayerConfig.tsx +22 -19
  78. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +15 -14
  79. package/reactHooks/cell-click/__tests__/index.test.js +3 -0
  80. package/reactHooks/cell-click/index.ts +8 -1
  81. package/reactHooks/debugging/__tests__/index.test.js +0 -1
  82. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +47 -90
  83. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +71 -31
  84. package/reactHooks/feed/index.ts +2 -0
  85. package/reactHooks/feed/useBatchLoading.ts +17 -10
  86. package/reactHooks/feed/useFeedLoader.tsx +36 -34
  87. package/reactHooks/feed/useLoadPipesDataDispatch.ts +63 -0
  88. package/reactHooks/feed/usePipesCacheReset.ts +3 -3
  89. package/reactHooks/flatList/useSequentialRenderItem.tsx +3 -3
  90. package/reactHooks/layout/__tests__/index.test.tsx +3 -1
  91. package/reactHooks/layout/isTablet/index.ts +12 -5
  92. package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +34 -36
  93. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -3
  94. package/reactHooks/layout/useLayoutVersion.ts +5 -5
  95. package/reactHooks/navigation/index.ts +7 -5
  96. package/reactHooks/navigation/useIsScreenActive.ts +9 -5
  97. package/reactHooks/navigation/useRoute.ts +7 -2
  98. package/reactHooks/navigation/useScreenStateStore.ts +8 -0
  99. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +4 -0
  100. package/reactHooks/screen/useScreenContext.ts +1 -1
  101. package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +2 -1
  102. package/reactHooks/state/index.ts +1 -1
  103. package/reactHooks/state/useHomeRiver.ts +4 -2
  104. package/reactHooks/state/useRivers.ts +7 -8
  105. package/riverComponetsMeasurementProvider/index.tsx +1 -1
  106. package/screenPickerUtils/index.ts +13 -0
  107. package/services/js2native.ts +1 -0
  108. package/storage/ScreenSingleValueProvider.ts +204 -0
  109. package/storage/ScreenStateMultiSelectProvider.ts +293 -0
  110. package/storage/StorageMultiSelectProvider.ts +192 -0
  111. package/storage/StorageSingleSelectProvider.ts +108 -0
  112. package/testUtils/index.tsx +7 -8
  113. package/time/BackgroundTimer.ts +6 -4
  114. package/utils/__tests__/endsWith.test.ts +30 -0
  115. package/utils/__tests__/find.test.ts +36 -0
  116. package/utils/__tests__/omit.test.ts +19 -0
  117. package/utils/__tests__/path.test.ts +33 -0
  118. package/utils/__tests__/pathOr.test.ts +37 -0
  119. package/utils/__tests__/startsWith.test.ts +30 -0
  120. package/utils/__tests__/take.test.ts +40 -0
  121. package/utils/endsWith.ts +9 -0
  122. package/utils/find.ts +3 -0
  123. package/utils/index.ts +24 -1
  124. package/utils/omit.ts +5 -0
  125. package/utils/path.ts +5 -0
  126. package/utils/pathOr.ts +5 -0
  127. package/utils/startsWith.ts +9 -0
  128. package/utils/take.ts +5 -0
  129. package/playerUtils/configurationGenerator.ts +0 -2572
  130. package/utils/isPresent.ts +0 -4
@@ -0,0 +1,144 @@
1
+ import { getArtworkImage } from "..";
2
+ import { DEFAULT_IMAGE } from "../assets";
3
+
4
+ describe("getArtworkImage", () => {
5
+ const entryWithImage = {
6
+ media_group: [
7
+ {
8
+ type: "image",
9
+ media_item: [
10
+ { key: "artwork_key", src: "image_from_entry" },
11
+ { key: "other_key", src: "other_image" },
12
+ ],
13
+ },
14
+ ],
15
+ extensions: {
16
+ artwork_key: "artwork_key",
17
+ },
18
+ };
19
+
20
+ const entryWithoutImage = {
21
+ media_group: [
22
+ {
23
+ type: "image",
24
+ media_item: [{ key: "other_key", src: "other_image" }],
25
+ },
26
+ ],
27
+ extensions: {
28
+ artwork_key: "artwork_key",
29
+ },
30
+ };
31
+
32
+ const pluginConfigWithImage = {
33
+ artwork_key: "plugin_artwork_key",
34
+ };
35
+
36
+ it("returns image from entry extensions key", () => {
37
+ const result = getArtworkImage({
38
+ key: "artwork_key",
39
+ entry: entryWithImage,
40
+ plugin_configuration: {},
41
+ });
42
+
43
+ expect(result).toBe("image_from_entry");
44
+ });
45
+
46
+ it("returns image from plugin configuration key if not in entry", () => {
47
+ const entryNoMatch = {
48
+ ...entryWithoutImage,
49
+ extensions: { artwork_key: "not_found_key" },
50
+ };
51
+
52
+ const pluginConfig = {
53
+ artwork_key: "plugin_artwork_key",
54
+ };
55
+
56
+ const entryWithPluginImage = {
57
+ ...entryNoMatch,
58
+ media_group: [
59
+ {
60
+ type: "image",
61
+ media_item: [{ key: "plugin_artwork_key", src: "image_from_plugin" }],
62
+ },
63
+ ],
64
+ };
65
+
66
+ const result = getArtworkImage({
67
+ key: "artwork_key",
68
+ entry: entryWithPluginImage,
69
+ plugin_configuration: pluginConfig,
70
+ });
71
+
72
+ expect(result).toBe("image_from_plugin");
73
+ });
74
+
75
+ it("returns DEFAULT_IMAGE if neither entry nor plugin configuration has image", () => {
76
+ const entryNoImage = {
77
+ media_group: [
78
+ {
79
+ type: "image",
80
+ media_item: [{ key: "other_key", src: "other_image" }],
81
+ },
82
+ ],
83
+ extensions: { artwork_key: "not_found_key" },
84
+ };
85
+
86
+ const pluginConfig = { artwork_key: "not_found_key" };
87
+
88
+ const result = getArtworkImage({
89
+ key: "artwork_key",
90
+ entry: entryNoImage,
91
+ plugin_configuration: pluginConfig,
92
+ });
93
+
94
+ expect(result).toBe(DEFAULT_IMAGE);
95
+ });
96
+
97
+ it("handles undefined key gracefully", () => {
98
+ const result = getArtworkImage({
99
+ key: undefined,
100
+ entry: entryWithImage,
101
+ plugin_configuration: pluginConfigWithImage,
102
+ });
103
+
104
+ expect(result).toBe(DEFAULT_IMAGE);
105
+ });
106
+
107
+ it("handles missing entry or plugin_configuration gracefully", () => {
108
+ const result = getArtworkImage({
109
+ key: "artwork_key",
110
+ entry: undefined,
111
+ plugin_configuration: undefined,
112
+ });
113
+
114
+ expect(result).toBe(DEFAULT_IMAGE);
115
+ });
116
+
117
+ it("handles empty media_group", () => {
118
+ const entryEmptyMedia = {
119
+ media_group: [],
120
+ extensions: { artwork_key: "artwork_key" },
121
+ };
122
+
123
+ const result = getArtworkImage({
124
+ key: "artwork_key",
125
+ entry: entryEmptyMedia,
126
+ plugin_configuration: pluginConfigWithImage,
127
+ });
128
+
129
+ expect(result).toBe(DEFAULT_IMAGE);
130
+ });
131
+
132
+ it("handles missing extensions/config keys", () => {
133
+ const entryNoExt = { media_group: [], extensions: {} };
134
+ const pluginConfig = {};
135
+
136
+ const result = getArtworkImage({
137
+ key: "artwork_key",
138
+ entry: entryNoExt,
139
+ plugin_configuration: pluginConfig,
140
+ });
141
+
142
+ expect(result).toBe(DEFAULT_IMAGE);
143
+ });
144
+ });
@@ -53,4 +53,58 @@ describe("getImageFromEntry", () => {
53
53
 
54
54
  expect(result).toBeUndefined();
55
55
  });
56
+
57
+ it("returns undefined for non string src", () => {
58
+ const entryWithNonStringSrc = {
59
+ media_group: [
60
+ {
61
+ media_item: [
62
+ {
63
+ key: "image_base_key",
64
+ src: 123,
65
+ },
66
+ {
67
+ key: "thumb_1",
68
+ src: null,
69
+ },
70
+ ],
71
+ type: "image",
72
+ },
73
+ ],
74
+ };
75
+
76
+ const result = getImageFromEntry({
77
+ entry: entryWithNonStringSrc,
78
+ imageKey: "image_base_key",
79
+ });
80
+
81
+ expect(result).toBeUndefined();
82
+ });
83
+
84
+ it("returns undefined for empty src", () => {
85
+ const entryWithEmptySrc = {
86
+ media_group: [
87
+ {
88
+ media_item: [
89
+ {
90
+ key: "image_base_key",
91
+ src: "",
92
+ },
93
+ {
94
+ key: "thumb_1",
95
+ src: null,
96
+ },
97
+ ],
98
+ type: "image",
99
+ },
100
+ ],
101
+ };
102
+
103
+ const result = getImageFromEntry({
104
+ entry: entryWithEmptySrc,
105
+ imageKey: "image_base_key",
106
+ });
107
+
108
+ expect(result).toBeUndefined();
109
+ });
56
110
  });
@@ -1,4 +1,10 @@
1
- import { isEmpty, isPresent } from "@applicaster/zapp-react-native-utils/utils";
1
+ import * as React from "react";
2
+ import { get, has } from "@applicaster/zapp-react-native-utils/utils";
3
+ import { useZStore } from "@applicaster/zapp-react-native-utils/reactHooks";
4
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
5
+
6
+ import { isNotEmptyString } from "@applicaster/zapp-react-native-utils/stringUtils";
7
+ import { getMediaItems } from "@applicaster/zapp-react-native-utils/configurationUtils";
2
8
 
3
9
  import { DEFAULT_IMAGE } from "./assets";
4
10
 
@@ -9,20 +15,9 @@ export function getImageFromEntry({
9
15
  entry: ZappEntry;
10
16
  imageKey: Option<string>;
11
17
  }): Option<string> {
12
- const mediaGroup = entry?.media_group;
18
+ const mediaItems = getMediaItems(entry);
13
19
 
14
- if (!Array.isArray(mediaGroup)) {
15
- return undefined;
16
- }
17
-
18
- // Find all media_items from groups of type 'image'
19
- const mediaItems = mediaGroup
20
- .filter((group) => group.type === "image")
21
- .flatMap((group) =>
22
- Array.isArray(group.media_item) ? group.media_item : [group.media_item]
23
- );
24
-
25
- if (!Array.isArray(mediaItems) || mediaItems.length === 0) {
20
+ if (!mediaItems) {
26
21
  return undefined;
27
22
  }
28
23
 
@@ -31,15 +26,28 @@ export function getImageFromEntry({
31
26
  const src = found?.src;
32
27
 
33
28
  // Special case for react native - uri cannot be an empty string (yellow warning).
34
- return isEmpty(src) ? undefined : src;
29
+ return isNotEmptyString(src) ? src : undefined;
35
30
  }
36
31
 
37
- const getPropertyFromExtensions = (key, entry) => entry?.extensions?.[key];
32
+ const getPropertyFromExtensions = (
33
+ key: string,
34
+ entry: ZappEntry
35
+ ): Option<string> => entry?.extensions?.[key];
36
+
37
+ const getPropertyFromConfiguration = (
38
+ key: string,
39
+ plugin_configuration: Record<string, any>
40
+ ) => plugin_configuration?.[key];
38
41
 
39
- const getPropertyFromConfiguration = (key, plugin_configuration) =>
40
- plugin_configuration?.[key];
42
+ type GetBackgroundImageParams = {
43
+ entry: ZappEntry;
44
+ plugin_configuration: Record<string, any>;
45
+ };
41
46
 
42
- export const getBackgroundImage = ({ entry, plugin_configuration }): string => {
47
+ export const getBackgroundImage = ({
48
+ entry,
49
+ plugin_configuration,
50
+ }: GetBackgroundImageParams): string => {
43
51
  // 1) image_key from extensions
44
52
  const imageKeyFromExtensions = getPropertyFromExtensions("image_key", entry);
45
53
 
@@ -48,7 +56,7 @@ export const getBackgroundImage = ({ entry, plugin_configuration }): string => {
48
56
  imageKey: imageKeyFromExtensions,
49
57
  });
50
58
 
51
- if (isPresent(imageFromExtensions)) {
59
+ if (imageFromExtensions) {
52
60
  return imageFromExtensions;
53
61
  }
54
62
 
@@ -58,7 +66,7 @@ export const getBackgroundImage = ({ entry, plugin_configuration }): string => {
58
66
  entry
59
67
  );
60
68
 
61
- if (isPresent(audioPlayerBackgroundImageFromExtensions)) {
69
+ if (isNotEmptyString(audioPlayerBackgroundImageFromExtensions)) {
62
70
  return audioPlayerBackgroundImageFromExtensions;
63
71
  }
64
72
 
@@ -73,7 +81,7 @@ export const getBackgroundImage = ({ entry, plugin_configuration }): string => {
73
81
  imageKey: imageKeyFromConfiguration,
74
82
  });
75
83
 
76
- if (isPresent(imageFromConfiguration)) {
84
+ if (imageFromConfiguration) {
77
85
  return imageFromConfiguration;
78
86
  }
79
87
 
@@ -84,7 +92,7 @@ export const getBackgroundImage = ({ entry, plugin_configuration }): string => {
84
92
  plugin_configuration
85
93
  );
86
94
 
87
- if (isPresent(audioPlayerBackgroundImageFromConfiguration)) {
95
+ if (isNotEmptyString(audioPlayerBackgroundImageFromConfiguration)) {
88
96
  return audioPlayerBackgroundImageFromConfiguration;
89
97
  }
90
98
 
@@ -92,12 +100,143 @@ export const getBackgroundImage = ({ entry, plugin_configuration }): string => {
92
100
  return DEFAULT_IMAGE;
93
101
  };
94
102
 
95
- // getConfiguration(key, entry, zappConfig) {
96
- // return entry.extensions[key] || zappConfig[key]
97
- // }
103
+ type GetArtworkImageParams = {
104
+ key: string;
105
+ entry: ZappEntry;
106
+ plugin_configuration: Record<string, any>;
107
+ };
108
+
109
+ export const getArtworkImage = ({
110
+ key,
111
+ entry,
112
+ plugin_configuration,
113
+ }: GetArtworkImageParams): string => {
114
+ // 1) image_key from extensions
115
+ const imageKeyFromExtensions = getPropertyFromExtensions(key, entry);
116
+
117
+ const imageFromExtensions = getImageFromEntry({
118
+ entry,
119
+ imageKey: imageKeyFromExtensions,
120
+ });
121
+
122
+ if (imageFromExtensions) {
123
+ return imageFromExtensions;
124
+ }
125
+
126
+ // 2) image_key from configuration
127
+ const imageKeyFromConfiguration = getPropertyFromConfiguration(
128
+ key,
129
+ plugin_configuration
130
+ );
98
131
 
99
- // const imageKey = getConfiguration("image_key", entry, zappConfig);
132
+ const imageFromConfiguration = getImageFromEntry({
133
+ entry,
134
+ imageKey: imageKeyFromConfiguration,
135
+ });
100
136
 
101
- // const audioPlayerBackgroundImage = getConfiguration("audio_player_background_image", entry, zappConfig)
137
+ if (imageFromConfiguration) {
138
+ return imageFromConfiguration;
139
+ }
102
140
 
103
- // const imageSrc = getIimageFromEntry(imageKey) || audio_player_background_image
141
+ // default image
142
+ return DEFAULT_IMAGE;
143
+ };
144
+
145
+ const useAdjustedKeyFromScreenData = ({ config, keys }) => {
146
+ const { screenData } = useRoute();
147
+
148
+ const adjustedConfig = { ...config };
149
+
150
+ keys.forEach((key) => {
151
+ const path = ["targetScreen", "styles", key];
152
+
153
+ if (has(screenData, path)) {
154
+ const value = get(screenData, path);
155
+
156
+ adjustedConfig[key] = value;
157
+ }
158
+ });
159
+
160
+ return adjustedConfig;
161
+ };
162
+
163
+ const AUDIO_PLAYER_ARTWORK_IMAGE_KEY = "audio_player_artwork_image_key";
164
+
165
+ const audioPlayerArtworkImageKeySelector = (config) =>
166
+ config?.[AUDIO_PLAYER_ARTWORK_IMAGE_KEY];
167
+
168
+ export const useArtworkImage = (entry: ZappEntry): string => {
169
+ const configuration = useZStore("playerConfiguration");
170
+
171
+ const audio_player_artwork_image_key_value = configuration(
172
+ audioPlayerArtworkImageKeySelector
173
+ );
174
+
175
+ const pluginConfiguration = React.useMemo(
176
+ () => ({
177
+ audio_player_artwork_image_key: audio_player_artwork_image_key_value,
178
+ }),
179
+ [audio_player_artwork_image_key_value]
180
+ );
181
+
182
+ // HACK: for override [key] from screenData, because we treat empty string value as missing key and replace it to initial_value from manifest(which is wrong)
183
+ const adjustedPluginConfiguration = useAdjustedKeyFromScreenData({
184
+ keys: [AUDIO_PLAYER_ARTWORK_IMAGE_KEY],
185
+ config: pluginConfiguration,
186
+ });
187
+
188
+ return React.useMemo(
189
+ () =>
190
+ getArtworkImage({
191
+ key: AUDIO_PLAYER_ARTWORK_IMAGE_KEY,
192
+ entry,
193
+ plugin_configuration: adjustedPluginConfiguration,
194
+ }),
195
+ []
196
+ );
197
+ };
198
+
199
+ const AUDIO_PLAYER_IMAGE_KEY = "audio_player_image_key";
200
+ const AUDIO_PLAYER_BACKGROUND_IMAGE = "audio_player_background_image";
201
+
202
+ const audioPlayerImageKeySelector = (config) =>
203
+ config?.[AUDIO_PLAYER_IMAGE_KEY];
204
+
205
+ const audioPlayerBackgroundImageSelector = (config) =>
206
+ config?.[AUDIO_PLAYER_BACKGROUND_IMAGE];
207
+
208
+ export const useBackgroundImage = (entry: ZappEntry): { uri: string } => {
209
+ const configuration = useZStore("playerConfiguration");
210
+
211
+ const audio_player_image_key_value = configuration(
212
+ audioPlayerImageKeySelector
213
+ );
214
+
215
+ const audio_player_background_image_value = configuration(
216
+ audioPlayerBackgroundImageSelector
217
+ );
218
+
219
+ const pluginConfiguration = React.useMemo(
220
+ () => ({
221
+ audio_player_image_key: audio_player_image_key_value,
222
+ audio_player_background_image: audio_player_background_image_value,
223
+ }),
224
+ [audio_player_image_key_value, audio_player_background_image_value]
225
+ );
226
+
227
+ // HACK: for override [key] from screenData, because we treat empty string value as missing key and replace it to initial_value from manifest(which is wrong)
228
+ const adjustedPluginConfiguration = useAdjustedKeyFromScreenData({
229
+ keys: [AUDIO_PLAYER_IMAGE_KEY, AUDIO_PLAYER_BACKGROUND_IMAGE],
230
+ config: pluginConfiguration,
231
+ });
232
+
233
+ return React.useMemo(
234
+ () => ({
235
+ uri: getBackgroundImage({
236
+ entry,
237
+ plugin_configuration: adjustedPluginConfiguration,
238
+ }),
239
+ }),
240
+ [entry, adjustedPluginConfiguration]
241
+ );
242
+ };
@@ -0,0 +1,38 @@
1
+ import { isTabsScreen } from "..";
2
+
3
+ describe("isTabsScreen", () => {
4
+ it("should return true if the component type is 'screen-picker-qb-tv' and tabs_screen=true", () => {
5
+ const item = { component_type: "screen-picker-qb-tv", tabs_screen: true };
6
+ expect(isTabsScreen(item)).toBe(true);
7
+ });
8
+
9
+ it("should return true if the component type is 'screen-picker-qb-tv' and tabs_screen=false", () => {
10
+ const item = { component_type: "screen-picker-qb-tv", tabs_screen: false };
11
+ expect(isTabsScreen(item)).toBe(false);
12
+ });
13
+
14
+ it("should return false if the component type is not 'screen-picker-qb-tv'", () => {
15
+ const item = { component_type: "other-component" };
16
+ expect(isTabsScreen(item)).toBe(false);
17
+ });
18
+
19
+ it("should return false if the component type is undefined", () => {
20
+ const item = { component_type: undefined };
21
+ expect(isTabsScreen(item)).toBe(false);
22
+ });
23
+
24
+ it("should return false if the item is null", () => {
25
+ const item = null;
26
+ expect(isTabsScreen(item)).toBe(false);
27
+ });
28
+
29
+ it("should return false if the item is undefined", () => {
30
+ const item = undefined;
31
+ expect(isTabsScreen(item)).toBe(false);
32
+ });
33
+
34
+ it("should return false if the item is an empty object", () => {
35
+ const item = {};
36
+ expect(isTabsScreen(item)).toBe(false);
37
+ });
38
+ });
@@ -5,7 +5,7 @@ const EMPTY_GROUP_COMPONENT = "empty_group_component";
5
5
 
6
6
  const GALLERY = "gallery-qb";
7
7
 
8
- const SCREEN_PICKER = "screen-picker-qb-tv";
8
+ export const SCREEN_PICKER = "screen-picker-qb-tv";
9
9
 
10
10
  const HORIZONTAL_LIST = "horizontal_list_qb";
11
11
 
@@ -37,3 +37,6 @@ export const isEmptyGroup = (item): boolean =>
37
37
  export const isGroupInfo = (item): boolean =>
38
38
  item?.component_type === GROUP_INFO ||
39
39
  item?.component_type === GROUP_INFO_OLD;
40
+
41
+ export const isTabsScreen = (item): boolean =>
42
+ isScreenPicker(item) && item?.tabs_screen;
@@ -0,0 +1,34 @@
1
+ import { selectActionButtons } from "@applicaster/zapp-react-native-utils/conf/player/selectors";
2
+
3
+ describe("selectActionButtons", () => {
4
+ it("returns the player_action_buttons array if present", () => {
5
+ const pluginConf = {
6
+ player_action_buttons: [
7
+ { id: "like", label: "Like" },
8
+ { id: "share", label: "Share" },
9
+ ],
10
+ };
11
+
12
+ expect(selectActionButtons(pluginConf)).toEqual(
13
+ pluginConf.player_action_buttons
14
+ );
15
+ });
16
+
17
+ it("returns null if player_action_buttons is not present", () => {
18
+ const pluginConf = { some_other_key: [] };
19
+ expect(selectActionButtons(pluginConf)).toBeNull();
20
+ });
21
+
22
+ it("returns null if pluginConf is undefined", () => {
23
+ expect(selectActionButtons(undefined)).toBeNull();
24
+ });
25
+
26
+ it("returns null if pluginConf is null", () => {
27
+ expect(selectActionButtons(null)).toBeNull();
28
+ });
29
+
30
+ it("returns null if player_action_buttons is explicitly set to null", () => {
31
+ const pluginConf = { player_action_buttons: null };
32
+ expect(selectActionButtons(pluginConf)).toBeNull();
33
+ });
34
+ });
@@ -0,0 +1,10 @@
1
+ import { get } from "@applicaster/zapp-react-native-utils/utils";
2
+
3
+ /**
4
+ * Selects the action buttons from the player configuration.
5
+ * @param {Object} pluginConf - player plugin config
6
+ * @returns {Array|null} An array of action buttons or null if not found.
7
+ */
8
+ export const selectActionButtons = (pluginConf: any) => {
9
+ return get(pluginConf, "player_action_buttons", null);
10
+ };
@@ -1,11 +1,8 @@
1
- import * as R from "ramda";
2
1
  import {
3
2
  populateConfigurationValues,
4
- imageSrcFromMediaItem,
5
3
  getBoolFromConfigValue,
6
4
  remapUpdatedKeys,
7
5
  } from "../";
8
- import { entry } from "./testEntry";
9
6
 
10
7
  describe("getBoolFromConfigValue", () => {
11
8
  it('returns true if value is "true" string', () => {
@@ -57,34 +54,6 @@ describe("getBoolFromConfigValue", () => {
57
54
  });
58
55
  });
59
56
 
60
- describe("imageSrcFromMediaItem", () => {
61
- describe("returns the src value of first media_item", () => {
62
- it("when the matching key is found and the src is not empty", () => {
63
- const result = imageSrcFromMediaItem(entry, ["logo_thumbnail"]);
64
-
65
- expect(result).toEqual(entry.media_group[1].media_item[0].src);
66
- expect(result).not.toEqual("");
67
- });
68
- });
69
-
70
- it("returns a media item with the 'image_base' key as a fallback", () => {
71
- const result = imageSrcFromMediaItem(entry, ["does_not_exist"]);
72
- const fallback = entry.media_group[0].media_item[0];
73
- expect(result).toEqual(fallback.src);
74
- expect(fallback.key).toBe("image_base");
75
- });
76
-
77
- it("returns undefined if the key was found but the source was empty", () => {
78
- const badEntry = R.set(
79
- R.lensPath(["media_group", 0, "media_item", 0, "src"]),
80
- "",
81
- entry
82
- );
83
-
84
- expect(imageSrcFromMediaItem(badEntry, ["image_base"])).toBeUndefined();
85
- });
86
- });
87
-
88
57
  describe("populateConfigurationValues", () => {
89
58
  it("transforms and returns the valid values", () => {
90
59
  const fields = [
@@ -0,0 +1,65 @@
1
+ import { getMediaItems } from "..";
2
+ import { entry as baseEntry } from "./testEntry";
3
+
4
+ describe("getMediaItems", () => {
5
+ it("returns both image and thumbnail media items", () => {
6
+ const items = getMediaItems(baseEntry);
7
+ expect(items).toHaveLength(2);
8
+ expect(items[0].key).toBe("image_base");
9
+ expect(items[1].key).toBe("logo_thumbnail");
10
+ });
11
+
12
+ it("returns only image media items if no thumbnail present", () => {
13
+ const entry = {
14
+ ...baseEntry,
15
+ media_group: [
16
+ {
17
+ type: "image",
18
+ media_item: [
19
+ { key: "image_base", src: "img.png" },
20
+ { key: "other", src: "other.png" },
21
+ ],
22
+ },
23
+ ],
24
+ };
25
+
26
+ const items = getMediaItems(entry);
27
+ expect(items).toHaveLength(2);
28
+ expect(items[0].key).toBe("image_base");
29
+ expect(items[1].key).toBe("other");
30
+ });
31
+
32
+ it("returns only thumbnail media items if no image present", () => {
33
+ const entry = {
34
+ ...baseEntry,
35
+ media_group: [
36
+ {
37
+ type: "thumbnail",
38
+ media_item: [{ key: "thumb1", src: "thumb1.png" }],
39
+ },
40
+ ],
41
+ };
42
+
43
+ const items = getMediaItems(entry);
44
+ expect(items).toHaveLength(1);
45
+ expect(items[0].key).toBe("thumb1");
46
+ });
47
+
48
+ it("returns undefined if no media_group present", () => {
49
+ const entry = { ...baseEntry };
50
+ delete entry.media_group;
51
+ expect(getMediaItems(entry)).toBeUndefined();
52
+ });
53
+
54
+ it("returns undefined if media_group is present but has no image or thumbnail", () => {
55
+ const entry = {
56
+ ...baseEntry,
57
+ media_group: [
58
+ { type: "audio", media_item: [{ key: "audio1", src: "audio1.mp3" }] },
59
+ ],
60
+ };
61
+
62
+ const items = getMediaItems(entry);
63
+ expect(items).toBeUndefined();
64
+ });
65
+ });