@applicaster/zapp-react-native-utils 15.1.7 → 15.1.9-alpha.3547421944
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actionsExecutor/__tests__/feedDecorator.test.ts +61 -0
- package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +133 -0
- package/actionsExecutor/actions/navigateToScreen.ts +18 -4
- package/actionsExecutor/feedDecorator.ts +6 -6
- package/appUtils/HooksManager/index.ts +9 -0
- package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +127 -46
- package/appUtils/playerManager/OverlayObserver/__tests__/liveContent.test.ts +205 -0
- package/appUtils/playerManager/__tests__/playerContent.test.ts +403 -0
- package/appUtils/playerManager/__tests__/usePlayerContent.test.tsx +64 -0
- package/appUtils/playerManager/player.ts +114 -2
- package/appUtils/playerManager/usePlayerContent.tsx +43 -0
- package/cellUtils/index.ts +30 -3
- package/manifestUtils/defaultManifestConfigurations/player.js +166 -0
- package/package.json +2 -2
- package/playerUtils/__tests__/contentDataKeys.test.ts +297 -0
- package/playerUtils/__tests__/resolvePlayerTitleSubtitle.test.ts +180 -0
- package/playerUtils/contentDataKeys.ts +134 -0
- package/playerUtils/index.ts +20 -15
- package/playerUtils/isAudioItem.ts +26 -0
- package/playerUtils/resolvePlayerTitleSubtitle.ts +76 -0
- package/reactHooks/cell-click/index.ts +2 -11
- package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +0 -56
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
-
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
import { BehaviorSubject, Observable } from "rxjs";
|
|
3
|
+
import { distinctUntilChanged } from "rxjs/operators";
|
|
3
4
|
import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
4
5
|
import { createLogger, utilsLogger } from "../../logger";
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getBoolFromConfigValue,
|
|
8
|
+
imageSrcFromMediaItem,
|
|
9
|
+
} from "../../configurationUtils";
|
|
10
|
+
// Imported from the leaf modules rather than the `playerUtils` barrel: that
|
|
11
|
+
// barrel imports `playerManager`, so going through it would create a require
|
|
12
|
+
// cycle (playerManager/index -> player -> playerUtils/index -> playerManager).
|
|
13
|
+
import {
|
|
14
|
+
resolvePlayerTitleSubtitle,
|
|
15
|
+
getPlayerContentDataKeys,
|
|
16
|
+
} from "../../playerUtils/resolvePlayerTitleSubtitle";
|
|
17
|
+
import { isAudioItem } from "../../playerUtils/isAudioItem";
|
|
18
|
+
import { isTV } from "../../reactUtils";
|
|
6
19
|
import { PlayerRole, SharedPlayerCallBacksKeys } from "./const";
|
|
7
20
|
import { OverlaysObserver } from "./OverlayObserver/OverlaysObserver";
|
|
8
21
|
import { TVSeekController } from "../../reactHooks/player/TVSeekControlller/TVSeekController";
|
|
@@ -19,6 +32,23 @@ export declare type PlayerControllerListenerData = {
|
|
|
19
32
|
id: string;
|
|
20
33
|
listener: QuickBrickPlayer.SharedPlayerCallBacks;
|
|
21
34
|
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The player's resolved Title/Subtitle text, published by the player the same
|
|
38
|
+
* way duration and progress are. Consumers render these values as-is rather
|
|
39
|
+
* than reading `entry` or the plugin configuration themselves.
|
|
40
|
+
*/
|
|
41
|
+
export type PlayerContent = {
|
|
42
|
+
title: Option<string | number>;
|
|
43
|
+
summary: Option<string | number>;
|
|
44
|
+
/**
|
|
45
|
+
* Cover art for the content currently playing. Published alongside the text
|
|
46
|
+
* (rather than read from `entry` by whoever needs it) so it follows the same
|
|
47
|
+
* entry: on live audio that means the programme's own artwork, refreshed
|
|
48
|
+
* with every feed poll, instead of the channel's.
|
|
49
|
+
*/
|
|
50
|
+
imageUrl: Option<string>;
|
|
51
|
+
};
|
|
22
52
|
/**
|
|
23
53
|
* Player Instance Controller
|
|
24
54
|
* Instance exposes utility methods that will help set or get state
|
|
@@ -45,6 +75,7 @@ export class Player {
|
|
|
45
75
|
readonly playerRole: PlayerRole = PlayerRole.Unspecified;
|
|
46
76
|
private overlaysObserver?: OverlaysObserver | null;
|
|
47
77
|
private entryObservable?: BehaviorSubject<ZappEntry> | null;
|
|
78
|
+
private contentSubject?: BehaviorSubject<PlayerContent> | null;
|
|
48
79
|
|
|
49
80
|
protected listeners: {
|
|
50
81
|
[key: string]: QuickBrickPlayer.SharedPlayerCallBacks;
|
|
@@ -58,7 +89,17 @@ export class Player {
|
|
|
58
89
|
|
|
59
90
|
set entry(newEntry: ZappEntry | null) {
|
|
60
91
|
this._entry = newEntry;
|
|
92
|
+
// The live entry is scoped to the entry it overlays - see
|
|
93
|
+
// `OverlaysObserver.clearLiveEntry`. No-op when there is no observer,
|
|
94
|
+
// which is every non-live player.
|
|
95
|
+
this.overlaysObserver?.clearLiveEntry();
|
|
61
96
|
this.entryObservable?.next(newEntry);
|
|
97
|
+
// Optional chaining short-circuits the whole call expression, so
|
|
98
|
+
// `getContent()` is NOT evaluated while no subscriber exists. That
|
|
99
|
+
// matters: `PlayerNative` assigns `this.entry` in its constructor, before
|
|
100
|
+
// subclass arrow-function properties such as `getPluginConfiguration`
|
|
101
|
+
// have been assigned.
|
|
102
|
+
this.contentSubject?.next(this.getContent());
|
|
62
103
|
}
|
|
63
104
|
|
|
64
105
|
constructor(props) {
|
|
@@ -117,6 +158,77 @@ export class Player {
|
|
|
117
158
|
return this.entryObservable;
|
|
118
159
|
};
|
|
119
160
|
|
|
161
|
+
/**
|
|
162
|
+
* The player's resolved Title/Subtitle.
|
|
163
|
+
*
|
|
164
|
+
* Resolved on demand rather than cached: the plugin configuration arrives
|
|
165
|
+
* asynchronously on both the native and cast paths, so a value computed once
|
|
166
|
+
* at entry-set time would be stale for whichever arrives first.
|
|
167
|
+
*
|
|
168
|
+
* Every fallback (no config, empty/whitespace key, unresolvable key, key
|
|
169
|
+
* resolving to an empty string or to a non-string/non-number) lives in
|
|
170
|
+
* `resolvePlayerContentText` and is never reimplemented here.
|
|
171
|
+
*/
|
|
172
|
+
getContent = (): PlayerContent => {
|
|
173
|
+
// Live audio keeps playing one channel while the programme on air
|
|
174
|
+
// changes, so the observer's freshly-loaded programme entry wins when
|
|
175
|
+
// there is one. Read through `?.` so this never constructs an observer:
|
|
176
|
+
// a VOD or cast player has none and falls straight through to its entry.
|
|
177
|
+
const entry = this.overlaysObserver?.liveEntry ?? this.entry;
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
...resolvePlayerTitleSubtitle({
|
|
181
|
+
entry,
|
|
182
|
+
pluginConfiguration: this.getPluginConfiguration(),
|
|
183
|
+
dataKeys: getPlayerContentDataKeys({
|
|
184
|
+
isTV: isTV(),
|
|
185
|
+
isAudioContent: isAudioItem(entry),
|
|
186
|
+
}),
|
|
187
|
+
}),
|
|
188
|
+
// Same selection Android's `EntryHelpers.extractCoverImage` makes:
|
|
189
|
+
// prefer the `image_base` media item, otherwise the first one available.
|
|
190
|
+
// Reused rather than reimplemented so the two platforms cannot drift.
|
|
191
|
+
imageUrl: entry ? imageSrcFromMediaItem(entry, []) : undefined,
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Created lazily, like `entryObservable`. An eagerly created subject would
|
|
197
|
+
* resolve during `PlayerNative`'s constructor and call a not-yet-assigned
|
|
198
|
+
* `getPluginConfiguration`.
|
|
199
|
+
*
|
|
200
|
+
* The seed is produced by calling the resolver, never from raw entry fields:
|
|
201
|
+
* a `BehaviorSubject` replays its seed synchronously on subscribe, so a raw
|
|
202
|
+
* seed would overwrite correctly-resolved text at mount.
|
|
203
|
+
*/
|
|
204
|
+
getContentObservable = (): Observable<PlayerContent> => {
|
|
205
|
+
if (!this.contentSubject) {
|
|
206
|
+
this.contentSubject = new BehaviorSubject<PlayerContent>(
|
|
207
|
+
this.getContent()
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return this.contentSubject.asObservable().pipe(
|
|
212
|
+
// `resolvePlayerTitleSubtitle` returns a fresh object every call, so
|
|
213
|
+
// reference equality would never dedupe. Compare by value.
|
|
214
|
+
distinctUntilChanged(
|
|
215
|
+
(previous, next) =>
|
|
216
|
+
previous.title === next.title &&
|
|
217
|
+
previous.summary === next.summary &&
|
|
218
|
+
previous.imageUrl === next.imageUrl
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Re-resolves and publishes the current content. Called when something the
|
|
225
|
+
* resolution depends on changed without `entry` itself being reassigned -
|
|
226
|
+
* today that is the observer swapping in a newly-loaded live programme.
|
|
227
|
+
*/
|
|
228
|
+
publishContent = () => {
|
|
229
|
+
this.contentSubject?.next(this.getContent());
|
|
230
|
+
};
|
|
231
|
+
|
|
120
232
|
/**
|
|
121
233
|
* ---------------------------------------- PLAYER SETTERS GETTERS ----------------------------------------
|
|
122
234
|
* These methods let you get information about the content / ad playing
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Player, PlayerContent } from "./player";
|
|
3
|
+
import { usePlayer } from "./usePlayer";
|
|
4
|
+
|
|
5
|
+
const EMPTY_CONTENT: PlayerContent = {
|
|
6
|
+
title: null,
|
|
7
|
+
summary: null,
|
|
8
|
+
imageUrl: null,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Subscribes to the player's resolved Title/Subtitle, the same way
|
|
13
|
+
* `usePlayerState` subscribes to playback state. Modeled on that hook
|
|
14
|
+
* deliberately: one React bridge pattern for everything the manager publishes.
|
|
15
|
+
*
|
|
16
|
+
* Passing no `playerId` uses the active player, which is the right default -
|
|
17
|
+
* only one player instance is live at a time. `usePlayer` additionally swaps
|
|
18
|
+
* to the casting receiver while casting, so this follows cast handover
|
|
19
|
+
* automatically.
|
|
20
|
+
*/
|
|
21
|
+
export const usePlayerContent = (playerId?: string): PlayerContent => {
|
|
22
|
+
const player: Player = usePlayer(playerId);
|
|
23
|
+
|
|
24
|
+
const [content, setContent] = React.useState<PlayerContent>(
|
|
25
|
+
() => player?.getContent() ?? EMPTY_CONTENT
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
if (!player) {
|
|
30
|
+
setContent(EMPTY_CONTENT);
|
|
31
|
+
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const subscription = player
|
|
36
|
+
.getContentObservable()
|
|
37
|
+
.subscribe((next) => setContent(next));
|
|
38
|
+
|
|
39
|
+
return () => subscription.unsubscribe();
|
|
40
|
+
}, [player]);
|
|
41
|
+
|
|
42
|
+
return content;
|
|
43
|
+
};
|
package/cellUtils/index.ts
CHANGED
|
@@ -65,11 +65,38 @@ export const isEmptyOrNil = (value: any): boolean =>
|
|
|
65
65
|
*/
|
|
66
66
|
export const getLabel = (dataKeyValue, customDataKeyValue) => (entry) => {
|
|
67
67
|
const label = getKeyForLabel(dataKeyValue, customDataKeyValue);
|
|
68
|
-
const isPath = label &&
|
|
69
|
-
|
|
68
|
+
const isPath = label && label.length > 0;
|
|
69
|
+
|
|
70
|
+
// An unresolved key is rendered as-is, because a data key is allowed to be a
|
|
71
|
+
// literal label ("More") rather than a path. Fall back to the whole key
|
|
72
|
+
// instead of its first segment: "extensions.section" reads as an unresolved
|
|
73
|
+
// path, while "extensions" reads as a legitimate label and hides the problem.
|
|
74
|
+
const fallback = isEmptyOrNil(label) ? "" : label.join(".");
|
|
70
75
|
|
|
71
76
|
try {
|
|
72
|
-
|
|
77
|
+
if (!isPath) {
|
|
78
|
+
return fallback;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const resolved = label.reduce(
|
|
82
|
+
(value, key) => (value == null ? undefined : value[key]),
|
|
83
|
+
entry
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (isNotNil(resolved)) {
|
|
87
|
+
return resolved;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// A multi-segment key is unambiguously a path, so failing to resolve it is
|
|
91
|
+
// a misconfiguration rather than someone typing a literal label.
|
|
92
|
+
if (label.length > 1) {
|
|
93
|
+
cellUtilsLogger.warning({
|
|
94
|
+
message: `getLabel: data key "${fallback}" was not found on the entry, rendering the key itself`,
|
|
95
|
+
data: { dataKeyValue, customDataKeyValue, entryId: entry?.id },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return fallback;
|
|
73
100
|
} catch (error) {
|
|
74
101
|
cellUtilsLogger.warning({
|
|
75
102
|
data: { error, dataKeyValue, customDataKeyValue },
|
|
@@ -1352,6 +1352,35 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
1352
1352
|
type: "switch",
|
|
1353
1353
|
label_tooltip: "Select whether you want to display title in player",
|
|
1354
1354
|
},
|
|
1355
|
+
{
|
|
1356
|
+
section: "title",
|
|
1357
|
+
key: "title_data_key_source",
|
|
1358
|
+
label: "Title data key",
|
|
1359
|
+
initial_value: "title",
|
|
1360
|
+
type: "select",
|
|
1361
|
+
options: [
|
|
1362
|
+
{ text: "Title", value: "title" },
|
|
1363
|
+
{ text: "Other", value: "other" },
|
|
1364
|
+
],
|
|
1365
|
+
label_tooltip:
|
|
1366
|
+
"Select which feed data key provides the player content title. Keep 'Title' to use the entry title",
|
|
1367
|
+
},
|
|
1368
|
+
{
|
|
1369
|
+
section: "title",
|
|
1370
|
+
key: "title_custom_data_key",
|
|
1371
|
+
label: "Custom title data key",
|
|
1372
|
+
initial_value: "",
|
|
1373
|
+
type: "text_input",
|
|
1374
|
+
rules: "conditional",
|
|
1375
|
+
conditional_fields: [
|
|
1376
|
+
{
|
|
1377
|
+
key: "styles/title_data_key_source",
|
|
1378
|
+
condition_value: "other",
|
|
1379
|
+
},
|
|
1380
|
+
],
|
|
1381
|
+
label_tooltip:
|
|
1382
|
+
"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",
|
|
1383
|
+
},
|
|
1355
1384
|
{
|
|
1356
1385
|
section: "title",
|
|
1357
1386
|
key: "title_font_android",
|
|
@@ -1759,6 +1788,35 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
1759
1788
|
label_tooltip:
|
|
1760
1789
|
"Select whether you want to display subtitle in player",
|
|
1761
1790
|
},
|
|
1791
|
+
{
|
|
1792
|
+
section: "subtitle",
|
|
1793
|
+
key: "subtitle_data_key_source",
|
|
1794
|
+
label: "Subtitle data key",
|
|
1795
|
+
initial_value: "summary",
|
|
1796
|
+
type: "select",
|
|
1797
|
+
options: [
|
|
1798
|
+
{ text: "Subtitle", value: "summary" },
|
|
1799
|
+
{ text: "Other", value: "other" },
|
|
1800
|
+
],
|
|
1801
|
+
label_tooltip:
|
|
1802
|
+
"Select which feed data key provides the player content subtitle. Keep 'Subtitle' to use the entry summary",
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
section: "subtitle",
|
|
1806
|
+
key: "subtitle_custom_data_key",
|
|
1807
|
+
label: "Custom subtitle data key",
|
|
1808
|
+
initial_value: "",
|
|
1809
|
+
type: "text_input",
|
|
1810
|
+
rules: "conditional",
|
|
1811
|
+
conditional_fields: [
|
|
1812
|
+
{
|
|
1813
|
+
key: "styles/subtitle_data_key_source",
|
|
1814
|
+
condition_value: "other",
|
|
1815
|
+
},
|
|
1816
|
+
],
|
|
1817
|
+
label_tooltip:
|
|
1818
|
+
"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",
|
|
1819
|
+
},
|
|
1762
1820
|
{
|
|
1763
1821
|
section: "subtitle",
|
|
1764
1822
|
key: "subtitle_font_android",
|
|
@@ -2923,6 +2981,33 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
2923
2981
|
"Video Player - Title Text",
|
|
2924
2982
|
"Video Player Title Text Label Styles",
|
|
2925
2983
|
[
|
|
2984
|
+
{
|
|
2985
|
+
key: "video_player_title_data_key_source",
|
|
2986
|
+
label: "Video title data key",
|
|
2987
|
+
initial_value: "title",
|
|
2988
|
+
type: "select",
|
|
2989
|
+
options: [
|
|
2990
|
+
{ text: "Title", value: "title" },
|
|
2991
|
+
{ text: "Other", value: "other" },
|
|
2992
|
+
],
|
|
2993
|
+
label_tooltip:
|
|
2994
|
+
"Select which feed data key provides the video player title. Keep 'Title' to use the entry title",
|
|
2995
|
+
},
|
|
2996
|
+
{
|
|
2997
|
+
key: "video_player_title_custom_data_key",
|
|
2998
|
+
label: "Custom video title data key",
|
|
2999
|
+
initial_value: "",
|
|
3000
|
+
type: "text_input",
|
|
3001
|
+
rules: "conditional",
|
|
3002
|
+
conditional_fields: [
|
|
3003
|
+
{
|
|
3004
|
+
key: "styles/video_player_title_data_key_source",
|
|
3005
|
+
condition_value: "other",
|
|
3006
|
+
},
|
|
3007
|
+
],
|
|
3008
|
+
label_tooltip:
|
|
3009
|
+
"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",
|
|
3010
|
+
},
|
|
2926
3011
|
{
|
|
2927
3012
|
key: "player_title_color",
|
|
2928
3013
|
label: "Font color",
|
|
@@ -3008,6 +3093,33 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
3008
3093
|
"Video Player - Summary Text",
|
|
3009
3094
|
"Video Player Summary Text Label Styles",
|
|
3010
3095
|
[
|
|
3096
|
+
{
|
|
3097
|
+
key: "video_player_summary_data_key_source",
|
|
3098
|
+
label: "Video summary data key",
|
|
3099
|
+
initial_value: "summary",
|
|
3100
|
+
type: "select",
|
|
3101
|
+
options: [
|
|
3102
|
+
{ text: "Summary", value: "summary" },
|
|
3103
|
+
{ text: "Other", value: "other" },
|
|
3104
|
+
],
|
|
3105
|
+
label_tooltip:
|
|
3106
|
+
"Select which feed data key provides the video player summary. Keep 'Summary' to use the entry summary",
|
|
3107
|
+
},
|
|
3108
|
+
{
|
|
3109
|
+
key: "video_player_summary_custom_data_key",
|
|
3110
|
+
label: "Custom video summary data key",
|
|
3111
|
+
initial_value: "",
|
|
3112
|
+
type: "text_input",
|
|
3113
|
+
rules: "conditional",
|
|
3114
|
+
conditional_fields: [
|
|
3115
|
+
{
|
|
3116
|
+
key: "styles/video_player_summary_data_key_source",
|
|
3117
|
+
condition_value: "other",
|
|
3118
|
+
},
|
|
3119
|
+
],
|
|
3120
|
+
label_tooltip:
|
|
3121
|
+
"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",
|
|
3122
|
+
},
|
|
3011
3123
|
{
|
|
3012
3124
|
key: "player_summary_color",
|
|
3013
3125
|
label: "Font color",
|
|
@@ -3093,6 +3205,33 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
3093
3205
|
"Audio Player - Title Text",
|
|
3094
3206
|
"Audio Player Title Text Label Styles",
|
|
3095
3207
|
[
|
|
3208
|
+
{
|
|
3209
|
+
key: "audio_player_title_data_key_source",
|
|
3210
|
+
label: "Audio title data key",
|
|
3211
|
+
initial_value: "title",
|
|
3212
|
+
type: "select",
|
|
3213
|
+
options: [
|
|
3214
|
+
{ text: "Title", value: "title" },
|
|
3215
|
+
{ text: "Other", value: "other" },
|
|
3216
|
+
],
|
|
3217
|
+
label_tooltip:
|
|
3218
|
+
"Select which feed data key provides the audio player title. Keep 'Title' to use the entry title",
|
|
3219
|
+
},
|
|
3220
|
+
{
|
|
3221
|
+
key: "audio_player_title_custom_data_key",
|
|
3222
|
+
label: "Custom audio title data key",
|
|
3223
|
+
initial_value: "",
|
|
3224
|
+
type: "text_input",
|
|
3225
|
+
rules: "conditional",
|
|
3226
|
+
conditional_fields: [
|
|
3227
|
+
{
|
|
3228
|
+
key: "styles/audio_player_title_data_key_source",
|
|
3229
|
+
condition_value: "other",
|
|
3230
|
+
},
|
|
3231
|
+
],
|
|
3232
|
+
label_tooltip:
|
|
3233
|
+
"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",
|
|
3234
|
+
},
|
|
3096
3235
|
{
|
|
3097
3236
|
key: "audio_player_title_color",
|
|
3098
3237
|
label: "Audio player title color",
|
|
@@ -3178,6 +3317,33 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
3178
3317
|
"Audio Player - Summary Text",
|
|
3179
3318
|
"Audio Player Summary Text Label Styles",
|
|
3180
3319
|
[
|
|
3320
|
+
{
|
|
3321
|
+
key: "audio_player_summary_data_key_source",
|
|
3322
|
+
label: "Audio summary data key",
|
|
3323
|
+
initial_value: "summary",
|
|
3324
|
+
type: "select",
|
|
3325
|
+
options: [
|
|
3326
|
+
{ text: "Summary", value: "summary" },
|
|
3327
|
+
{ text: "Other", value: "other" },
|
|
3328
|
+
],
|
|
3329
|
+
label_tooltip:
|
|
3330
|
+
"Select which feed data key provides the audio player summary. Keep 'Summary' to use the entry summary",
|
|
3331
|
+
},
|
|
3332
|
+
{
|
|
3333
|
+
key: "audio_player_summary_custom_data_key",
|
|
3334
|
+
label: "Custom audio summary data key",
|
|
3335
|
+
initial_value: "",
|
|
3336
|
+
type: "text_input",
|
|
3337
|
+
rules: "conditional",
|
|
3338
|
+
conditional_fields: [
|
|
3339
|
+
{
|
|
3340
|
+
key: "styles/audio_player_summary_data_key_source",
|
|
3341
|
+
condition_value: "other",
|
|
3342
|
+
},
|
|
3343
|
+
],
|
|
3344
|
+
label_tooltip:
|
|
3345
|
+
"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",
|
|
3346
|
+
},
|
|
3181
3347
|
{
|
|
3182
3348
|
key: "audio_player_summary_color",
|
|
3183
3349
|
label: "Audio player summary color",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "15.1.
|
|
3
|
+
"version": "15.1.9-alpha.3547421944",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@applicaster/applicaster-types": "15.1.
|
|
30
|
+
"@applicaster/applicaster-types": "15.1.9-alpha.3547421944",
|
|
31
31
|
"buffer": "^5.2.1",
|
|
32
32
|
"camelize": "^1.0.0",
|
|
33
33
|
"dayjs": "^1.11.10",
|