@applicaster/zapp-react-native-utils 16.0.0-rc.12 → 16.0.0-rc.120

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 (224) hide show
  1. package/actionUtils/index.ts +3 -1
  2. package/actionsExecutor/ActionExecutor.ts +57 -12
  3. package/actionsExecutor/ActionExecutorContext.tsx +113 -314
  4. package/actionsExecutor/__tests__/ActionExecutor.test.ts +71 -0
  5. package/actionsExecutor/__tests__/feedDecorator.test.ts +61 -0
  6. package/actionsExecutor/actions/__tests__/dismissBottomSheet.test.ts +25 -0
  7. package/actionsExecutor/actions/__tests__/goBack.test.ts +62 -0
  8. package/actionsExecutor/actions/__tests__/goHome.test.ts +19 -0
  9. package/actionsExecutor/actions/__tests__/loadItemsFromSource.test.ts +88 -0
  10. package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +267 -0
  11. package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +100 -0
  12. package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +300 -0
  13. package/actionsExecutor/actions/__tests__/presentScreen.test.ts +127 -0
  14. package/actionsExecutor/actions/__tests__/showAlert.test.ts +49 -0
  15. package/actionsExecutor/actions/__tests__/showToast.test.ts +88 -0
  16. package/actionsExecutor/actions/appRestart.ts +24 -0
  17. package/actionsExecutor/actions/confirmDialog.ts +53 -0
  18. package/actionsExecutor/actions/dismissBottomSheet.ts +23 -0
  19. package/actionsExecutor/actions/goBack.ts +60 -0
  20. package/actionsExecutor/actions/goHome.ts +31 -0
  21. package/actionsExecutor/actions/index.ts +42 -0
  22. package/actionsExecutor/actions/localStorageRemove.ts +27 -0
  23. package/actionsExecutor/actions/localStorageSet.ts +28 -0
  24. package/actionsExecutor/actions/localStorageToggleFlag.ts +28 -0
  25. package/actionsExecutor/actions/navigateToScreen.ts +121 -0
  26. package/actionsExecutor/actions/openBottomSheet.ts +339 -0
  27. package/actionsExecutor/actions/presentScreen.ts +78 -0
  28. package/actionsExecutor/actions/refreshComponent.ts +85 -0
  29. package/actionsExecutor/actions/screenSetVariable.ts +35 -0
  30. package/actionsExecutor/actions/screenToggleFlag.ts +38 -0
  31. package/actionsExecutor/actions/sendCloudEvent.ts +93 -0
  32. package/actionsExecutor/actions/sessionStorageRemove.ts +19 -0
  33. package/actionsExecutor/actions/sessionStorageSet.ts +20 -0
  34. package/actionsExecutor/actions/sessionStorageToggleFlag.ts +15 -0
  35. package/actionsExecutor/actions/showAlert.ts +51 -0
  36. package/actionsExecutor/actions/showToast.ts +87 -0
  37. package/actionsExecutor/actions/switchLayout.ts +40 -0
  38. package/actionsExecutor/consts.ts +27 -0
  39. package/actionsExecutor/feedDecorator.ts +6 -6
  40. package/actionsExecutor/types.ts +59 -0
  41. package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -2
  42. package/analyticsUtils/PlayerAnalyticsManager.ts +12 -2
  43. package/analyticsUtils/__tests__/analyticsMapper.test.ts +35 -0
  44. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testACP_events.json +1 -1
  45. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testBlockUnlistedParams_rules.json +1 -1
  46. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_events.json +4 -0
  47. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_rules.json +6 -0
  48. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEventRegex_events.json +3 -9
  49. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_events.json +18 -0
  50. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_rules.json +16 -0
  51. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_events.json +4 -0
  52. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_rules.json +6 -0
  53. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_events.json +4 -0
  54. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_rules.json +6 -0
  55. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_events.json +4 -0
  56. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_rules.json +6 -0
  57. package/analyticsUtils/__tests__/fixtures/index.js +20 -0
  58. package/analyticsUtils/analyticsMapper.ts +4 -1
  59. package/analyticsUtils/playerAnalyticsTracker.ts +26 -3
  60. package/appUtils/HooksManager/index.ts +9 -0
  61. package/appUtils/contextKeysManager/__tests__/getKey/failure.test.ts +1 -1
  62. package/appUtils/contextKeysManager/__tests__/removeKey/failure.test.ts +1 -1
  63. package/appUtils/contextKeysManager/__tests__/setKey/failure/invalidKey.test.ts +4 -4
  64. package/appUtils/contextKeysManager/index.ts +1 -1
  65. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +142 -59
  66. package/appUtils/playerManager/OverlayObserver/__tests__/getUpcomingQueue.test.ts +67 -0
  67. package/appUtils/playerManager/OverlayObserver/__tests__/liveContent.test.ts +205 -0
  68. package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +56 -0
  69. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.android.tv.ts +4 -0
  70. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ios.tv.ts +4 -0
  71. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +24 -0
  72. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.web.ts +4 -0
  73. package/appUtils/playerManager/OverlayObserver/utils.ts +51 -20
  74. package/appUtils/playerManager/__tests__/playerContent.test.ts +403 -0
  75. package/appUtils/playerManager/__tests__/playerFactory.test.ts +1 -1
  76. package/appUtils/playerManager/__tests__/playerNative.test.ts +114 -0
  77. package/appUtils/playerManager/__tests__/playerNativeCommand.test.ts +62 -0
  78. package/appUtils/playerManager/__tests__/playerToolbox.test.ts +97 -0
  79. package/appUtils/playerManager/__tests__/usePlayerContent.test.tsx +64 -0
  80. package/appUtils/playerManager/__tests__/usePlayerControllerSetup.test.tsx +62 -0
  81. package/appUtils/playerManager/{conts.ts → const.ts} +33 -0
  82. package/appUtils/playerManager/index.ts +8 -1
  83. package/appUtils/playerManager/player.ts +125 -3
  84. package/appUtils/playerManager/playerFactory.ts +1 -1
  85. package/appUtils/playerManager/playerNative.ts +28 -4
  86. package/appUtils/playerManager/playerNativeCommand.ts +3 -6
  87. package/appUtils/playerManager/playerToolbox.ts +157 -0
  88. package/appUtils/playerManager/usePlayerContent.tsx +43 -0
  89. package/appUtils/playerManager/usePlayerControllerSetup.tsx +7 -1
  90. package/appUtils/playerManager/userLanguagePreference.ts +1 -1
  91. package/arrayUtils/__tests__/{anyThruthy.test.ts → anyTruthy.test.ts} +8 -0
  92. package/arrayUtils/__tests__/arrayUtils.test.ts +165 -108
  93. package/arrayUtils/__tests__/isEmptyArray.test.ts +11 -0
  94. package/arrayUtils/__tests__/isFilledArray.test.ts +12 -0
  95. package/arrayUtils/__tests__/isFirst.test.ts +17 -0
  96. package/arrayUtils/__tests__/isIndexInRange.test.ts +14 -0
  97. package/arrayUtils/__tests__/isLast.test.ts +31 -0
  98. package/arrayUtils/__tests__/makeListOf.test.ts +31 -0
  99. package/arrayUtils/__tests__/makeListOfIndexes.test.ts +20 -0
  100. package/arrayUtils/__tests__/reorderByIds.test.ts +63 -0
  101. package/arrayUtils/__tests__/sample.test.ts +61 -0
  102. package/arrayUtils/index.ts +119 -20
  103. package/bottomSheet/__tests__/scrollContext.test.tsx +35 -0
  104. package/bottomSheet/index.ts +17 -0
  105. package/cellUtils/__tests__/getButtonsEnabled.test.ts +28 -0
  106. package/cellUtils/__tests__/getCellNavigationEnabled.test.ts +36 -0
  107. package/cellUtils/__tests__/getIsCellNavigationEnabled.test.ts +35 -0
  108. package/cellUtils/__tests__/isCellNavigationEnabled.test.ts +21 -0
  109. package/cellUtils/index.ts +73 -8
  110. package/colorUtils/__tests__/isTransparentColor.test.ts +76 -0
  111. package/colorUtils/__tests__/isValidColor.test.ts +70 -0
  112. package/colorUtils/index.ts +50 -0
  113. package/configurationUtils/__tests__/manifestKeyParser.test.ts +21 -0
  114. package/configurationUtils/__tests__/modalBlocksParser.test.ts +392 -0
  115. package/configurationUtils/manifestKeyParser.ts +50 -10
  116. package/configurationUtils/modalBlocksParser.ts +252 -0
  117. package/entryActions/__tests__/buildEntryActions.test.ts +235 -0
  118. package/entryActions/aliasPresentation.ts +108 -0
  119. package/entryActions/buildEntryActions.ts +167 -0
  120. package/entryActions/index.ts +15 -0
  121. package/entryActions/types.ts +39 -0
  122. package/focusManager/FocusManager.ts +26 -0
  123. package/focusManager/__tests__/FocusManager.test.ts +9 -0
  124. package/localizationUtils/index.ts +12 -6
  125. package/logger/__tests__/xrayToolbox.test.ts +69 -0
  126. package/logger/xrayToolbox.ts +67 -0
  127. package/manifestUtils/__tests__/player.audioControls.test.js +158 -0
  128. package/manifestUtils/_internals/index.js +14 -3
  129. package/manifestUtils/defaultManifestConfigurations/generalContent.js +41 -0
  130. package/manifestUtils/defaultManifestConfigurations/player.js +357 -20
  131. package/manifestUtils/fieldUtils/__tests__/fieldUtils.test.js +84 -0
  132. package/manifestUtils/fieldUtils/index.js +125 -0
  133. package/manifestUtils/index.js +3 -0
  134. package/manifestUtils/keys.js +15 -1
  135. package/manifestUtils/mobileAction/button/index.js +16 -0
  136. package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +21 -0
  137. package/manifestUtils/mobileAction/container/index.js +3 -1
  138. package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +1 -1
  139. package/manifestUtils/mobileAction/groups/defaults.js +5 -2
  140. package/manifestUtils/modalBlocks.js +304 -0
  141. package/manifestUtils/platformIsTV.js +1 -0
  142. package/modalState/ContentViewModel.ts +113 -0
  143. package/modalState/EditableCollection.ts +17 -0
  144. package/modalState/EditableCollectionRegistry.ts +51 -0
  145. package/modalState/ModalOrchestrator.ts +212 -0
  146. package/modalState/RemoteEditableCollection.ts +235 -0
  147. package/modalState/__tests__/ContentViewModel.editable.test.ts +69 -0
  148. package/modalState/__tests__/ContentViewModel.test.ts +165 -0
  149. package/modalState/__tests__/EditableCollectionRegistry.test.ts +112 -0
  150. package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +63 -0
  151. package/modalState/__tests__/RemoteEditableCollection.test.ts +415 -0
  152. package/modalState/__tests__/index.test.ts +30 -1
  153. package/modalState/__tests__/useBottomSheetContent.test.ts +349 -0
  154. package/modalState/__tests__/useModalStoreState.test.ts +142 -0
  155. package/modalState/index.ts +35 -83
  156. package/modalState/store.ts +109 -0
  157. package/modalState/types.ts +162 -0
  158. package/modalState/useBottomSheetContent.ts +109 -0
  159. package/navigationUtils/xrayNavigator.ts +12 -0
  160. package/numberUtils/__tests__/isMinusZero.test.ts +20 -0
  161. package/numberUtils/__tests__/isZero.test.ts +27 -0
  162. package/numberUtils/__tests__/requireNumber.test.ts +50 -0
  163. package/numberUtils/__tests__/toNumber.test.ts +27 -0
  164. package/numberUtils/__tests__/toNumberWithDefault.test.ts +64 -0
  165. package/numberUtils/__tests__/toNumberWithDefaultZero.test.ts +48 -0
  166. package/numberUtils/index.ts +27 -8
  167. package/package.json +2 -2
  168. package/pipesUtils/__tests__/buildUrlWithQuery.test.ts +33 -0
  169. package/pipesUtils/__tests__/withPipesEndpoint.test.tsx +56 -0
  170. package/pipesUtils/index.ts +1 -0
  171. package/pipesUtils/withPipesEndpoint.tsx +54 -0
  172. package/playerUtils/__tests__/contentDataKeys.test.ts +297 -0
  173. package/playerUtils/__tests__/resolvePlayerActions.test.ts +138 -0
  174. package/playerUtils/__tests__/resolvePlayerTitleSubtitle.test.ts +180 -0
  175. package/playerUtils/contentDataKeys.ts +134 -0
  176. package/playerUtils/index.ts +37 -16
  177. package/playerUtils/isAudioItem.ts +26 -0
  178. package/playerUtils/resolvePlayerActions.ts +71 -0
  179. package/playerUtils/resolvePlayerTitleSubtitle.ts +76 -0
  180. package/pluginUtils/__tests__/pluginUtils.test.js +32 -0
  181. package/pluginUtils/index.ts +29 -0
  182. package/reactHooks/__tests__/getPluginConfiguration.test.ts +82 -0
  183. package/reactHooks/actions/index.ts +117 -2
  184. package/reactHooks/cell-click/__tests__/index.test.js +64 -0
  185. package/reactHooks/cell-click/index.ts +39 -24
  186. package/reactHooks/connection/index.tsx +8 -5
  187. package/reactHooks/feed/__mocks__/useMarkPipesDataStale.ts +4 -0
  188. package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +25 -0
  189. package/reactHooks/feed/__tests__/useMarkPipesDataStale.test.tsx +243 -0
  190. package/reactHooks/feed/index.ts +5 -1
  191. package/reactHooks/feed/useBatchLoading.ts +9 -1
  192. package/reactHooks/feed/useMarkPipesDataStale.ts +97 -0
  193. package/reactHooks/index.ts +2 -0
  194. package/reactHooks/layout/index.ts +1 -1
  195. package/reactHooks/navigation/__mocks__/index.ts +4 -0
  196. package/reactHooks/navigation/__tests__/index.test.tsx +176 -1
  197. package/reactHooks/navigation/__tests__/useIsScreenActive.test.ts +42 -0
  198. package/reactHooks/navigation/__tests__/usePresentScreen.test.ts +154 -0
  199. package/reactHooks/navigation/index.ts +3 -1
  200. package/reactHooks/navigation/useIsScreenActive.ts +29 -8
  201. package/reactHooks/navigation/usePresentScreen.ts +157 -0
  202. package/reactHooks/navigation/useRoute.ts +22 -5
  203. package/reactHooks/state/useComponentScreenState.ts +1 -1
  204. package/reactHooks/usePluginConfiguration.ts +88 -47
  205. package/reactHooks/utils/__tests__/index.test.js +22 -2
  206. package/reactHooks/utils/index.ts +13 -2
  207. package/riverComponetsMeasurementProvider/index.tsx +15 -9
  208. package/stringUtils/__tests__/stringUtils.test.js +42 -0
  209. package/stringUtils/index.ts +2 -2
  210. package/uiActionsRegistrator/__tests__/resolveActionIdentifiers.test.ts +93 -0
  211. package/uiActionsRegistrator/__tests__/subscribe.test.ts +133 -0
  212. package/uiActionsRegistrator/__tests__/usableActionItems.test.ts +52 -0
  213. package/uiActionsRegistrator/index.ts +25 -0
  214. package/uiActionsRegistrator/registry.ts +351 -0
  215. package/uiActionsRegistrator/resolveActionIdentifiers.ts +100 -0
  216. package/uiActionsRegistrator/usableActionItems.ts +66 -0
  217. package/zappFrameworkUtils/__tests__/localStorageHelper.test.ts +146 -0
  218. package/zappFrameworkUtils/localStorageHelper.ts +19 -15
  219. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +0 -56
  220. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +0 -54
  221. package/playerUtils/getPlayerActionButtons.ts +0 -17
  222. package/reactHooks/feed/__mocks__/usePipesCacheReset.ts +0 -1
  223. package/reactHooks/feed/usePipesCacheReset.ts +0 -44
  224. /package/reactHooks/actions/__tests__/{index.test.js → index.test.tsx} +0 -0
@@ -12,6 +12,12 @@ const generalContent = () => ({
12
12
  label: "Navigation Bar",
13
13
  placeholder: "Choose Nav Bar",
14
14
  },
15
+ {
16
+ key: "hide_menu",
17
+ type: "switch",
18
+ label: "Hide Menu",
19
+ initial_value: false,
20
+ },
15
21
  ],
16
22
  },
17
23
  styles: {
@@ -308,6 +314,41 @@ const generalContent = () => ({
308
314
  key: "pull_to_refresh_enabled",
309
315
  initial_value: false,
310
316
  },
317
+ {
318
+ type: "switch",
319
+ label: "Allow using this screen as a hook",
320
+ label_tooltip:
321
+ "Make sure that screen uses 'finishHook' action or performs navigation action to exit the screen after performing the hook action, or user will be stuck on the screen", // eslint-disable-line max-len
322
+ key: "available_as_hook",
323
+ initial_value: false,
324
+ },
325
+ {
326
+ type: "data_source_selector",
327
+ label: "Skip hook endpoint",
328
+ key: "skip_hook_endpoint",
329
+ label_tooltip:
330
+ "If set, this endpoint will check with the server whether the hook should be skipped",
331
+ conditional_fields: [
332
+ {
333
+ key: "rules/available_as_hook",
334
+ condition_value: true,
335
+ },
336
+ ],
337
+ },
338
+ {
339
+ key: "skip_hook_storage_key",
340
+ type: "text_input",
341
+ label: "Hook will be skipped if storage key is set",
342
+ initial_value: "",
343
+ label_tooltip:
344
+ "Comma-separated keys in namespace.key format (key without a dot uses the default namespace)", // eslint-disable-line max-len
345
+ conditional_fields: [
346
+ {
347
+ key: "rules/available_as_hook",
348
+ condition_value: true,
349
+ },
350
+ ],
351
+ },
311
352
  ],
312
353
  },
313
354
  });
@@ -204,6 +204,20 @@ function getPlayerConfiguration({ platform, version }) {
204
204
  label_tooltip: "Label for close button accessibility",
205
205
  type: "text_input",
206
206
  },
207
+ {
208
+ key: "accessibility_overflow_label",
209
+ label: "Accessibility overflow label",
210
+ initial_value: "More actions",
211
+ label_tooltip: "Label for the overflow (…) button accessibility",
212
+ type: "text_input",
213
+ },
214
+ {
215
+ key: "accessibility_overflow_hint",
216
+ label: "Accessibility overflow hint",
217
+ initial_value: "Opens the remaining player actions",
218
+ label_tooltip: "Hint for the overflow (…) button accessibility",
219
+ type: "text_input",
220
+ },
207
221
  {
208
222
  key: "accessibility_close_hint",
209
223
  label: "Accessibility close hint",
@@ -383,7 +397,45 @@ function getPlayerConfiguration({ platform, version }) {
383
397
  initial_value: "Start Over",
384
398
  type: "text_input",
385
399
  });
400
+ }
401
+
402
+ if (isMobile(platform)) {
403
+ // Screen reader strings for the audio player's playlist controls. Mobile
404
+ // only, because the audio player layout itself is mobile only.
405
+ localizations.fields.push({
406
+ key: "accessibility_previous_track_label",
407
+ label: "Accessibility previous track label",
408
+ initial_value: "Previous track button",
409
+ label_tooltip: "Label for previous track button accessibility",
410
+ type: "text_input",
411
+ });
412
+
413
+ localizations.fields.push({
414
+ key: "accessibility_previous_track_hint",
415
+ label: "Accessibility previous track hint",
416
+ initial_value: "Press to play the previous track",
417
+ label_tooltip: "Hint for previous track button accessibility",
418
+ type: "text_input",
419
+ });
420
+
421
+ localizations.fields.push({
422
+ key: "accessibility_next_track_label",
423
+ label: "Accessibility next track label",
424
+ initial_value: "Next track button",
425
+ label_tooltip: "Label for next track button accessibility",
426
+ type: "text_input",
427
+ });
428
+
429
+ localizations.fields.push({
430
+ key: "accessibility_next_track_hint",
431
+ label: "Accessibility next track hint",
432
+ initial_value: "Press to play the next track",
433
+ label_tooltip: "Hint for next track button accessibility",
434
+ type: "text_input",
435
+ });
436
+ }
386
437
 
438
+ if (isTV(platform)) {
387
439
  styles.fields.push(
388
440
  fieldsGroup("Always Show Scrub Bar & Timestamp", "", [
389
441
  {
@@ -753,7 +805,7 @@ function getPlayerConfiguration({ platform, version }) {
753
805
  label: "Player action buttons",
754
806
  initial_value: null,
755
807
  label_tooltip:
756
- "type here the identifiers of the action buttons you want to show on the player, separated by commas. Only 2 action buttons may be added at the same time",
808
+ "Comma-separated action identifiers. The entry_actions token expands from the playing entry's extensions.entry_action. When more than 2 actions resolve, the video and fullscreen audio players show Overflow (…) plus one companion - the first action that did not come from the entry, otherwise the first one - and the Overflow sheet lists every resolved action. A repeated identifier is counted once. No extra plugin is required.",
757
809
  type: "text_input",
758
810
  },
759
811
  {
@@ -778,24 +830,17 @@ function getPlayerConfiguration({ platform, version }) {
778
830
  type: "switch",
779
831
  label_tooltip:
780
832
  "Enable this option to have a floating video player when the app is in the background.",
833
+ },
834
+ {
835
+ key: "vertical_video_player",
836
+ label: "Vertical video mode",
837
+ initial_value: false,
838
+ type: "switch",
839
+ label_tooltip:
840
+ "Enable this option to use the vertical video player layout.",
781
841
  }
782
842
  );
783
843
 
784
- if (
785
- platform.includes("android") ||
786
- platform.includes("amazon") ||
787
- platform.includes("ios") ||
788
- platform.includes("tvos")
789
- ) {
790
- general.fields.push({
791
- key: "user_agent",
792
- label: "User agent",
793
- initial_value: "",
794
- type: "text_input",
795
- label_tooltip: "Override default user agent string",
796
- });
797
- }
798
-
799
844
  if (platform === "android_for_quickbrick") {
800
845
  general.fields.push({
801
846
  key: "stop_playback_on_task_removal",
@@ -1068,6 +1113,13 @@ function getPlayerConfiguration({ platform, version }) {
1068
1113
  type: "uploader",
1069
1114
  label_tooltip: "Override default close badge / icon",
1070
1115
  },
1116
+ {
1117
+ key: "overflow",
1118
+ label: "Overflow badge",
1119
+ type: "uploader",
1120
+ label_tooltip:
1121
+ "Override default overflow (…) badge / icon, shown when more action buttons resolve than fit on the player overlay",
1122
+ },
1071
1123
  {
1072
1124
  key: "lock",
1073
1125
  label: "Lock player badge",
@@ -1236,6 +1288,13 @@ function getPlayerConfiguration({ platform, version }) {
1236
1288
  type: "uploader",
1237
1289
  label_tooltip: "Playback Speed 0.8x",
1238
1290
  initial_value: null,
1291
+ rules: "conditional",
1292
+ conditional_fields: [
1293
+ {
1294
+ key: "styles/player_controls_layout",
1295
+ condition_value: "playback_speed_and_sleep_timer",
1296
+ },
1297
+ ],
1239
1298
  },
1240
1299
  {
1241
1300
  key: "speed_1",
@@ -1243,6 +1302,13 @@ function getPlayerConfiguration({ platform, version }) {
1243
1302
  type: "uploader",
1244
1303
  label_tooltip: "Playback Speed 1x",
1245
1304
  initial_value: null,
1305
+ rules: "conditional",
1306
+ conditional_fields: [
1307
+ {
1308
+ key: "styles/player_controls_layout",
1309
+ condition_value: "playback_speed_and_sleep_timer",
1310
+ },
1311
+ ],
1246
1312
  },
1247
1313
  {
1248
1314
  key: "speed_1_2",
@@ -1250,6 +1316,13 @@ function getPlayerConfiguration({ platform, version }) {
1250
1316
  type: "uploader",
1251
1317
  label_tooltip: "Playback Speed 1.2x",
1252
1318
  initial_value: null,
1319
+ rules: "conditional",
1320
+ conditional_fields: [
1321
+ {
1322
+ key: "styles/player_controls_layout",
1323
+ condition_value: "playback_speed_and_sleep_timer",
1324
+ },
1325
+ ],
1253
1326
  },
1254
1327
  {
1255
1328
  key: "speed_1_5",
@@ -1257,6 +1330,13 @@ function getPlayerConfiguration({ platform, version }) {
1257
1330
  type: "uploader",
1258
1331
  label_tooltip: "Playback Speed 1.5x",
1259
1332
  initial_value: null,
1333
+ rules: "conditional",
1334
+ conditional_fields: [
1335
+ {
1336
+ key: "styles/player_controls_layout",
1337
+ condition_value: "playback_speed_and_sleep_timer",
1338
+ },
1339
+ ],
1260
1340
  },
1261
1341
  {
1262
1342
  key: "speed_2",
@@ -1264,6 +1344,13 @@ function getPlayerConfiguration({ platform, version }) {
1264
1344
  type: "uploader",
1265
1345
  label_tooltip: "Playback Speed 2x",
1266
1346
  initial_value: null,
1347
+ rules: "conditional",
1348
+ conditional_fields: [
1349
+ {
1350
+ key: "styles/player_controls_layout",
1351
+ condition_value: "playback_speed_and_sleep_timer",
1352
+ },
1353
+ ],
1267
1354
  },
1268
1355
  {
1269
1356
  key: "sleep_timer",
@@ -1271,6 +1358,13 @@ function getPlayerConfiguration({ platform, version }) {
1271
1358
  type: "uploader",
1272
1359
  label_tooltip: "Sleep Timer Default",
1273
1360
  initial_value: null,
1361
+ rules: "conditional",
1362
+ conditional_fields: [
1363
+ {
1364
+ key: "styles/player_controls_layout",
1365
+ condition_value: "playback_speed_and_sleep_timer",
1366
+ },
1367
+ ],
1274
1368
  },
1275
1369
  {
1276
1370
  key: "sleep_timer_active",
@@ -1278,6 +1372,43 @@ function getPlayerConfiguration({ platform, version }) {
1278
1372
  type: "uploader",
1279
1373
  label_tooltip: "Sleep Timer Active",
1280
1374
  initial_value: null,
1375
+ rules: "conditional",
1376
+ conditional_fields: [
1377
+ {
1378
+ key: "styles/player_controls_layout",
1379
+ condition_value: "playback_speed_and_sleep_timer",
1380
+ },
1381
+ ],
1382
+ },
1383
+ {
1384
+ key: "previous_track",
1385
+ label: "Previous Track Control Icon",
1386
+ type: "uploader",
1387
+ label_tooltip:
1388
+ "Override the default Previous Track icon. Recommended size 120x120px (@3x).",
1389
+ initial_value: null,
1390
+ rules: "conditional",
1391
+ conditional_fields: [
1392
+ {
1393
+ key: "styles/player_controls_layout",
1394
+ condition_value: "playlist_controls",
1395
+ },
1396
+ ],
1397
+ },
1398
+ {
1399
+ key: "next_track",
1400
+ label: "Next Track Control Icon",
1401
+ type: "uploader",
1402
+ label_tooltip:
1403
+ "Override the default Next Track icon. Recommended size 120x120px (@3x).",
1404
+ initial_value: null,
1405
+ rules: "conditional",
1406
+ conditional_fields: [
1407
+ {
1408
+ key: "styles/player_controls_layout",
1409
+ condition_value: "playlist_controls",
1410
+ },
1411
+ ],
1281
1412
  },
1282
1413
  ]
1283
1414
  ),
@@ -1294,6 +1425,35 @@ function getPlayerConfiguration({ platform, version }) {
1294
1425
  type: "switch",
1295
1426
  label_tooltip: "Select whether you want to display title in player",
1296
1427
  },
1428
+ {
1429
+ section: "title",
1430
+ key: "title_data_key_source",
1431
+ label: "Title data key",
1432
+ initial_value: "title",
1433
+ type: "select",
1434
+ options: [
1435
+ { text: "Title", value: "title" },
1436
+ { text: "Other", value: "other" },
1437
+ ],
1438
+ label_tooltip:
1439
+ "Select which feed data key provides the player content title. Keep 'Title' to use the entry title",
1440
+ },
1441
+ {
1442
+ section: "title",
1443
+ key: "title_custom_data_key",
1444
+ label: "Custom title data key",
1445
+ initial_value: "",
1446
+ type: "text_input",
1447
+ rules: "conditional",
1448
+ conditional_fields: [
1449
+ {
1450
+ key: "styles/title_data_key_source",
1451
+ condition_value: "other",
1452
+ },
1453
+ ],
1454
+ label_tooltip:
1455
+ "Data key on the feed entry, dot notation supported, for example extensions.episode_name. Falls back to the entry title if the key is empty or missing",
1456
+ },
1297
1457
  {
1298
1458
  section: "title",
1299
1459
  key: "title_font_android",
@@ -1701,6 +1861,35 @@ function getPlayerConfiguration({ platform, version }) {
1701
1861
  label_tooltip:
1702
1862
  "Select whether you want to display subtitle in player",
1703
1863
  },
1864
+ {
1865
+ section: "subtitle",
1866
+ key: "subtitle_data_key_source",
1867
+ label: "Subtitle data key",
1868
+ initial_value: "summary",
1869
+ type: "select",
1870
+ options: [
1871
+ { text: "Subtitle", value: "summary" },
1872
+ { text: "Other", value: "other" },
1873
+ ],
1874
+ label_tooltip:
1875
+ "Select which feed data key provides the player content subtitle. Keep 'Subtitle' to use the entry summary",
1876
+ },
1877
+ {
1878
+ section: "subtitle",
1879
+ key: "subtitle_custom_data_key",
1880
+ label: "Custom subtitle data key",
1881
+ initial_value: "",
1882
+ type: "text_input",
1883
+ rules: "conditional",
1884
+ conditional_fields: [
1885
+ {
1886
+ key: "styles/subtitle_data_key_source",
1887
+ condition_value: "other",
1888
+ },
1889
+ ],
1890
+ label_tooltip:
1891
+ "Data key on the feed entry, dot notation supported, for example extensions.episode_subtitle. Falls back to the entry summary if the key is empty or missing",
1892
+ },
1704
1893
  {
1705
1894
  section: "subtitle",
1706
1895
  key: "subtitle_font_android",
@@ -2415,6 +2604,21 @@ function getPlayerConfiguration({ platform, version }) {
2415
2604
  );
2416
2605
  }
2417
2606
 
2607
+ if (
2608
+ platform.includes("android") ||
2609
+ platform.includes("amazon") ||
2610
+ platform.includes("ios") ||
2611
+ platform.includes("tvos")
2612
+ ) {
2613
+ general.fields.push({
2614
+ key: "user_agent",
2615
+ label: "User agent",
2616
+ initial_value: "",
2617
+ type: "text_input",
2618
+ label_tooltip: "Override default user agent string",
2619
+ });
2620
+ }
2621
+
2418
2622
  compact([
2419
2623
  {
2420
2624
  key: "liveCatchUpEnabled",
@@ -2850,6 +3054,33 @@ function getPlayerConfiguration({ platform, version }) {
2850
3054
  "Video Player - Title Text",
2851
3055
  "Video Player Title Text Label Styles",
2852
3056
  [
3057
+ {
3058
+ key: "video_player_title_data_key_source",
3059
+ label: "Video title data key",
3060
+ initial_value: "title",
3061
+ type: "select",
3062
+ options: [
3063
+ { text: "Title", value: "title" },
3064
+ { text: "Other", value: "other" },
3065
+ ],
3066
+ label_tooltip:
3067
+ "Select which feed data key provides the video player title. Keep 'Title' to use the entry title",
3068
+ },
3069
+ {
3070
+ key: "video_player_title_custom_data_key",
3071
+ label: "Custom video title data key",
3072
+ initial_value: "",
3073
+ type: "text_input",
3074
+ rules: "conditional",
3075
+ conditional_fields: [
3076
+ {
3077
+ key: "styles/video_player_title_data_key_source",
3078
+ condition_value: "other",
3079
+ },
3080
+ ],
3081
+ label_tooltip:
3082
+ "Data key on the feed entry, dot notation supported, for example extensions.episode_name. Falls back to the entry title if the key is empty or missing",
3083
+ },
2853
3084
  {
2854
3085
  key: "player_title_color",
2855
3086
  label: "Font color",
@@ -2935,6 +3166,33 @@ function getPlayerConfiguration({ platform, version }) {
2935
3166
  "Video Player - Summary Text",
2936
3167
  "Video Player Summary Text Label Styles",
2937
3168
  [
3169
+ {
3170
+ key: "video_player_summary_data_key_source",
3171
+ label: "Video summary data key",
3172
+ initial_value: "summary",
3173
+ type: "select",
3174
+ options: [
3175
+ { text: "Summary", value: "summary" },
3176
+ { text: "Other", value: "other" },
3177
+ ],
3178
+ label_tooltip:
3179
+ "Select which feed data key provides the video player summary. Keep 'Summary' to use the entry summary",
3180
+ },
3181
+ {
3182
+ key: "video_player_summary_custom_data_key",
3183
+ label: "Custom video summary data key",
3184
+ initial_value: "",
3185
+ type: "text_input",
3186
+ rules: "conditional",
3187
+ conditional_fields: [
3188
+ {
3189
+ key: "styles/video_player_summary_data_key_source",
3190
+ condition_value: "other",
3191
+ },
3192
+ ],
3193
+ label_tooltip:
3194
+ "Data key on the feed entry, dot notation supported, for example extensions.episode_subtitle. Falls back to the entry summary if the key is empty or missing",
3195
+ },
2938
3196
  {
2939
3197
  key: "player_summary_color",
2940
3198
  label: "Font color",
@@ -3020,6 +3278,33 @@ function getPlayerConfiguration({ platform, version }) {
3020
3278
  "Audio Player - Title Text",
3021
3279
  "Audio Player Title Text Label Styles",
3022
3280
  [
3281
+ {
3282
+ key: "audio_player_title_data_key_source",
3283
+ label: "Audio title data key",
3284
+ initial_value: "title",
3285
+ type: "select",
3286
+ options: [
3287
+ { text: "Title", value: "title" },
3288
+ { text: "Other", value: "other" },
3289
+ ],
3290
+ label_tooltip:
3291
+ "Select which feed data key provides the audio player title. Keep 'Title' to use the entry title",
3292
+ },
3293
+ {
3294
+ key: "audio_player_title_custom_data_key",
3295
+ label: "Custom audio title data key",
3296
+ initial_value: "",
3297
+ type: "text_input",
3298
+ rules: "conditional",
3299
+ conditional_fields: [
3300
+ {
3301
+ key: "styles/audio_player_title_data_key_source",
3302
+ condition_value: "other",
3303
+ },
3304
+ ],
3305
+ label_tooltip:
3306
+ "Data key on the feed entry, dot notation supported, for example extensions.episode_name. Falls back to the entry title if the key is empty or missing",
3307
+ },
3023
3308
  {
3024
3309
  key: "audio_player_title_color",
3025
3310
  label: "Audio player title color",
@@ -3105,6 +3390,33 @@ function getPlayerConfiguration({ platform, version }) {
3105
3390
  "Audio Player - Summary Text",
3106
3391
  "Audio Player Summary Text Label Styles",
3107
3392
  [
3393
+ {
3394
+ key: "audio_player_summary_data_key_source",
3395
+ label: "Audio summary data key",
3396
+ initial_value: "summary",
3397
+ type: "select",
3398
+ options: [
3399
+ { text: "Summary", value: "summary" },
3400
+ { text: "Other", value: "other" },
3401
+ ],
3402
+ label_tooltip:
3403
+ "Select which feed data key provides the audio player summary. Keep 'Summary' to use the entry summary",
3404
+ },
3405
+ {
3406
+ key: "audio_player_summary_custom_data_key",
3407
+ label: "Custom audio summary data key",
3408
+ initial_value: "",
3409
+ type: "text_input",
3410
+ rules: "conditional",
3411
+ conditional_fields: [
3412
+ {
3413
+ key: "styles/audio_player_summary_data_key_source",
3414
+ condition_value: "other",
3415
+ },
3416
+ ],
3417
+ label_tooltip:
3418
+ "Data key on the feed entry, dot notation supported, for example extensions.episode_subtitle. Falls back to the entry summary if the key is empty or missing",
3419
+ },
3108
3420
  {
3109
3421
  key: "audio_player_summary_color",
3110
3422
  label: "Audio player summary color",
@@ -3451,6 +3763,31 @@ function getPlayerConfiguration({ platform, version }) {
3451
3763
  },
3452
3764
  ],
3453
3765
  },
3766
+ {
3767
+ key: "player_controls_layout",
3768
+ label: "Player Controls Layout",
3769
+ type: "select",
3770
+ initial_value: "playback_speed_and_sleep_timer",
3771
+ label_tooltip:
3772
+ "Choose which secondary controls appear in the audio player transport row.",
3773
+ rules: "conditional",
3774
+ conditional_fields: [
3775
+ {
3776
+ key: "styles/full_screen_audio_player",
3777
+ condition_value: true,
3778
+ },
3779
+ ],
3780
+ options: [
3781
+ {
3782
+ text: "Playback Speed & Sleep Timer (Default)",
3783
+ value: "playback_speed_and_sleep_timer",
3784
+ },
3785
+ {
3786
+ text: "Playlist Controls (Previous & Next)",
3787
+ value: "playlist_controls",
3788
+ },
3789
+ ],
3790
+ },
3454
3791
  {
3455
3792
  key: "audio_player_artwork_shadow_enabled",
3456
3793
  label: "Image Shadow Enabled",
@@ -3522,13 +3859,13 @@ function getPlayerConfiguration({ platform, version }) {
3522
3859
  label: "Advanced Player Styles",
3523
3860
  type: "text_input",
3524
3861
  multiline: true,
3525
- initial_value: `.vjs-text-track-cue {
3526
- inset: unset !important;
3527
- top: 94% !important;
3862
+ initial_value: `.vjs-text-track-cue {
3863
+ top: auto !important;
3528
3864
  bottom: 0 !important;
3529
3865
  left: 0 !important;
3530
- right: 0 !important;
3866
+ right: auto !important;
3531
3867
  width: 100% !important;
3868
+ height: auto !important;
3532
3869
  }`,
3533
3870
  label_tooltip:
3534
3871
  "This field allows you to inject custom CSS styles to override default player styles. The styles will be injected into the document head and can be used to customize the appearance of the player. Leave empty to use only default styles.",
@@ -1,9 +1,93 @@
1
1
  const {
2
+ parseShorthand,
3
+ margin,
4
+ padding,
2
5
  withConditional,
3
6
  getConditionalKey,
4
7
  createConditionalField,
5
8
  } = require("..");
6
9
 
10
+ describe("parseShorthand", () => {
11
+ it("1 value → all four sides equal", () => {
12
+ expect(parseShorthand("8")).toEqual([8, 8, 8, 8]);
13
+ });
14
+
15
+ it("2 values → top/bottom, right/left", () => {
16
+ expect(parseShorthand("0 20")).toEqual([0, 20, 0, 20]);
17
+ });
18
+
19
+ it("3 values → top, right/left, bottom", () => {
20
+ expect(parseShorthand("0 0 20")).toEqual([0, 0, 20, 0]);
21
+ });
22
+
23
+ it("4 values → top right bottom left", () => {
24
+ expect(parseShorthand("1 2 3 4")).toEqual([1, 2, 3, 4]);
25
+ });
26
+
27
+ it("throws on invalid count", () => {
28
+ expect(() => parseShorthand("1 2 3 4 5")).toThrow();
29
+ });
30
+ });
31
+
32
+ describe("margin", () => {
33
+ it("returns 4 field descriptors with correct suffixes", () => {
34
+ const fields = margin("margin", { mobile: "0" });
35
+
36
+ expect(fields.map((f) => f.suffix)).toEqual([
37
+ "margin top",
38
+ "margin right",
39
+ "margin bottom",
40
+ "margin left",
41
+ ]);
42
+ });
43
+
44
+ it("sets type to number_input", () => {
45
+ margin("margin", { mobile: "0" }).forEach((f) => {
46
+ expect(f.type).toBe("number_input");
47
+ });
48
+ });
49
+
50
+ it("expands shorthand per platform", () => {
51
+ const fields = margin("margin", { mobile: "0 0 20", tv: "0 0 90" });
52
+ const byKey = Object.fromEntries(fields.map((f) => [f.suffix, f]));
53
+ expect(byKey["margin top"].initialValue).toEqual({ mobile: 0, tv: 0 });
54
+ expect(byKey["margin bottom"].initialValue).toEqual({ mobile: 20, tv: 90 });
55
+ expect(byKey["margin left"].initialValue).toEqual({ mobile: 0, tv: 0 });
56
+ });
57
+
58
+ it("supports a custom suffix prefix", () => {
59
+ const fields = margin("input margin", { mobile: "4" });
60
+ expect(fields[0].suffix).toBe("input margin top");
61
+ });
62
+ });
63
+
64
+ describe("padding", () => {
65
+ it("returns 4 field descriptors with correct suffixes", () => {
66
+ const fields = padding("padding", { mobile: "0" });
67
+
68
+ expect(fields.map((f) => f.suffix)).toEqual([
69
+ "padding top",
70
+ "padding right",
71
+ "padding bottom",
72
+ "padding left",
73
+ ]);
74
+ });
75
+
76
+ it("expands 2-value shorthand symmetrically", () => {
77
+ const fields = padding("padding", { mobile: "0 20", tv: "0 124" });
78
+ const byKey = Object.fromEntries(fields.map((f) => [f.suffix, f]));
79
+
80
+ expect(byKey["padding right"].initialValue).toEqual({
81
+ mobile: 20,
82
+ tv: 124,
83
+ });
84
+
85
+ expect(byKey["padding left"].initialValue).toEqual({ mobile: 20, tv: 124 });
86
+ expect(byKey["padding top"].initialValue).toEqual({ mobile: 0, tv: 0 });
87
+ expect(byKey["padding bottom"].initialValue).toEqual({ mobile: 0, tv: 0 });
88
+ });
89
+ });
90
+
7
91
  describe("manifestUtils/fieldUtils", () => {
8
92
  it("appends conditions and adds all_conditions when there is more than one condition", () => {
9
93
  const config = {