@applicaster/zapp-react-native-utils 16.0.0-rc.7 → 16.0.0-rc.71

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 (169) hide show
  1. package/actionsExecutor/ActionExecutor.ts +47 -11
  2. package/actionsExecutor/ActionExecutorContext.tsx +87 -316
  3. package/actionsExecutor/__tests__/feedDecorator.test.ts +61 -0
  4. package/actionsExecutor/actions/__tests__/dismissBottomSheet.test.ts +22 -0
  5. package/actionsExecutor/actions/__tests__/loadItemsFromSource.test.ts +88 -0
  6. package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +133 -0
  7. package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +76 -0
  8. package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +100 -0
  9. package/actionsExecutor/actions/__tests__/showToast.test.ts +88 -0
  10. package/actionsExecutor/actions/appRestart.ts +24 -0
  11. package/actionsExecutor/actions/confirmDialog.ts +53 -0
  12. package/actionsExecutor/actions/dismissBottomSheet.ts +22 -0
  13. package/actionsExecutor/actions/index.ts +34 -0
  14. package/actionsExecutor/actions/localStorageRemove.ts +27 -0
  15. package/actionsExecutor/actions/localStorageSet.ts +28 -0
  16. package/actionsExecutor/actions/localStorageToggleFlag.ts +28 -0
  17. package/actionsExecutor/actions/navigateToScreen.ts +137 -0
  18. package/actionsExecutor/actions/openBottomSheet.ts +320 -0
  19. package/actionsExecutor/actions/refreshComponent.ts +85 -0
  20. package/actionsExecutor/actions/screenSetVariable.ts +35 -0
  21. package/actionsExecutor/actions/screenToggleFlag.ts +38 -0
  22. package/actionsExecutor/actions/sendCloudEvent.ts +93 -0
  23. package/actionsExecutor/actions/sessionStorageRemove.ts +19 -0
  24. package/actionsExecutor/actions/sessionStorageSet.ts +20 -0
  25. package/actionsExecutor/actions/sessionStorageToggleFlag.ts +15 -0
  26. package/actionsExecutor/actions/showToast.ts +87 -0
  27. package/actionsExecutor/actions/switchLayout.ts +40 -0
  28. package/actionsExecutor/consts.ts +23 -0
  29. package/actionsExecutor/feedDecorator.ts +6 -6
  30. package/actionsExecutor/types.ts +59 -0
  31. package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -2
  32. package/analyticsUtils/PlayerAnalyticsManager.ts +12 -2
  33. package/analyticsUtils/__tests__/analyticsMapper.test.ts +35 -0
  34. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testACP_events.json +1 -1
  35. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testBlockUnlistedParams_rules.json +1 -1
  36. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_events.json +4 -0
  37. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEmptyRenameKeepsName_rules.json +6 -0
  38. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testEventRegex_events.json +3 -9
  39. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_events.json +18 -0
  40. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testIgnoreOrdering_rules.json +16 -0
  41. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_events.json +4 -0
  42. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexEventNoFallback_rules.json +6 -0
  43. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_events.json +4 -0
  44. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexMultiGroupRename_rules.json +6 -0
  45. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_events.json +4 -0
  46. package/analyticsUtils/__tests__/fixtures/analytics_mapper_testRegexNonNamedFullMatch_rules.json +6 -0
  47. package/analyticsUtils/__tests__/fixtures/index.js +20 -0
  48. package/analyticsUtils/analyticsMapper.ts +4 -1
  49. package/analyticsUtils/playerAnalyticsTracker.ts +26 -3
  50. package/appUtils/HooksManager/index.ts +12 -8
  51. package/appUtils/contextKeysManager/__tests__/getKey/failure.test.ts +1 -1
  52. package/appUtils/contextKeysManager/__tests__/removeKey/failure.test.ts +1 -1
  53. package/appUtils/contextKeysManager/__tests__/setKey/failure/invalidKey.test.ts +4 -4
  54. package/appUtils/contextKeysManager/index.ts +1 -1
  55. package/appUtils/contextKeysManager/utils/index.ts +38 -25
  56. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +143 -53
  57. package/appUtils/playerManager/OverlayObserver/__tests__/liveContent.test.ts +205 -0
  58. package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +49 -0
  59. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.android.tv.ts +4 -0
  60. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ios.tv.ts +4 -0
  61. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +11 -0
  62. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.web.ts +4 -0
  63. package/appUtils/playerManager/OverlayObserver/utils.ts +54 -20
  64. package/appUtils/playerManager/__tests__/playerContent.test.ts +403 -0
  65. package/appUtils/playerManager/__tests__/playerFactory.test.ts +150 -0
  66. package/appUtils/playerManager/__tests__/playerNative.test.ts +38 -0
  67. package/appUtils/playerManager/__tests__/usePlayerContent.test.tsx +64 -0
  68. package/appUtils/playerManager/{conts.ts → const.ts} +20 -0
  69. package/appUtils/playerManager/index.ts +1 -1
  70. package/appUtils/playerManager/player.ts +115 -3
  71. package/appUtils/playerManager/playerFactory.ts +18 -35
  72. package/appUtils/playerManager/playerNative.ts +6 -4
  73. package/appUtils/playerManager/usePlayerContent.tsx +43 -0
  74. package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -1
  75. package/appUtils/playerManager/userLanguagePreference.ts +1 -1
  76. package/arrayUtils/__tests__/{anyThruthy.test.ts → anyTruthy.test.ts} +8 -0
  77. package/arrayUtils/__tests__/arrayUtils.test.ts +165 -108
  78. package/arrayUtils/__tests__/isEmptyArray.test.ts +11 -0
  79. package/arrayUtils/__tests__/isFilledArray.test.ts +12 -0
  80. package/arrayUtils/__tests__/isFirst.test.ts +17 -0
  81. package/arrayUtils/__tests__/isIndexInRange.test.ts +14 -0
  82. package/arrayUtils/__tests__/isLast.test.ts +31 -0
  83. package/arrayUtils/__tests__/makeListOf.test.ts +31 -0
  84. package/arrayUtils/__tests__/makeListOfIndexes.test.ts +20 -0
  85. package/arrayUtils/__tests__/reorderByIds.test.ts +50 -0
  86. package/arrayUtils/__tests__/sample.test.ts +61 -0
  87. package/arrayUtils/index.ts +136 -20
  88. package/bottomSheet/__tests__/scrollContext.test.tsx +35 -0
  89. package/bottomSheet/index.ts +17 -0
  90. package/cellUtils/index.ts +33 -8
  91. package/colorUtils/__tests__/isTransparentColor.test.ts +76 -0
  92. package/colorUtils/__tests__/isValidColor.test.ts +70 -0
  93. package/colorUtils/index.ts +50 -0
  94. package/configurationUtils/__tests__/modalBlocksParser.test.ts +107 -0
  95. package/configurationUtils/manifestKeyParser.ts +38 -9
  96. package/configurationUtils/modalBlocksParser.ts +178 -0
  97. package/manifestUtils/__tests__/player.audioControls.test.js +158 -0
  98. package/manifestUtils/_internals/index.js +6 -0
  99. package/manifestUtils/defaultManifestConfigurations/generalContent.js +35 -0
  100. package/manifestUtils/defaultManifestConfigurations/player.js +327 -19
  101. package/manifestUtils/fieldUtils/__tests__/fieldUtils.test.js +84 -0
  102. package/manifestUtils/fieldUtils/index.js +125 -0
  103. package/manifestUtils/index.js +3 -0
  104. package/manifestUtils/keys.js +9 -0
  105. package/manifestUtils/mobileAction/button/index.js +16 -0
  106. package/manifestUtils/mobileAction/container/index.js +3 -1
  107. package/manifestUtils/mobileAction/groups/defaults.js +3 -1
  108. package/manifestUtils/modalBlocks.js +304 -0
  109. package/manifestUtils/platformIsTV.js +1 -0
  110. package/modalState/ContentViewModel.ts +113 -0
  111. package/modalState/EditableCollection.ts +17 -0
  112. package/modalState/EditableCollectionRegistry.ts +51 -0
  113. package/modalState/ModalOrchestrator.ts +199 -0
  114. package/modalState/RemoteEditableCollection.ts +227 -0
  115. package/modalState/__tests__/ContentViewModel.editable.test.ts +69 -0
  116. package/modalState/__tests__/ContentViewModel.test.ts +165 -0
  117. package/modalState/__tests__/EditableCollectionRegistry.test.ts +112 -0
  118. package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +47 -0
  119. package/modalState/__tests__/RemoteEditableCollection.test.ts +326 -0
  120. package/modalState/__tests__/useBottomSheetContent.test.ts +349 -0
  121. package/modalState/index.ts +30 -83
  122. package/modalState/store.ts +102 -0
  123. package/modalState/types.ts +133 -0
  124. package/modalState/useBottomSheetContent.ts +102 -0
  125. package/numberUtils/__tests__/isMinusZero.test.ts +20 -0
  126. package/numberUtils/__tests__/isZero.test.ts +27 -0
  127. package/numberUtils/__tests__/requireNumber.test.ts +50 -0
  128. package/numberUtils/__tests__/toNumber.test.ts +27 -0
  129. package/numberUtils/__tests__/toNumberWithDefault.test.ts +64 -0
  130. package/numberUtils/__tests__/toNumberWithDefaultZero.test.ts +48 -0
  131. package/numberUtils/index.ts +27 -8
  132. package/package.json +2 -2
  133. package/pipesUtils/__tests__/buildUrlWithQuery.test.ts +33 -0
  134. package/pipesUtils/__tests__/withPipesEndpoint.test.tsx +56 -0
  135. package/pipesUtils/index.ts +1 -0
  136. package/pipesUtils/withPipesEndpoint.tsx +54 -0
  137. package/playerUtils/__tests__/contentDataKeys.test.ts +297 -0
  138. package/playerUtils/__tests__/resolvePlayerTitleSubtitle.test.ts +180 -0
  139. package/playerUtils/contentDataKeys.ts +134 -0
  140. package/playerUtils/index.ts +41 -14
  141. package/playerUtils/isAudioItem.ts +26 -0
  142. package/playerUtils/resolvePlayerTitleSubtitle.ts +76 -0
  143. package/reactHooks/actions/index.ts +51 -1
  144. package/reactHooks/cell-click/index.ts +18 -10
  145. package/reactHooks/feed/__mocks__/useMarkPipesDataStale.ts +4 -0
  146. package/reactHooks/feed/__tests__/useInflatedUrl.test.tsx +25 -0
  147. package/reactHooks/feed/index.ts +5 -1
  148. package/reactHooks/feed/useBatchLoading.ts +9 -1
  149. package/reactHooks/feed/{usePipesCacheReset.ts → useMarkPipesDataStale.ts} +12 -4
  150. package/reactHooks/index.ts +2 -0
  151. package/reactHooks/layout/index.ts +1 -1
  152. package/reactHooks/navigation/__mocks__/index.ts +4 -0
  153. package/reactHooks/navigation/__tests__/useIsScreenActive.test.ts +42 -0
  154. package/reactHooks/navigation/index.ts +1 -1
  155. package/reactHooks/navigation/useIsScreenActive.ts +29 -8
  156. package/reactHooks/state/useComponentScreenState.ts +1 -1
  157. package/reactHooks/usePluginConfiguration.ts +4 -1
  158. package/reactHooks/utils/__tests__/index.test.js +22 -2
  159. package/reactHooks/utils/index.ts +13 -2
  160. package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +22 -4
  161. package/riverComponetsMeasurementProvider/index.tsx +12 -8
  162. package/stringUtils/__tests__/stringUtils.test.js +42 -0
  163. package/stringUtils/index.ts +2 -2
  164. package/uiActionsRegistrator/index.ts +203 -0
  165. package/zappFrameworkUtils/__tests__/localStorageHelper.test.ts +146 -0
  166. package/zappFrameworkUtils/localStorageHelper.ts +19 -15
  167. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +0 -56
  168. package/reactHooks/feed/__mocks__/usePipesCacheReset.ts +0 -1
  169. /package/reactHooks/actions/__tests__/{index.test.js → index.test.tsx} +0 -0
@@ -0,0 +1,158 @@
1
+ const { player } = require("../defaultManifestConfigurations/player");
2
+
3
+ const manifestFor = (platform) => player({ platform, version: "0.0.0" });
4
+
5
+ const findGroup = (manifest, label) =>
6
+ manifest.styles.fields.find((group) => group.group && group.label === label);
7
+
8
+ const findFieldInGroup = (manifest, label, key) =>
9
+ findGroup(manifest, label)?.fields?.find((field) => field.key === key);
10
+
11
+ const findLocalization = (manifest, key) =>
12
+ manifest.localizations.fields.find((field) => field.key === key);
13
+
14
+ describe("player manifest — audio playlist controls", () => {
15
+ const manifest = manifestFor("ios_for_quickbrick");
16
+
17
+ describe("Player Controls Layout", () => {
18
+ const field = () =>
19
+ findFieldInGroup(manifest, "Audio Player", "player_controls_layout");
20
+
21
+ it("is declared in the Audio Player group", () => {
22
+ expect(field()).toBeDefined();
23
+ });
24
+
25
+ it("is a select defaulting to the existing controls", () => {
26
+ expect(field().type).toBe("select");
27
+ expect(field().initial_value).toBe("playback_speed_and_sleep_timer");
28
+ });
29
+
30
+ it("only shows when the audio player layout is in use", () => {
31
+ expect(field()).toMatchObject({
32
+ rules: "conditional",
33
+ conditional_fields: [
34
+ { key: "styles/full_screen_audio_player", condition_value: true },
35
+ ],
36
+ });
37
+ });
38
+
39
+ it("offers exactly the two documented options", () => {
40
+ expect(field().options).toEqual([
41
+ {
42
+ text: "Playback Speed & Sleep Timer (Default)",
43
+ value: "playback_speed_and_sleep_timer",
44
+ },
45
+ {
46
+ text: "Playlist Controls (Previous & Next)",
47
+ value: "playlist_controls",
48
+ },
49
+ ]);
50
+ });
51
+ });
52
+
53
+ describe("control icons", () => {
54
+ it.each(["previous_track", "next_track"])(
55
+ "declares %s as an uploader",
56
+ (key) => {
57
+ expect(findFieldInGroup(manifest, "Assets", key)).toMatchObject({
58
+ key,
59
+ type: "uploader",
60
+ });
61
+ }
62
+ );
63
+
64
+ it.each(["previous_track", "next_track"])(
65
+ "only shows %s when the playlist controls layout is selected",
66
+ (key) => {
67
+ expect(findFieldInGroup(manifest, "Assets", key)).toMatchObject({
68
+ rules: "conditional",
69
+ conditional_fields: [
70
+ {
71
+ key: "styles/player_controls_layout",
72
+ condition_value: "playlist_controls",
73
+ },
74
+ ],
75
+ });
76
+ }
77
+ );
78
+
79
+ it.each([
80
+ "speed_0_8",
81
+ "speed_1",
82
+ "speed_1_2",
83
+ "speed_1_5",
84
+ "speed_2",
85
+ "sleep_timer",
86
+ "sleep_timer_active",
87
+ ])(
88
+ "only shows %s when the playback speed and sleep timer layout is selected",
89
+ (key) => {
90
+ expect(findFieldInGroup(manifest, "Assets", key)).toMatchObject({
91
+ rules: "conditional",
92
+ conditional_fields: [
93
+ {
94
+ key: "styles/player_controls_layout",
95
+ condition_value: "playback_speed_and_sleep_timer",
96
+ },
97
+ ],
98
+ });
99
+ }
100
+ );
101
+ });
102
+
103
+ describe("accessibility strings", () => {
104
+ it.each([
105
+ "accessibility_previous_track_label",
106
+ "accessibility_previous_track_hint",
107
+ "accessibility_next_track_label",
108
+ "accessibility_next_track_hint",
109
+ ])("declares %s with a default", (key) => {
110
+ expect(findLocalization(manifest, key)).toMatchObject({
111
+ type: "text_input",
112
+ });
113
+
114
+ expect(findLocalization(manifest, key).initial_value).toBeTruthy();
115
+ });
116
+ });
117
+
118
+ describe("tv platforms", () => {
119
+ // The audio player layout is mobile only, so nothing this feature adds —
120
+ // including its screen reader strings — belongs in a tv manifest.
121
+ it.each(["tvos_for_quickbrick", "android_tv_for_quickbrick", "lg_tv"])(
122
+ "adds no audio controls layout field on %s",
123
+ (platform) => {
124
+ expect(
125
+ findFieldInGroup(
126
+ manifestFor(platform),
127
+ "Audio Player",
128
+ "player_controls_layout"
129
+ )
130
+ ).toBeUndefined();
131
+ }
132
+ );
133
+
134
+ it.each([
135
+ "accessibility_previous_track_label",
136
+ "accessibility_previous_track_hint",
137
+ "accessibility_next_track_label",
138
+ "accessibility_next_track_hint",
139
+ ])("does not declare %s on tv", (key) => {
140
+ expect(
141
+ findLocalization(manifestFor("tvos_for_quickbrick"), key)
142
+ ).toBeUndefined();
143
+ });
144
+
145
+ it("still declares the tv-only localizations", () => {
146
+ expect(
147
+ findLocalization(
148
+ manifestFor("tvos_for_quickbrick"),
149
+ "back_to_live_label"
150
+ )
151
+ ).toBeDefined();
152
+
153
+ expect(
154
+ findLocalization(manifestFor("tvos_for_quickbrick"), "start_over_label")
155
+ ).toBeDefined();
156
+ });
157
+ });
158
+ });
@@ -157,6 +157,10 @@ function generateFieldsFromDefaultsWithoutPrefixedLabel(
157
157
 
158
158
  if (conditions) {
159
159
  generatedField.conditional_fields = conditions.map((condition) => {
160
+ if (condition.section === null) {
161
+ return { key: condition.key, condition_value: condition.value };
162
+ }
163
+
160
164
  const section = condition.section ? `${condition.section}/` : "";
161
165
 
162
166
  return {
@@ -164,6 +168,8 @@ function generateFieldsFromDefaultsWithoutPrefixedLabel(
164
168
  condition_value: condition.value,
165
169
  };
166
170
  });
171
+
172
+ generatedField.rules = "all_conditions";
167
173
  }
168
174
 
169
175
  return generatedField;
@@ -308,6 +308,41 @@ const generalContent = () => ({
308
308
  key: "pull_to_refresh_enabled",
309
309
  initial_value: false,
310
310
  },
311
+ {
312
+ type: "switch",
313
+ label: "Allow using this screen as a hook",
314
+ label_tooltip:
315
+ "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
316
+ key: "available_as_hook",
317
+ initial_value: false,
318
+ },
319
+ {
320
+ type: "data_source_selector",
321
+ label: "Skip hook endpoint",
322
+ key: "skip_hook_endpoint",
323
+ label_tooltip:
324
+ "If set, this endpoint will check with the server whether the hook should be skipped",
325
+ conditional_fields: [
326
+ {
327
+ key: "rules/available_as_hook",
328
+ condition_value: true,
329
+ },
330
+ ],
331
+ },
332
+ {
333
+ key: "skip_hook_storage_key",
334
+ type: "text_input",
335
+ label: "Hook will be skipped if storage key is set",
336
+ initial_value: "",
337
+ label_tooltip:
338
+ "Comma-separated keys in namespace.key format (key without a dot uses the default namespace)", // eslint-disable-line max-len
339
+ conditional_fields: [
340
+ {
341
+ key: "rules/available_as_hook",
342
+ condition_value: true,
343
+ },
344
+ ],
345
+ },
311
346
  ],
312
347
  },
313
348
  });
@@ -383,7 +383,45 @@ function getPlayerConfiguration({ platform, version }) {
383
383
  initial_value: "Start Over",
384
384
  type: "text_input",
385
385
  });
386
+ }
387
+
388
+ if (isMobile(platform)) {
389
+ // Screen reader strings for the audio player's playlist controls. Mobile
390
+ // only, because the audio player layout itself is mobile only.
391
+ localizations.fields.push({
392
+ key: "accessibility_previous_track_label",
393
+ label: "Accessibility previous track label",
394
+ initial_value: "Previous track button",
395
+ label_tooltip: "Label for previous track button accessibility",
396
+ type: "text_input",
397
+ });
398
+
399
+ localizations.fields.push({
400
+ key: "accessibility_previous_track_hint",
401
+ label: "Accessibility previous track hint",
402
+ initial_value: "Press to play the previous track",
403
+ label_tooltip: "Hint for previous track button accessibility",
404
+ type: "text_input",
405
+ });
406
+
407
+ localizations.fields.push({
408
+ key: "accessibility_next_track_label",
409
+ label: "Accessibility next track label",
410
+ initial_value: "Next track button",
411
+ label_tooltip: "Label for next track button accessibility",
412
+ type: "text_input",
413
+ });
386
414
 
415
+ localizations.fields.push({
416
+ key: "accessibility_next_track_hint",
417
+ label: "Accessibility next track hint",
418
+ initial_value: "Press to play the next track",
419
+ label_tooltip: "Hint for next track button accessibility",
420
+ type: "text_input",
421
+ });
422
+ }
423
+
424
+ if (isTV(platform)) {
387
425
  styles.fields.push(
388
426
  fieldsGroup("Always Show Scrub Bar & Timestamp", "", [
389
427
  {
@@ -781,21 +819,6 @@ function getPlayerConfiguration({ platform, version }) {
781
819
  }
782
820
  );
783
821
 
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
822
  if (platform === "android_for_quickbrick") {
800
823
  general.fields.push({
801
824
  key: "stop_playback_on_task_removal",
@@ -1236,6 +1259,13 @@ function getPlayerConfiguration({ platform, version }) {
1236
1259
  type: "uploader",
1237
1260
  label_tooltip: "Playback Speed 0.8x",
1238
1261
  initial_value: null,
1262
+ rules: "conditional",
1263
+ conditional_fields: [
1264
+ {
1265
+ key: "styles/player_controls_layout",
1266
+ condition_value: "playback_speed_and_sleep_timer",
1267
+ },
1268
+ ],
1239
1269
  },
1240
1270
  {
1241
1271
  key: "speed_1",
@@ -1243,6 +1273,13 @@ function getPlayerConfiguration({ platform, version }) {
1243
1273
  type: "uploader",
1244
1274
  label_tooltip: "Playback Speed 1x",
1245
1275
  initial_value: null,
1276
+ rules: "conditional",
1277
+ conditional_fields: [
1278
+ {
1279
+ key: "styles/player_controls_layout",
1280
+ condition_value: "playback_speed_and_sleep_timer",
1281
+ },
1282
+ ],
1246
1283
  },
1247
1284
  {
1248
1285
  key: "speed_1_2",
@@ -1250,6 +1287,13 @@ function getPlayerConfiguration({ platform, version }) {
1250
1287
  type: "uploader",
1251
1288
  label_tooltip: "Playback Speed 1.2x",
1252
1289
  initial_value: null,
1290
+ rules: "conditional",
1291
+ conditional_fields: [
1292
+ {
1293
+ key: "styles/player_controls_layout",
1294
+ condition_value: "playback_speed_and_sleep_timer",
1295
+ },
1296
+ ],
1253
1297
  },
1254
1298
  {
1255
1299
  key: "speed_1_5",
@@ -1257,6 +1301,13 @@ function getPlayerConfiguration({ platform, version }) {
1257
1301
  type: "uploader",
1258
1302
  label_tooltip: "Playback Speed 1.5x",
1259
1303
  initial_value: null,
1304
+ rules: "conditional",
1305
+ conditional_fields: [
1306
+ {
1307
+ key: "styles/player_controls_layout",
1308
+ condition_value: "playback_speed_and_sleep_timer",
1309
+ },
1310
+ ],
1260
1311
  },
1261
1312
  {
1262
1313
  key: "speed_2",
@@ -1264,6 +1315,13 @@ function getPlayerConfiguration({ platform, version }) {
1264
1315
  type: "uploader",
1265
1316
  label_tooltip: "Playback Speed 2x",
1266
1317
  initial_value: null,
1318
+ rules: "conditional",
1319
+ conditional_fields: [
1320
+ {
1321
+ key: "styles/player_controls_layout",
1322
+ condition_value: "playback_speed_and_sleep_timer",
1323
+ },
1324
+ ],
1267
1325
  },
1268
1326
  {
1269
1327
  key: "sleep_timer",
@@ -1271,6 +1329,13 @@ function getPlayerConfiguration({ platform, version }) {
1271
1329
  type: "uploader",
1272
1330
  label_tooltip: "Sleep Timer Default",
1273
1331
  initial_value: null,
1332
+ rules: "conditional",
1333
+ conditional_fields: [
1334
+ {
1335
+ key: "styles/player_controls_layout",
1336
+ condition_value: "playback_speed_and_sleep_timer",
1337
+ },
1338
+ ],
1274
1339
  },
1275
1340
  {
1276
1341
  key: "sleep_timer_active",
@@ -1278,6 +1343,43 @@ function getPlayerConfiguration({ platform, version }) {
1278
1343
  type: "uploader",
1279
1344
  label_tooltip: "Sleep Timer Active",
1280
1345
  initial_value: null,
1346
+ rules: "conditional",
1347
+ conditional_fields: [
1348
+ {
1349
+ key: "styles/player_controls_layout",
1350
+ condition_value: "playback_speed_and_sleep_timer",
1351
+ },
1352
+ ],
1353
+ },
1354
+ {
1355
+ key: "previous_track",
1356
+ label: "Previous Track Control Icon",
1357
+ type: "uploader",
1358
+ label_tooltip:
1359
+ "Override the default Previous Track icon. Recommended size 120x120px (@3x).",
1360
+ initial_value: null,
1361
+ rules: "conditional",
1362
+ conditional_fields: [
1363
+ {
1364
+ key: "styles/player_controls_layout",
1365
+ condition_value: "playlist_controls",
1366
+ },
1367
+ ],
1368
+ },
1369
+ {
1370
+ key: "next_track",
1371
+ label: "Next Track Control Icon",
1372
+ type: "uploader",
1373
+ label_tooltip:
1374
+ "Override the default Next Track icon. Recommended size 120x120px (@3x).",
1375
+ initial_value: null,
1376
+ rules: "conditional",
1377
+ conditional_fields: [
1378
+ {
1379
+ key: "styles/player_controls_layout",
1380
+ condition_value: "playlist_controls",
1381
+ },
1382
+ ],
1281
1383
  },
1282
1384
  ]
1283
1385
  ),
@@ -1294,6 +1396,35 @@ function getPlayerConfiguration({ platform, version }) {
1294
1396
  type: "switch",
1295
1397
  label_tooltip: "Select whether you want to display title in player",
1296
1398
  },
1399
+ {
1400
+ section: "title",
1401
+ key: "title_data_key_source",
1402
+ label: "Title data key",
1403
+ initial_value: "title",
1404
+ type: "select",
1405
+ options: [
1406
+ { text: "Title", value: "title" },
1407
+ { text: "Other", value: "other" },
1408
+ ],
1409
+ label_tooltip:
1410
+ "Select which feed data key provides the player content title. Keep 'Title' to use the entry title",
1411
+ },
1412
+ {
1413
+ section: "title",
1414
+ key: "title_custom_data_key",
1415
+ label: "Custom title data key",
1416
+ initial_value: "",
1417
+ type: "text_input",
1418
+ rules: "conditional",
1419
+ conditional_fields: [
1420
+ {
1421
+ key: "styles/title_data_key_source",
1422
+ condition_value: "other",
1423
+ },
1424
+ ],
1425
+ label_tooltip:
1426
+ "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",
1427
+ },
1297
1428
  {
1298
1429
  section: "title",
1299
1430
  key: "title_font_android",
@@ -1701,6 +1832,35 @@ function getPlayerConfiguration({ platform, version }) {
1701
1832
  label_tooltip:
1702
1833
  "Select whether you want to display subtitle in player",
1703
1834
  },
1835
+ {
1836
+ section: "subtitle",
1837
+ key: "subtitle_data_key_source",
1838
+ label: "Subtitle data key",
1839
+ initial_value: "summary",
1840
+ type: "select",
1841
+ options: [
1842
+ { text: "Subtitle", value: "summary" },
1843
+ { text: "Other", value: "other" },
1844
+ ],
1845
+ label_tooltip:
1846
+ "Select which feed data key provides the player content subtitle. Keep 'Subtitle' to use the entry summary",
1847
+ },
1848
+ {
1849
+ section: "subtitle",
1850
+ key: "subtitle_custom_data_key",
1851
+ label: "Custom subtitle data key",
1852
+ initial_value: "",
1853
+ type: "text_input",
1854
+ rules: "conditional",
1855
+ conditional_fields: [
1856
+ {
1857
+ key: "styles/subtitle_data_key_source",
1858
+ condition_value: "other",
1859
+ },
1860
+ ],
1861
+ label_tooltip:
1862
+ "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",
1863
+ },
1704
1864
  {
1705
1865
  section: "subtitle",
1706
1866
  key: "subtitle_font_android",
@@ -2415,6 +2575,21 @@ function getPlayerConfiguration({ platform, version }) {
2415
2575
  );
2416
2576
  }
2417
2577
 
2578
+ if (
2579
+ platform.includes("android") ||
2580
+ platform.includes("amazon") ||
2581
+ platform.includes("ios") ||
2582
+ platform.includes("tvos")
2583
+ ) {
2584
+ general.fields.push({
2585
+ key: "user_agent",
2586
+ label: "User agent",
2587
+ initial_value: "",
2588
+ type: "text_input",
2589
+ label_tooltip: "Override default user agent string",
2590
+ });
2591
+ }
2592
+
2418
2593
  compact([
2419
2594
  {
2420
2595
  key: "liveCatchUpEnabled",
@@ -2850,6 +3025,33 @@ function getPlayerConfiguration({ platform, version }) {
2850
3025
  "Video Player - Title Text",
2851
3026
  "Video Player Title Text Label Styles",
2852
3027
  [
3028
+ {
3029
+ key: "video_player_title_data_key_source",
3030
+ label: "Video title data key",
3031
+ initial_value: "title",
3032
+ type: "select",
3033
+ options: [
3034
+ { text: "Title", value: "title" },
3035
+ { text: "Other", value: "other" },
3036
+ ],
3037
+ label_tooltip:
3038
+ "Select which feed data key provides the video player title. Keep 'Title' to use the entry title",
3039
+ },
3040
+ {
3041
+ key: "video_player_title_custom_data_key",
3042
+ label: "Custom video title data key",
3043
+ initial_value: "",
3044
+ type: "text_input",
3045
+ rules: "conditional",
3046
+ conditional_fields: [
3047
+ {
3048
+ key: "styles/video_player_title_data_key_source",
3049
+ condition_value: "other",
3050
+ },
3051
+ ],
3052
+ label_tooltip:
3053
+ "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",
3054
+ },
2853
3055
  {
2854
3056
  key: "player_title_color",
2855
3057
  label: "Font color",
@@ -2935,6 +3137,33 @@ function getPlayerConfiguration({ platform, version }) {
2935
3137
  "Video Player - Summary Text",
2936
3138
  "Video Player Summary Text Label Styles",
2937
3139
  [
3140
+ {
3141
+ key: "video_player_summary_data_key_source",
3142
+ label: "Video summary data key",
3143
+ initial_value: "summary",
3144
+ type: "select",
3145
+ options: [
3146
+ { text: "Summary", value: "summary" },
3147
+ { text: "Other", value: "other" },
3148
+ ],
3149
+ label_tooltip:
3150
+ "Select which feed data key provides the video player summary. Keep 'Summary' to use the entry summary",
3151
+ },
3152
+ {
3153
+ key: "video_player_summary_custom_data_key",
3154
+ label: "Custom video summary data key",
3155
+ initial_value: "",
3156
+ type: "text_input",
3157
+ rules: "conditional",
3158
+ conditional_fields: [
3159
+ {
3160
+ key: "styles/video_player_summary_data_key_source",
3161
+ condition_value: "other",
3162
+ },
3163
+ ],
3164
+ label_tooltip:
3165
+ "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",
3166
+ },
2938
3167
  {
2939
3168
  key: "player_summary_color",
2940
3169
  label: "Font color",
@@ -3020,6 +3249,33 @@ function getPlayerConfiguration({ platform, version }) {
3020
3249
  "Audio Player - Title Text",
3021
3250
  "Audio Player Title Text Label Styles",
3022
3251
  [
3252
+ {
3253
+ key: "audio_player_title_data_key_source",
3254
+ label: "Audio title data key",
3255
+ initial_value: "title",
3256
+ type: "select",
3257
+ options: [
3258
+ { text: "Title", value: "title" },
3259
+ { text: "Other", value: "other" },
3260
+ ],
3261
+ label_tooltip:
3262
+ "Select which feed data key provides the audio player title. Keep 'Title' to use the entry title",
3263
+ },
3264
+ {
3265
+ key: "audio_player_title_custom_data_key",
3266
+ label: "Custom audio title data key",
3267
+ initial_value: "",
3268
+ type: "text_input",
3269
+ rules: "conditional",
3270
+ conditional_fields: [
3271
+ {
3272
+ key: "styles/audio_player_title_data_key_source",
3273
+ condition_value: "other",
3274
+ },
3275
+ ],
3276
+ label_tooltip:
3277
+ "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",
3278
+ },
3023
3279
  {
3024
3280
  key: "audio_player_title_color",
3025
3281
  label: "Audio player title color",
@@ -3105,6 +3361,33 @@ function getPlayerConfiguration({ platform, version }) {
3105
3361
  "Audio Player - Summary Text",
3106
3362
  "Audio Player Summary Text Label Styles",
3107
3363
  [
3364
+ {
3365
+ key: "audio_player_summary_data_key_source",
3366
+ label: "Audio summary data key",
3367
+ initial_value: "summary",
3368
+ type: "select",
3369
+ options: [
3370
+ { text: "Summary", value: "summary" },
3371
+ { text: "Other", value: "other" },
3372
+ ],
3373
+ label_tooltip:
3374
+ "Select which feed data key provides the audio player summary. Keep 'Summary' to use the entry summary",
3375
+ },
3376
+ {
3377
+ key: "audio_player_summary_custom_data_key",
3378
+ label: "Custom audio summary data key",
3379
+ initial_value: "",
3380
+ type: "text_input",
3381
+ rules: "conditional",
3382
+ conditional_fields: [
3383
+ {
3384
+ key: "styles/audio_player_summary_data_key_source",
3385
+ condition_value: "other",
3386
+ },
3387
+ ],
3388
+ label_tooltip:
3389
+ "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",
3390
+ },
3108
3391
  {
3109
3392
  key: "audio_player_summary_color",
3110
3393
  label: "Audio player summary color",
@@ -3451,6 +3734,31 @@ function getPlayerConfiguration({ platform, version }) {
3451
3734
  },
3452
3735
  ],
3453
3736
  },
3737
+ {
3738
+ key: "player_controls_layout",
3739
+ label: "Player Controls Layout",
3740
+ type: "select",
3741
+ initial_value: "playback_speed_and_sleep_timer",
3742
+ label_tooltip:
3743
+ "Choose which secondary controls appear in the audio player transport row.",
3744
+ rules: "conditional",
3745
+ conditional_fields: [
3746
+ {
3747
+ key: "styles/full_screen_audio_player",
3748
+ condition_value: true,
3749
+ },
3750
+ ],
3751
+ options: [
3752
+ {
3753
+ text: "Playback Speed & Sleep Timer (Default)",
3754
+ value: "playback_speed_and_sleep_timer",
3755
+ },
3756
+ {
3757
+ text: "Playlist Controls (Previous & Next)",
3758
+ value: "playlist_controls",
3759
+ },
3760
+ ],
3761
+ },
3454
3762
  {
3455
3763
  key: "audio_player_artwork_shadow_enabled",
3456
3764
  label: "Image Shadow Enabled",
@@ -3522,13 +3830,13 @@ function getPlayerConfiguration({ platform, version }) {
3522
3830
  label: "Advanced Player Styles",
3523
3831
  type: "text_input",
3524
3832
  multiline: true,
3525
- initial_value: `.vjs-text-track-cue {
3526
- inset: unset !important;
3527
- top: 94% !important;
3833
+ initial_value: `.vjs-text-track-cue {
3834
+ top: auto !important;
3528
3835
  bottom: 0 !important;
3529
3836
  left: 0 !important;
3530
- right: 0 !important;
3837
+ right: auto !important;
3531
3838
  width: 100% !important;
3839
+ height: auto !important;
3532
3840
  }`,
3533
3841
  label_tooltip:
3534
3842
  "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.",