@applicaster/zapp-react-native-utils 16.0.0-alpha.7845362739 → 16.0.0-alpha.8101951023

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 (120) hide show
  1. package/actionsExecutor/ActionExecutor.ts +50 -3
  2. package/actionsExecutor/ActionExecutorContext.tsx +99 -17
  3. package/actionsExecutor/__tests__/ActionExecutor.test.ts +71 -0
  4. package/actionsExecutor/__tests__/feedDecorator.test.ts +61 -0
  5. package/actionsExecutor/actions/__tests__/dismissBottomSheet.test.ts +25 -0
  6. package/actionsExecutor/actions/__tests__/goBack.test.ts +62 -0
  7. package/actionsExecutor/actions/__tests__/goHome.test.ts +19 -0
  8. package/actionsExecutor/actions/__tests__/loadItemsFromSource.test.ts +88 -0
  9. package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +133 -0
  10. package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +76 -0
  11. package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +212 -0
  12. package/actionsExecutor/actions/__tests__/presentScreen.test.ts +127 -0
  13. package/actionsExecutor/actions/__tests__/showAlert.test.ts +49 -0
  14. package/actionsExecutor/actions/__tests__/showToast.test.ts +88 -0
  15. package/actionsExecutor/actions/dismissBottomSheet.ts +23 -0
  16. package/actionsExecutor/actions/goBack.ts +60 -0
  17. package/actionsExecutor/actions/goHome.ts +31 -0
  18. package/actionsExecutor/actions/index.ts +12 -0
  19. package/actionsExecutor/actions/navigateToScreen.ts +18 -4
  20. package/actionsExecutor/actions/openBottomSheet.ts +329 -0
  21. package/actionsExecutor/actions/presentScreen.ts +78 -0
  22. package/actionsExecutor/actions/refreshComponent.ts +6 -0
  23. package/actionsExecutor/actions/sendCloudEvent.ts +6 -1
  24. package/actionsExecutor/actions/showAlert.ts +51 -0
  25. package/actionsExecutor/actions/showToast.ts +64 -8
  26. package/actionsExecutor/consts.ts +27 -0
  27. package/actionsExecutor/feedDecorator.ts +6 -6
  28. package/analyticsUtils/PlayerAnalyticsManager.ts +1 -1
  29. package/appUtils/HooksManager/index.ts +9 -0
  30. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +142 -59
  31. package/appUtils/playerManager/OverlayObserver/__tests__/liveContent.test.ts +205 -0
  32. package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +56 -0
  33. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.android.tv.ts +4 -0
  34. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ios.tv.ts +4 -0
  35. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +11 -0
  36. package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.web.ts +4 -0
  37. package/appUtils/playerManager/OverlayObserver/utils.ts +51 -20
  38. package/appUtils/playerManager/__tests__/playerContent.test.ts +403 -0
  39. package/appUtils/playerManager/__tests__/playerFactory.test.ts +1 -1
  40. package/appUtils/playerManager/__tests__/playerNative.test.ts +38 -0
  41. package/appUtils/playerManager/__tests__/usePlayerContent.test.tsx +64 -0
  42. package/appUtils/playerManager/{conts.ts → const.ts} +20 -0
  43. package/appUtils/playerManager/index.ts +1 -1
  44. package/appUtils/playerManager/player.ts +115 -3
  45. package/appUtils/playerManager/playerFactory.ts +1 -1
  46. package/appUtils/playerManager/playerNative.ts +6 -4
  47. package/appUtils/playerManager/usePlayerContent.tsx +43 -0
  48. package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -1
  49. package/appUtils/playerManager/userLanguagePreference.ts +1 -1
  50. package/arrayUtils/__tests__/reorderByIds.test.ts +50 -0
  51. package/arrayUtils/index.ts +79 -0
  52. package/cellUtils/index.ts +30 -3
  53. package/configurationUtils/__tests__/manifestKeyParser.test.ts +21 -0
  54. package/configurationUtils/__tests__/modalBlocksParser.test.ts +269 -0
  55. package/configurationUtils/manifestKeyParser.ts +50 -10
  56. package/configurationUtils/modalBlocksParser.ts +214 -0
  57. package/entryActions/__tests__/buildEntryActions.test.ts +235 -0
  58. package/entryActions/aliasPresentation.ts +108 -0
  59. package/entryActions/buildEntryActions.ts +167 -0
  60. package/entryActions/index.ts +15 -0
  61. package/entryActions/types.ts +39 -0
  62. package/manifestUtils/__tests__/player.audioControls.test.js +158 -0
  63. package/manifestUtils/_internals/index.js +8 -3
  64. package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
  65. package/manifestUtils/defaultManifestConfigurations/player.js +330 -1
  66. package/manifestUtils/index.js +3 -0
  67. package/manifestUtils/modalBlocks.js +304 -0
  68. package/modalState/ContentViewModel.ts +113 -0
  69. package/modalState/EditableCollection.ts +17 -0
  70. package/modalState/EditableCollectionRegistry.ts +51 -0
  71. package/modalState/ModalOrchestrator.ts +211 -0
  72. package/modalState/RemoteEditableCollection.ts +235 -0
  73. package/modalState/__tests__/ContentViewModel.editable.test.ts +69 -0
  74. package/modalState/__tests__/ContentViewModel.test.ts +165 -0
  75. package/modalState/__tests__/EditableCollectionRegistry.test.ts +112 -0
  76. package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +47 -0
  77. package/modalState/__tests__/RemoteEditableCollection.test.ts +415 -0
  78. package/modalState/__tests__/index.test.ts +30 -1
  79. package/modalState/__tests__/useBottomSheetContent.test.ts +349 -0
  80. package/modalState/__tests__/useModalStoreState.test.ts +142 -0
  81. package/modalState/index.ts +35 -83
  82. package/modalState/store.ts +109 -0
  83. package/modalState/types.ts +160 -0
  84. package/modalState/useBottomSheetContent.ts +109 -0
  85. package/package.json +2 -2
  86. package/playerUtils/__tests__/contentDataKeys.test.ts +297 -0
  87. package/playerUtils/__tests__/resolvePlayerActions.test.ts +138 -0
  88. package/playerUtils/__tests__/resolvePlayerTitleSubtitle.test.ts +180 -0
  89. package/playerUtils/contentDataKeys.ts +134 -0
  90. package/playerUtils/index.ts +37 -16
  91. package/playerUtils/isAudioItem.ts +26 -0
  92. package/playerUtils/resolvePlayerActions.ts +71 -0
  93. package/playerUtils/resolvePlayerTitleSubtitle.ts +76 -0
  94. package/pluginUtils/__tests__/pluginUtils.test.js +32 -0
  95. package/pluginUtils/index.ts +29 -0
  96. package/reactHooks/actions/index.ts +67 -2
  97. package/reactHooks/cell-click/__tests__/index.test.js +64 -0
  98. package/reactHooks/cell-click/index.ts +21 -14
  99. package/reactHooks/index.ts +2 -0
  100. package/reactHooks/layout/index.ts +1 -1
  101. package/reactHooks/navigation/__tests__/index.test.tsx +176 -1
  102. package/reactHooks/navigation/__tests__/usePresentScreen.test.ts +154 -0
  103. package/reactHooks/navigation/index.ts +2 -0
  104. package/reactHooks/navigation/usePresentScreen.ts +157 -0
  105. package/reactHooks/navigation/useRoute.ts +22 -5
  106. package/reactHooks/usePluginConfiguration.ts +4 -1
  107. package/reactHooks/utils/__tests__/index.test.js +22 -2
  108. package/reactHooks/utils/index.ts +11 -1
  109. package/uiActionsRegistrator/__tests__/resolveActionIdentifiers.test.ts +93 -0
  110. package/uiActionsRegistrator/__tests__/subscribe.test.ts +107 -0
  111. package/uiActionsRegistrator/__tests__/usableActionItems.test.ts +52 -0
  112. package/uiActionsRegistrator/index.ts +25 -203
  113. package/uiActionsRegistrator/registry.ts +335 -0
  114. package/uiActionsRegistrator/resolveActionIdentifiers.ts +100 -0
  115. package/uiActionsRegistrator/usableActionItems.ts +66 -0
  116. package/zappFrameworkUtils/localStorageHelper.ts +10 -2
  117. package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +0 -56
  118. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +0 -54
  119. package/playerUtils/getPlayerActionButtons.ts +0 -17
  120. /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
+ });
@@ -135,7 +135,8 @@ function generateFieldsFromDefaults(label, defaults, fields) {
135
135
  function generateFieldsFromDefaultsWithoutPrefixedLabel(
136
136
  label,
137
137
  defaults,
138
- fields
138
+ fields,
139
+ { withStyle = false } = {}
139
140
  ) {
140
141
  const keyPrefix = toSnakeCase(label);
141
142
 
@@ -151,7 +152,9 @@ function generateFieldsFromDefaultsWithoutPrefixedLabel(
151
152
  const generatedField = {
152
153
  ...fieldProps,
153
154
  label: capitalizeFirstLetter(field.suffix),
154
- key: `${keyPrefix}_${toSnakeCase(field.suffix)}`,
155
+ key: withStyle
156
+ ? `${keyPrefix}_style_${toSnakeCase(field.suffix)}`
157
+ : `${keyPrefix}_${toSnakeCase(field.suffix)}`,
155
158
  initial_value: R.when(R.is(Array), R.head)(initialValue),
156
159
  };
157
160
 
@@ -164,7 +167,9 @@ function generateFieldsFromDefaultsWithoutPrefixedLabel(
164
167
  const section = condition.section ? `${condition.section}/` : "";
165
168
 
166
169
  return {
167
- key: `${section}${keyPrefix}_${toSnakeCase(condition.key)}`,
170
+ key: withStyle
171
+ ? `${section}${keyPrefix}_style_${toSnakeCase(condition.key)}`
172
+ : `${section}${keyPrefix}_${toSnakeCase(condition.key)}`,
168
173
  condition_value: condition.value,
169
174
  };
170
175
  });
@@ -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: {
@@ -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
+ });
386
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
+ }
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
  {
@@ -1053,6 +1105,13 @@ function getPlayerConfiguration({ platform, version }) {
1053
1105
  type: "uploader",
1054
1106
  label_tooltip: "Override default close badge / icon",
1055
1107
  },
1108
+ {
1109
+ key: "overflow",
1110
+ label: "Overflow badge",
1111
+ type: "uploader",
1112
+ label_tooltip:
1113
+ "Override default overflow (…) badge / icon, shown when more action buttons resolve than fit on the player overlay",
1114
+ },
1056
1115
  {
1057
1116
  key: "lock",
1058
1117
  label: "Lock player badge",
@@ -1221,6 +1280,13 @@ function getPlayerConfiguration({ platform, version }) {
1221
1280
  type: "uploader",
1222
1281
  label_tooltip: "Playback Speed 0.8x",
1223
1282
  initial_value: null,
1283
+ rules: "conditional",
1284
+ conditional_fields: [
1285
+ {
1286
+ key: "styles/player_controls_layout",
1287
+ condition_value: "playback_speed_and_sleep_timer",
1288
+ },
1289
+ ],
1224
1290
  },
1225
1291
  {
1226
1292
  key: "speed_1",
@@ -1228,6 +1294,13 @@ function getPlayerConfiguration({ platform, version }) {
1228
1294
  type: "uploader",
1229
1295
  label_tooltip: "Playback Speed 1x",
1230
1296
  initial_value: null,
1297
+ rules: "conditional",
1298
+ conditional_fields: [
1299
+ {
1300
+ key: "styles/player_controls_layout",
1301
+ condition_value: "playback_speed_and_sleep_timer",
1302
+ },
1303
+ ],
1231
1304
  },
1232
1305
  {
1233
1306
  key: "speed_1_2",
@@ -1235,6 +1308,13 @@ function getPlayerConfiguration({ platform, version }) {
1235
1308
  type: "uploader",
1236
1309
  label_tooltip: "Playback Speed 1.2x",
1237
1310
  initial_value: null,
1311
+ rules: "conditional",
1312
+ conditional_fields: [
1313
+ {
1314
+ key: "styles/player_controls_layout",
1315
+ condition_value: "playback_speed_and_sleep_timer",
1316
+ },
1317
+ ],
1238
1318
  },
1239
1319
  {
1240
1320
  key: "speed_1_5",
@@ -1242,6 +1322,13 @@ function getPlayerConfiguration({ platform, version }) {
1242
1322
  type: "uploader",
1243
1323
  label_tooltip: "Playback Speed 1.5x",
1244
1324
  initial_value: null,
1325
+ rules: "conditional",
1326
+ conditional_fields: [
1327
+ {
1328
+ key: "styles/player_controls_layout",
1329
+ condition_value: "playback_speed_and_sleep_timer",
1330
+ },
1331
+ ],
1245
1332
  },
1246
1333
  {
1247
1334
  key: "speed_2",
@@ -1249,6 +1336,13 @@ function getPlayerConfiguration({ platform, version }) {
1249
1336
  type: "uploader",
1250
1337
  label_tooltip: "Playback Speed 2x",
1251
1338
  initial_value: null,
1339
+ rules: "conditional",
1340
+ conditional_fields: [
1341
+ {
1342
+ key: "styles/player_controls_layout",
1343
+ condition_value: "playback_speed_and_sleep_timer",
1344
+ },
1345
+ ],
1252
1346
  },
1253
1347
  {
1254
1348
  key: "sleep_timer",
@@ -1256,6 +1350,13 @@ function getPlayerConfiguration({ platform, version }) {
1256
1350
  type: "uploader",
1257
1351
  label_tooltip: "Sleep Timer Default",
1258
1352
  initial_value: null,
1353
+ rules: "conditional",
1354
+ conditional_fields: [
1355
+ {
1356
+ key: "styles/player_controls_layout",
1357
+ condition_value: "playback_speed_and_sleep_timer",
1358
+ },
1359
+ ],
1259
1360
  },
1260
1361
  {
1261
1362
  key: "sleep_timer_active",
@@ -1263,6 +1364,43 @@ function getPlayerConfiguration({ platform, version }) {
1263
1364
  type: "uploader",
1264
1365
  label_tooltip: "Sleep Timer Active",
1265
1366
  initial_value: null,
1367
+ rules: "conditional",
1368
+ conditional_fields: [
1369
+ {
1370
+ key: "styles/player_controls_layout",
1371
+ condition_value: "playback_speed_and_sleep_timer",
1372
+ },
1373
+ ],
1374
+ },
1375
+ {
1376
+ key: "previous_track",
1377
+ label: "Previous Track Control Icon",
1378
+ type: "uploader",
1379
+ label_tooltip:
1380
+ "Override the default Previous Track icon. Recommended size 120x120px (@3x).",
1381
+ initial_value: null,
1382
+ rules: "conditional",
1383
+ conditional_fields: [
1384
+ {
1385
+ key: "styles/player_controls_layout",
1386
+ condition_value: "playlist_controls",
1387
+ },
1388
+ ],
1389
+ },
1390
+ {
1391
+ key: "next_track",
1392
+ label: "Next Track Control Icon",
1393
+ type: "uploader",
1394
+ label_tooltip:
1395
+ "Override the default Next Track icon. Recommended size 120x120px (@3x).",
1396
+ initial_value: null,
1397
+ rules: "conditional",
1398
+ conditional_fields: [
1399
+ {
1400
+ key: "styles/player_controls_layout",
1401
+ condition_value: "playlist_controls",
1402
+ },
1403
+ ],
1266
1404
  },
1267
1405
  ]
1268
1406
  ),
@@ -1279,6 +1417,35 @@ function getPlayerConfiguration({ platform, version }) {
1279
1417
  type: "switch",
1280
1418
  label_tooltip: "Select whether you want to display title in player",
1281
1419
  },
1420
+ {
1421
+ section: "title",
1422
+ key: "title_data_key_source",
1423
+ label: "Title data key",
1424
+ initial_value: "title",
1425
+ type: "select",
1426
+ options: [
1427
+ { text: "Title", value: "title" },
1428
+ { text: "Other", value: "other" },
1429
+ ],
1430
+ label_tooltip:
1431
+ "Select which feed data key provides the player content title. Keep 'Title' to use the entry title",
1432
+ },
1433
+ {
1434
+ section: "title",
1435
+ key: "title_custom_data_key",
1436
+ label: "Custom title data key",
1437
+ initial_value: "",
1438
+ type: "text_input",
1439
+ rules: "conditional",
1440
+ conditional_fields: [
1441
+ {
1442
+ key: "styles/title_data_key_source",
1443
+ condition_value: "other",
1444
+ },
1445
+ ],
1446
+ label_tooltip:
1447
+ "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",
1448
+ },
1282
1449
  {
1283
1450
  section: "title",
1284
1451
  key: "title_font_android",
@@ -1686,6 +1853,35 @@ function getPlayerConfiguration({ platform, version }) {
1686
1853
  label_tooltip:
1687
1854
  "Select whether you want to display subtitle in player",
1688
1855
  },
1856
+ {
1857
+ section: "subtitle",
1858
+ key: "subtitle_data_key_source",
1859
+ label: "Subtitle data key",
1860
+ initial_value: "summary",
1861
+ type: "select",
1862
+ options: [
1863
+ { text: "Subtitle", value: "summary" },
1864
+ { text: "Other", value: "other" },
1865
+ ],
1866
+ label_tooltip:
1867
+ "Select which feed data key provides the player content subtitle. Keep 'Subtitle' to use the entry summary",
1868
+ },
1869
+ {
1870
+ section: "subtitle",
1871
+ key: "subtitle_custom_data_key",
1872
+ label: "Custom subtitle data key",
1873
+ initial_value: "",
1874
+ type: "text_input",
1875
+ rules: "conditional",
1876
+ conditional_fields: [
1877
+ {
1878
+ key: "styles/subtitle_data_key_source",
1879
+ condition_value: "other",
1880
+ },
1881
+ ],
1882
+ label_tooltip:
1883
+ "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",
1884
+ },
1689
1885
  {
1690
1886
  section: "subtitle",
1691
1887
  key: "subtitle_font_android",
@@ -2850,6 +3046,33 @@ function getPlayerConfiguration({ platform, version }) {
2850
3046
  "Video Player - Title Text",
2851
3047
  "Video Player Title Text Label Styles",
2852
3048
  [
3049
+ {
3050
+ key: "video_player_title_data_key_source",
3051
+ label: "Video title data key",
3052
+ initial_value: "title",
3053
+ type: "select",
3054
+ options: [
3055
+ { text: "Title", value: "title" },
3056
+ { text: "Other", value: "other" },
3057
+ ],
3058
+ label_tooltip:
3059
+ "Select which feed data key provides the video player title. Keep 'Title' to use the entry title",
3060
+ },
3061
+ {
3062
+ key: "video_player_title_custom_data_key",
3063
+ label: "Custom video title data key",
3064
+ initial_value: "",
3065
+ type: "text_input",
3066
+ rules: "conditional",
3067
+ conditional_fields: [
3068
+ {
3069
+ key: "styles/video_player_title_data_key_source",
3070
+ condition_value: "other",
3071
+ },
3072
+ ],
3073
+ label_tooltip:
3074
+ "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",
3075
+ },
2853
3076
  {
2854
3077
  key: "player_title_color",
2855
3078
  label: "Font color",
@@ -2935,6 +3158,33 @@ function getPlayerConfiguration({ platform, version }) {
2935
3158
  "Video Player - Summary Text",
2936
3159
  "Video Player Summary Text Label Styles",
2937
3160
  [
3161
+ {
3162
+ key: "video_player_summary_data_key_source",
3163
+ label: "Video summary data key",
3164
+ initial_value: "summary",
3165
+ type: "select",
3166
+ options: [
3167
+ { text: "Summary", value: "summary" },
3168
+ { text: "Other", value: "other" },
3169
+ ],
3170
+ label_tooltip:
3171
+ "Select which feed data key provides the video player summary. Keep 'Summary' to use the entry summary",
3172
+ },
3173
+ {
3174
+ key: "video_player_summary_custom_data_key",
3175
+ label: "Custom video summary data key",
3176
+ initial_value: "",
3177
+ type: "text_input",
3178
+ rules: "conditional",
3179
+ conditional_fields: [
3180
+ {
3181
+ key: "styles/video_player_summary_data_key_source",
3182
+ condition_value: "other",
3183
+ },
3184
+ ],
3185
+ label_tooltip:
3186
+ "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",
3187
+ },
2938
3188
  {
2939
3189
  key: "player_summary_color",
2940
3190
  label: "Font color",
@@ -3020,6 +3270,33 @@ function getPlayerConfiguration({ platform, version }) {
3020
3270
  "Audio Player - Title Text",
3021
3271
  "Audio Player Title Text Label Styles",
3022
3272
  [
3273
+ {
3274
+ key: "audio_player_title_data_key_source",
3275
+ label: "Audio title data key",
3276
+ initial_value: "title",
3277
+ type: "select",
3278
+ options: [
3279
+ { text: "Title", value: "title" },
3280
+ { text: "Other", value: "other" },
3281
+ ],
3282
+ label_tooltip:
3283
+ "Select which feed data key provides the audio player title. Keep 'Title' to use the entry title",
3284
+ },
3285
+ {
3286
+ key: "audio_player_title_custom_data_key",
3287
+ label: "Custom audio title data key",
3288
+ initial_value: "",
3289
+ type: "text_input",
3290
+ rules: "conditional",
3291
+ conditional_fields: [
3292
+ {
3293
+ key: "styles/audio_player_title_data_key_source",
3294
+ condition_value: "other",
3295
+ },
3296
+ ],
3297
+ label_tooltip:
3298
+ "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",
3299
+ },
3023
3300
  {
3024
3301
  key: "audio_player_title_color",
3025
3302
  label: "Audio player title color",
@@ -3105,6 +3382,33 @@ function getPlayerConfiguration({ platform, version }) {
3105
3382
  "Audio Player - Summary Text",
3106
3383
  "Audio Player Summary Text Label Styles",
3107
3384
  [
3385
+ {
3386
+ key: "audio_player_summary_data_key_source",
3387
+ label: "Audio summary data key",
3388
+ initial_value: "summary",
3389
+ type: "select",
3390
+ options: [
3391
+ { text: "Summary", value: "summary" },
3392
+ { text: "Other", value: "other" },
3393
+ ],
3394
+ label_tooltip:
3395
+ "Select which feed data key provides the audio player summary. Keep 'Summary' to use the entry summary",
3396
+ },
3397
+ {
3398
+ key: "audio_player_summary_custom_data_key",
3399
+ label: "Custom audio summary data key",
3400
+ initial_value: "",
3401
+ type: "text_input",
3402
+ rules: "conditional",
3403
+ conditional_fields: [
3404
+ {
3405
+ key: "styles/audio_player_summary_data_key_source",
3406
+ condition_value: "other",
3407
+ },
3408
+ ],
3409
+ label_tooltip:
3410
+ "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",
3411
+ },
3108
3412
  {
3109
3413
  key: "audio_player_summary_color",
3110
3414
  label: "Audio player summary color",
@@ -3451,6 +3755,31 @@ function getPlayerConfiguration({ platform, version }) {
3451
3755
  },
3452
3756
  ],
3453
3757
  },
3758
+ {
3759
+ key: "player_controls_layout",
3760
+ label: "Player Controls Layout",
3761
+ type: "select",
3762
+ initial_value: "playback_speed_and_sleep_timer",
3763
+ label_tooltip:
3764
+ "Choose which secondary controls appear in the audio player transport row.",
3765
+ rules: "conditional",
3766
+ conditional_fields: [
3767
+ {
3768
+ key: "styles/full_screen_audio_player",
3769
+ condition_value: true,
3770
+ },
3771
+ ],
3772
+ options: [
3773
+ {
3774
+ text: "Playback Speed & Sleep Timer (Default)",
3775
+ value: "playback_speed_and_sleep_timer",
3776
+ },
3777
+ {
3778
+ text: "Playlist Controls (Previous & Next)",
3779
+ value: "playlist_controls",
3780
+ },
3781
+ ],
3782
+ },
3454
3783
  {
3455
3784
  key: "audio_player_artwork_shadow_enabled",
3456
3785
  label: "Image Shadow Enabled",
@@ -24,6 +24,7 @@ const {
24
24
  const { buttonKey } = require("./buttons");
25
25
  const { tvBadges } = require("./tvBadges");
26
26
  const { mobileProgressBar, tvProgressBar } = require("./progressBar");
27
+ const { getModalBlockStyles, getModalLocalizations } = require("./modalBlocks");
27
28
 
28
29
  const {
29
30
  secondaryImage,
@@ -54,4 +55,6 @@ module.exports = {
54
55
  getUpdatedSecondaryImageKeys,
55
56
  DEFAULT_GRADIENT_IMAGE,
56
57
  compact,
58
+ getModalBlockStyles,
59
+ getModalLocalizations,
57
60
  };