@applicaster/zapp-react-native-utils 12.0.0-alpha.5812543239 → 12.0.0-alpha.5895113034
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/ActionExecutorContext.tsx +16 -8
- package/analyticsUtils/AnalyticPlayerListener.ts +155 -0
- package/analyticsUtils/AnalyticsEvents/index.ts +6 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
- package/analyticsUtils/__tests__/analyticsManager.test.js +75 -73
- package/analyticsUtils/analyticsPlugins.ts +25 -0
- package/analyticsUtils/events.ts +6 -0
- package/analyticsUtils/helpers/hooks.ts +1 -1
- package/analyticsUtils/index.tsx +6 -0
- package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
- package/appUtils/HooksManager/index.js +3 -1
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +12 -3
- package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +1 -1
- package/appUtils/keyCodes/__tests__/keyCodes.test.js +80 -85
- package/appUtils/keyCodes/keys.web.js +1 -0
- package/appUtils/orientationHelper.ts +1 -1
- package/appUtils/playerManager/DelayedSeekPlayerWrapper.tsx +179 -0
- package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +53 -0
- package/appUtils/playerManager/conts.ts +5 -0
- package/appUtils/playerManager/index.ts +9 -27
- package/appUtils/playerManager/player.ts +60 -9
- package/appUtils/playerManager/playerFactory.ts +15 -1
- package/appUtils/playerManager/playerNative.ts +39 -3
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +23 -4
- package/appUtils/playerManager/usePlayerState.tsx +13 -1
- package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +50 -0
- package/cellUtils/__tests__/cellUtils.test.ts +48 -0
- package/cellUtils/__tests__/fixtures.ts +13 -0
- package/cellUtils/index.ts +40 -2
- package/dateUtils/index.ts +11 -0
- package/focusManager/index.ts +1 -1
- package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +2 -2
- package/functionUtils/__tests__/functionUtils.test.js +244 -248
- package/headersUtils/index.ts +1 -0
- package/inflatedUrlUtils/index.ts +4 -4
- package/localizationUtils/__tests__/localizationUtils.test.ts +78 -55
- package/localizationUtils/index.ts +13 -19
- package/logger/index.ts +1 -1
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +10 -1
- package/manifestUtils/index.js +5 -1
- package/manifestUtils/secondaryImage/index.js +18 -15
- package/modalState/index.ts +57 -0
- package/navigationUtils/index.ts +56 -7
- package/numberUtils/index.ts +4 -0
- package/package.json +3 -2
- package/playerUtils/configurationGenerator.ts +11 -1
- package/pluginUtils/index.ts +11 -7
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
- package/reactHooks/cell-click/__tests__/index.test.js +11 -2
- package/reactHooks/cell-click/index.ts +4 -4
- package/reactHooks/device/index.ts +0 -1
- package/reactHooks/device/useIsTablet.tsx +28 -3
- package/reactHooks/device/useMemoizedIsTablet.ts +1 -1
- package/reactHooks/device/useOrientation.tsx +3 -1
- package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -9
- package/reactHooks/index.ts +2 -0
- package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +66 -59
- package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
- package/reactHooks/layout/index.ts +2 -1
- package/reactHooks/layout/types/index.ts +2 -0
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +11 -16
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +12 -14
- package/reactHooks/layout/useDimensions/useDimensions.ts +13 -7
- package/reactHooks/navigation/__mocks__/index.ts +15 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +7 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +240 -81
- package/reactHooks/navigation/index.ts +208 -86
- package/reactHooks/player/__tests__/useAutoSeek.test.tsx +8 -1
- package/reactHooks/player/useAutoSeek.tsx +28 -8
- package/reactHooks/player/useSeek.tsx +4 -2
- package/reactHooks/player/useTapSeek.tsx +34 -7
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
- package/reactHooks/screen/__mocks__/useScreenContext.ts +3 -1
- package/reactHooks/screen/index.ts +2 -0
- package/reactHooks/screen/useScreenContext.ts +5 -4
- package/reactHooks/screen/useScreenData.ts +1 -1
- package/reactHooks/screen/useTargetScreenData.ts +7 -0
- package/reactHooks/state/useRivers.ts +3 -1
- package/reactHooks/useListenEventBusEvent.ts +1 -4
- package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +10 -9
- package/riverComponetsMeasurementProvider/index.tsx +2 -2
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +38 -23
- package/types.ts +4 -0
- package/reactHooks/device/useIsTablet.android.tsx +0 -14
- package/reactHooks/device/useIsTablet.ios.tsx +0 -7
- package/reactHooks/device/useIsTablet.web.tsx +0 -3
|
@@ -42,7 +42,7 @@ export class Player {
|
|
|
42
42
|
|
|
43
43
|
protected startPosition?: number | null;
|
|
44
44
|
readonly playerRole: PlayerRole = PlayerRole.Unspecified;
|
|
45
|
-
private
|
|
45
|
+
private overlaysObserver?: OverlaysObserver | null;
|
|
46
46
|
private entryObservable?: BehaviorSubject<ZappEntry> | null;
|
|
47
47
|
|
|
48
48
|
protected listeners: {
|
|
@@ -91,15 +91,17 @@ export class Player {
|
|
|
91
91
|
this.onVideoProgress = this.onVideoProgress.bind(this);
|
|
92
92
|
this.onVideoLoad = this.onVideoLoad.bind(this);
|
|
93
93
|
this.isFullScreenSupported = this.isFullScreenSupported.bind(this);
|
|
94
|
+
this.onPlayerSeekComplete = this.onPlayerSeekComplete.bind(this);
|
|
95
|
+
|
|
94
96
|
this.entryObservable = null;
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
getOverlayObservable = () => {
|
|
98
|
-
if (!this.
|
|
99
|
-
this.
|
|
100
|
+
if (!this.overlaysObserver) {
|
|
101
|
+
this.overlaysObserver = new OverlaysObserver({ player: this });
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
return this.
|
|
104
|
+
return this.overlaysObserver;
|
|
103
105
|
};
|
|
104
106
|
|
|
105
107
|
getEntryObservable = (): BehaviorSubject<ZappEntry | null> => {
|
|
@@ -278,6 +280,10 @@ export class Player {
|
|
|
278
280
|
return this.playerState.isBuffering;
|
|
279
281
|
};
|
|
280
282
|
|
|
283
|
+
isReadyToPlay = (): boolean => {
|
|
284
|
+
return this.playerState.isReadyToPlay;
|
|
285
|
+
};
|
|
286
|
+
|
|
281
287
|
// Methods to call actions on the player
|
|
282
288
|
seekTo = (_position: number) => {};
|
|
283
289
|
|
|
@@ -317,7 +323,7 @@ export class Player {
|
|
|
317
323
|
* You should use dispatchPlayerEvent at PlayerContainer or call playerManager.invokeHandler directly
|
|
318
324
|
* This allows you to use one method for everything.
|
|
319
325
|
*/
|
|
320
|
-
|
|
326
|
+
onPlayerSeekStart = (event: QuickBrickPlayer.OnSeekStartEvent) => {
|
|
321
327
|
const { isLive, seekableDuration, currentTime, duration, seekTime } = event;
|
|
322
328
|
|
|
323
329
|
this.setPlaybackState({ currentTime, duration, seekableDuration, isLive });
|
|
@@ -332,10 +338,11 @@ export class Player {
|
|
|
332
338
|
});
|
|
333
339
|
};
|
|
334
340
|
|
|
335
|
-
|
|
341
|
+
onPlayerSeekComplete(event: QuickBrickPlayer.OnSeekCompleteEvent) {
|
|
336
342
|
const { isLive, seekableDuration, currentTime, duration } = event;
|
|
337
343
|
|
|
338
344
|
this.setPlaybackState({ currentTime, duration, seekableDuration, isLive });
|
|
345
|
+
|
|
339
346
|
this.playerState.seekPosition = null;
|
|
340
347
|
|
|
341
348
|
if (!isLive) {
|
|
@@ -346,7 +353,7 @@ export class Player {
|
|
|
346
353
|
callbackName: SharedPlayerCallBacksKeys.OnPlayerSeekComplete,
|
|
347
354
|
event,
|
|
348
355
|
});
|
|
349
|
-
}
|
|
356
|
+
}
|
|
350
357
|
|
|
351
358
|
onVideoEnd = (event) => {
|
|
352
359
|
this.invokeListenersUpdate({
|
|
@@ -410,6 +417,32 @@ export class Player {
|
|
|
410
417
|
});
|
|
411
418
|
};
|
|
412
419
|
|
|
420
|
+
onVideoFullscreenPlayerWillDismiss = () => {
|
|
421
|
+
this.invokeListenersUpdate({
|
|
422
|
+
callbackName:
|
|
423
|
+
SharedPlayerCallBacksKeys.onVideoFullscreenPlayerWillDismiss,
|
|
424
|
+
});
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
onVideoFullscreenPlayerDidDismiss = () => {
|
|
428
|
+
this.invokeListenersUpdate({
|
|
429
|
+
callbackName: SharedPlayerCallBacksKeys.onVideoFullscreenPlayerDidDismiss,
|
|
430
|
+
});
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
onVideoFullscreenPlayerWillPresent = () => {
|
|
434
|
+
this.invokeListenersUpdate({
|
|
435
|
+
callbackName:
|
|
436
|
+
SharedPlayerCallBacksKeys.onVideoFullscreenPlayerWillPresent,
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
onVideoFullscreenPlayerDidPresent = () => {
|
|
441
|
+
this.invokeListenersUpdate({
|
|
442
|
+
callbackName: SharedPlayerCallBacksKeys.onVideoFullscreenPlayerDidPresent,
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
|
|
413
446
|
public invokeListenersUpdate = ({
|
|
414
447
|
callbackName,
|
|
415
448
|
event = {},
|
|
@@ -616,8 +649,8 @@ export class Player {
|
|
|
616
649
|
};
|
|
617
650
|
|
|
618
651
|
public getListener = (): QuickBrickPlayer.SharedPlayerCallBacks => ({
|
|
619
|
-
onPlayerSeekStart: this.
|
|
620
|
-
onPlayerSeekComplete: this.
|
|
652
|
+
onPlayerSeekStart: this.onPlayerSeekStart,
|
|
653
|
+
onPlayerSeekComplete: this.onPlayerSeekComplete,
|
|
621
654
|
onVideoEnd: this.onVideoEnd,
|
|
622
655
|
onError: this.onError,
|
|
623
656
|
onLoad: this.onLoad,
|
|
@@ -630,6 +663,12 @@ export class Player {
|
|
|
630
663
|
onBufferStart: this.onBufferStart,
|
|
631
664
|
onBufferComplete: this.onBufferComplete,
|
|
632
665
|
|
|
666
|
+
onPlayerClose: this.onPlayerClose,
|
|
667
|
+
onVideoFullscreenPlayerWillPresent: this.onVideoFullscreenPlayerWillPresent,
|
|
668
|
+
onVideoFullscreenPlayerDidPresent: this.onVideoFullscreenPlayerDidPresent,
|
|
669
|
+
onVideoFullscreenPlayerWillDismiss: this.onVideoFullscreenPlayerWillDismiss,
|
|
670
|
+
onVideoFullscreenPlayerDidDismiss: this.onVideoFullscreenPlayerDidDismiss,
|
|
671
|
+
|
|
633
672
|
// Ads Callbacks:
|
|
634
673
|
onAdBegin: this.onAdBegin,
|
|
635
674
|
onAdBreakBegin: this.onAdBreakBegin,
|
|
@@ -652,6 +691,10 @@ export class Player {
|
|
|
652
691
|
_selected?: QuickBrickPlayer.TextTrack | QuickBrickPlayer.AudioTrack
|
|
653
692
|
) => {};
|
|
654
693
|
|
|
694
|
+
setPlaybackRate = (_rate) => {};
|
|
695
|
+
startSleepTimer = (_timestamp) => {};
|
|
696
|
+
cancelSleepTimer = () => {};
|
|
697
|
+
|
|
655
698
|
getPluginConfiguration = () => null;
|
|
656
699
|
appStateChange = (_appState, _previousAppState) => {};
|
|
657
700
|
|
|
@@ -685,4 +728,12 @@ export class Player {
|
|
|
685
728
|
isFullScreenSupported(): boolean {
|
|
686
729
|
return true;
|
|
687
730
|
}
|
|
731
|
+
|
|
732
|
+
public supportsNativeControls = (): boolean => false;
|
|
733
|
+
public supportNativeCast = (): boolean => false;
|
|
734
|
+
|
|
735
|
+
public isInDelayedSeek = (): boolean => false;
|
|
736
|
+
public enterDelayedSeek() {}
|
|
737
|
+
|
|
738
|
+
public exitDelayedSeek() {}
|
|
688
739
|
}
|
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
2
2
|
import { findPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
3
3
|
import { Player } from "./player";
|
|
4
|
+
import { PlayerRole } from "./conts";
|
|
4
5
|
|
|
5
6
|
type PlayerFactoryItem = {
|
|
6
7
|
Component: any;
|
|
7
8
|
controller: Player;
|
|
8
9
|
};
|
|
9
10
|
|
|
11
|
+
type PlayerFactoryProps = {
|
|
12
|
+
player: any;
|
|
13
|
+
playerId: string;
|
|
14
|
+
autoplay: boolean;
|
|
15
|
+
entry: ZappEntry;
|
|
16
|
+
muted: boolean;
|
|
17
|
+
playerPluginId: string;
|
|
18
|
+
screenConfig: Record<string, any>;
|
|
19
|
+
playerRole: PlayerRole;
|
|
20
|
+
};
|
|
21
|
+
|
|
10
22
|
interface PlayerConstructor {
|
|
11
23
|
new (params: any): Player;
|
|
12
24
|
}
|
|
13
25
|
|
|
14
|
-
export const playerFactory = (
|
|
26
|
+
export const playerFactory = (
|
|
27
|
+
config: PlayerFactoryProps
|
|
28
|
+
): PlayerFactoryItem | null => {
|
|
15
29
|
const IDENTIFIER = config?.playerPluginId;
|
|
16
30
|
|
|
17
31
|
if (!IDENTIFIER) {
|
|
@@ -5,6 +5,7 @@ import { Player } from "./player";
|
|
|
5
5
|
import { findNodeHandle, NativeModules } from "react-native";
|
|
6
6
|
import { isApplePlatform } from "../../reactUtils";
|
|
7
7
|
import { PlayerRole } from "./conts";
|
|
8
|
+
import { toBooleanWithDefaultFalse } from "../../booleanUtils";
|
|
8
9
|
|
|
9
10
|
const RNPlayerManager = {
|
|
10
11
|
QuickBrickPlayerPlugin: isApplePlatform()
|
|
@@ -41,6 +42,9 @@ const PlayerModuleFuncNames = {
|
|
|
41
42
|
rewind: "rewind",
|
|
42
43
|
play: "play",
|
|
43
44
|
pause: "pause",
|
|
45
|
+
startSleepTimer: "startSleepTimer",
|
|
46
|
+
cancelSleepTimer: "cancelSleepTimer",
|
|
47
|
+
getSleepTimerEnd: "getSleepTimerEnd",
|
|
44
48
|
};
|
|
45
49
|
|
|
46
50
|
export class PlayerNative extends Player {
|
|
@@ -66,7 +70,7 @@ export class PlayerNative extends Player {
|
|
|
66
70
|
currentPlayerComponent = (): ReactComponent<PlayerProps> =>
|
|
67
71
|
this.playerComponent?.current;
|
|
68
72
|
|
|
69
|
-
getState = () => this.
|
|
73
|
+
getState = () => this.playerState;
|
|
70
74
|
|
|
71
75
|
// TODO: Try to add module interface
|
|
72
76
|
invokeNativeFunction = (funcName: string, ...params: any[]): boolean => {
|
|
@@ -107,7 +111,7 @@ export class PlayerNative extends Player {
|
|
|
107
111
|
return;
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
const newPosition = R.clamp(0,
|
|
114
|
+
const newPosition = R.clamp(0, this.playerState.seekableDuration, position);
|
|
111
115
|
|
|
112
116
|
if (
|
|
113
117
|
!this.invokeNativeFunction(
|
|
@@ -172,6 +176,25 @@ export class PlayerNative extends Player {
|
|
|
172
176
|
}
|
|
173
177
|
};
|
|
174
178
|
|
|
179
|
+
startSleepTimer = (sleepTimestamp: number) => {
|
|
180
|
+
if (
|
|
181
|
+
!this.invokeNativeFunction(
|
|
182
|
+
PlayerModuleFuncNames.startSleepTimer,
|
|
183
|
+
sleepTimestamp
|
|
184
|
+
)
|
|
185
|
+
) {
|
|
186
|
+
this.currentPlayerComponent()?.["startSleepTimer"](sleepTimestamp);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
cancelSleepTimer = () => {
|
|
191
|
+
if (!this.invokeNativeFunction(PlayerModuleFuncNames.cancelSleepTimer)) {
|
|
192
|
+
this.currentPlayerComponent()?.[
|
|
193
|
+
PlayerModuleFuncNames.cancelSleepTimer
|
|
194
|
+
]?.();
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
175
198
|
disableBufferAnimation = (): boolean => false;
|
|
176
199
|
|
|
177
200
|
setLanguage = (
|
|
@@ -180,6 +203,10 @@ export class PlayerNative extends Player {
|
|
|
180
203
|
this.currentPlayerComponent()?.["setLanguage"]?.(selected);
|
|
181
204
|
};
|
|
182
205
|
|
|
206
|
+
setPlaybackRate = (rate: number) => {
|
|
207
|
+
this.currentPlayerComponent()?.["setPlaybackRate"]?.(rate);
|
|
208
|
+
};
|
|
209
|
+
|
|
183
210
|
getPluginConfiguration = () => {
|
|
184
211
|
return (this.currentPlayerComponent() as any)?.props?.pluginConfiguration;
|
|
185
212
|
};
|
|
@@ -204,9 +231,10 @@ export class PlayerNative extends Player {
|
|
|
204
231
|
|
|
205
232
|
onVideoLoad = (event) => {
|
|
206
233
|
if (this.startPosition) {
|
|
207
|
-
super.onVideoLoad({ ...event, ignoreContinueWatching: true });
|
|
208
234
|
this.seekTo(this.startPosition);
|
|
209
235
|
this.startPosition = null;
|
|
236
|
+
|
|
237
|
+
super.onVideoLoad({ ...event, ignoreContinueWatching: true });
|
|
210
238
|
} else {
|
|
211
239
|
super.onVideoLoad(event);
|
|
212
240
|
}
|
|
@@ -265,4 +293,12 @@ export class PlayerNative extends Player {
|
|
|
265
293
|
|
|
266
294
|
return !(isFullScreenAudioPlayer && this.isAudioItem());
|
|
267
295
|
};
|
|
296
|
+
|
|
297
|
+
supportsNativeControls = (): boolean =>
|
|
298
|
+
toBooleanWithDefaultFalse(this.getConfig()?.["supports_native_controls"]);
|
|
299
|
+
|
|
300
|
+
supportNativeCast = (): boolean =>
|
|
301
|
+
toBooleanWithDefaultFalse(
|
|
302
|
+
this.playerPluginId === "quick-brick-player-brightcove"
|
|
303
|
+
);
|
|
268
304
|
}
|
|
@@ -4,6 +4,9 @@ import { useEffect } from "react";
|
|
|
4
4
|
import { playerManager } from "./index";
|
|
5
5
|
import { useValidatePlayerConfig } from "../../playerUtils/useValidatePlayerConfig";
|
|
6
6
|
import { PlayerRole } from "./conts";
|
|
7
|
+
import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
|
|
8
|
+
import { isTV } from "../../reactUtils";
|
|
9
|
+
import { DelayedSeekPlayerWrapper } from "./DelayedSeekPlayerWrapper";
|
|
7
10
|
|
|
8
11
|
// TODO: Rename to ControllerType
|
|
9
12
|
export const usePlayerControllerSetup = ({
|
|
@@ -34,17 +37,33 @@ export const usePlayerControllerSetup = ({
|
|
|
34
37
|
useValidatePlayerConfig(config);
|
|
35
38
|
|
|
36
39
|
useEffect(() => {
|
|
37
|
-
const
|
|
40
|
+
const newConfig = isAppleTV()
|
|
41
|
+
? { supports_native_controls: true, ...config }
|
|
42
|
+
: config;
|
|
43
|
+
|
|
44
|
+
const combinedProps = {
|
|
38
45
|
player: ref,
|
|
39
46
|
listener: props as any,
|
|
40
|
-
config,
|
|
47
|
+
config: newConfig,
|
|
41
48
|
playerId,
|
|
42
49
|
entry,
|
|
43
50
|
playerPluginId: pluginId,
|
|
44
51
|
playerRole: PlayerRole.Primary,
|
|
45
|
-
}
|
|
52
|
+
};
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
if (isTV()) {
|
|
55
|
+
const wrappedPlayerController = new ControllerType({
|
|
56
|
+
...combinedProps,
|
|
57
|
+
playerRole: PlayerRole.Unspecified,
|
|
58
|
+
playerId: playerId + "." + "wrapped",
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
setPlayerController(
|
|
62
|
+
new DelayedSeekPlayerWrapper(combinedProps, wrappedPlayerController)
|
|
63
|
+
);
|
|
64
|
+
} else {
|
|
65
|
+
setPlayerController(new ControllerType(combinedProps));
|
|
66
|
+
}
|
|
48
67
|
}, [playerId, entry?.id]);
|
|
49
68
|
|
|
50
69
|
React.useEffect(() => {
|
|
@@ -9,6 +9,7 @@ type PlayerState = {
|
|
|
9
9
|
isLive: boolean;
|
|
10
10
|
isPaused: boolean;
|
|
11
11
|
isBuffering: boolean;
|
|
12
|
+
isReadyToPlay: boolean;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
export const usePlayerState = (
|
|
@@ -20,12 +21,14 @@ export const usePlayerState = (
|
|
|
20
21
|
const [seekableDuration, setSeekableDuration] = React.useState<number>(null);
|
|
21
22
|
const [isLive, setIsLive] = React.useState<boolean>(null);
|
|
22
23
|
const [isBuffering, setIsBuffering] = React.useState<boolean>(false);
|
|
24
|
+
const [isReadyToPlay, setIsReadyToPlay] = React.useState<boolean>(false);
|
|
23
25
|
const [isPaused, setIsPaused] = React.useState<boolean>(false);
|
|
24
26
|
|
|
25
27
|
const player: Player = usePlayer(playerId);
|
|
26
28
|
|
|
27
29
|
const onPlayerChangeState = React.useCallback(() => {
|
|
28
30
|
setIsBuffering(player.isBuffering());
|
|
31
|
+
setIsReadyToPlay(player.isReadyToPlay);
|
|
29
32
|
setCurrentTime(player.getContentPosition());
|
|
30
33
|
setDuration(player.getDuration());
|
|
31
34
|
setSeekableDuration(player.getSeekableDuration());
|
|
@@ -60,7 +63,16 @@ export const usePlayerState = (
|
|
|
60
63
|
isLive,
|
|
61
64
|
isPaused,
|
|
62
65
|
isBuffering,
|
|
66
|
+
isReadyToPlay,
|
|
63
67
|
}),
|
|
64
|
-
[
|
|
68
|
+
[
|
|
69
|
+
currentTime,
|
|
70
|
+
duration,
|
|
71
|
+
seekableDuration,
|
|
72
|
+
isLive,
|
|
73
|
+
isPaused,
|
|
74
|
+
isBuffering,
|
|
75
|
+
isReadyToPlay,
|
|
76
|
+
]
|
|
65
77
|
);
|
|
66
78
|
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { OverlaysObserver } from "./OverlayObserver/OverlaysObserver";
|
|
3
|
+
import { useFeedLoader } from "../../reactHooks";
|
|
4
|
+
import { usePlayer } from "./usePlayer";
|
|
5
|
+
|
|
6
|
+
export const useTitleSummaryOverlay = (
|
|
7
|
+
feedUrl,
|
|
8
|
+
entry,
|
|
9
|
+
isLive = false,
|
|
10
|
+
interval: number
|
|
11
|
+
) => {
|
|
12
|
+
const player = usePlayer();
|
|
13
|
+
const [title, setTitle] = useState<string | number>(entry?.title || "");
|
|
14
|
+
const [summary, setSummary] = useState<string | number>(entry?.summary || "");
|
|
15
|
+
|
|
16
|
+
// Hook to load feed data
|
|
17
|
+
const { data: feedData, reloadData } = useFeedLoader({ feedUrl });
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (player && feedUrl && isLive) {
|
|
21
|
+
const observer = new OverlaysObserver({ player });
|
|
22
|
+
|
|
23
|
+
observer.setFeedDataHandlers(feedUrl, reloadData, interval, (entry) => {
|
|
24
|
+
setTitle(entry?.title || "");
|
|
25
|
+
setSummary(entry?.summary || "");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const subscription = observer
|
|
29
|
+
.getTitleSummaryObservable()
|
|
30
|
+
.subscribe(({ title, summary }) => {
|
|
31
|
+
setTitle(title);
|
|
32
|
+
setSummary(summary);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
subscription.unsubscribe();
|
|
37
|
+
observer.clearFeedDataInterval();
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}, [player, feedUrl, reloadData, isLive]);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (isLive && feedData?.entry?.[0]) {
|
|
44
|
+
setTitle(feedData.entry[0].title || "");
|
|
45
|
+
setSummary(feedData.entry[0].summary || "");
|
|
46
|
+
}
|
|
47
|
+
}, [feedData]);
|
|
48
|
+
|
|
49
|
+
return { title, summary };
|
|
50
|
+
};
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
castAndFallbackColor,
|
|
15
15
|
compact,
|
|
16
16
|
getValue,
|
|
17
|
+
getColorFromData,
|
|
17
18
|
} from "../";
|
|
18
19
|
|
|
19
20
|
import {
|
|
@@ -388,3 +389,50 @@ describe("getValue", () => {
|
|
|
388
389
|
expect(getValue({ value })(value)).toBeNull();
|
|
389
390
|
});
|
|
390
391
|
});
|
|
392
|
+
|
|
393
|
+
describe("getColorFromData", () => {
|
|
394
|
+
it("should return value directly if valid color", () => {
|
|
395
|
+
const result = getColorFromData({
|
|
396
|
+
data: entry,
|
|
397
|
+
valueFromLayout: "#123456",
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
expect(result).toBe("#123456");
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("should return valid rgba color name", () => {
|
|
404
|
+
const result = getColorFromData({
|
|
405
|
+
data: entry,
|
|
406
|
+
valueFromLayout: "rgba(255, 255, 255, 0)",
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
expect(result).toBe("rgba(255, 255, 255, 0)");
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it("should return path value if path value is set and is valid color", () => {
|
|
413
|
+
const result = getColorFromData({
|
|
414
|
+
data: entry,
|
|
415
|
+
valueFromLayout: "background_color",
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
expect(result).toBe("#123456");
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it("should return path value if path value is set, nested and is valid color", () => {
|
|
422
|
+
const result = getColorFromData({
|
|
423
|
+
data: entry,
|
|
424
|
+
valueFromLayout: "extensions.background_color",
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
expect(result).toBe("#123456");
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("should return valueFromLayout if path does not exist", () => {
|
|
431
|
+
const result = getColorFromData({
|
|
432
|
+
data: entry,
|
|
433
|
+
valueFromLayout: "invalid_color",
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
expect(result).toBe("invalid_color");
|
|
437
|
+
});
|
|
438
|
+
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
2
|
|
|
3
|
+
export const defaultColor = "#ffffff";
|
|
4
|
+
|
|
3
5
|
export const entry = {
|
|
4
6
|
type: {
|
|
5
7
|
value: "video",
|
|
@@ -26,7 +28,9 @@ export const entry = {
|
|
|
26
28
|
type: "video/mp4",
|
|
27
29
|
src: "https://wino-feed.s3.amazonaws.com/movieFeed/Ford+v+Ferrari.mp4",
|
|
28
30
|
},
|
|
31
|
+
background_color: "#123456",
|
|
29
32
|
extensions: {
|
|
33
|
+
background_color: "#123456",
|
|
30
34
|
free: false,
|
|
31
35
|
duration: 9000,
|
|
32
36
|
section: "New Releases",
|
|
@@ -46,7 +50,11 @@ export const componentStyle = {
|
|
|
46
50
|
horizontal_list_cell_width: "400",
|
|
47
51
|
cell_plugin_configuration_id: "267bd05a-08bd-4675-bdd3-9b73899773f0",
|
|
48
52
|
horizontal_list_scaled_switch: false,
|
|
53
|
+
background_color: "#123456",
|
|
49
54
|
component_background_color: "rgba(255, 255, 255, 0)",
|
|
55
|
+
background_color_1: "extensions.background_color",
|
|
56
|
+
background_color_2: "invalidColorKey",
|
|
57
|
+
background_color_3: "extensions.invalidColorKeyPath",
|
|
50
58
|
component_padding_left: "",
|
|
51
59
|
horizontal_list_next_offset: 20,
|
|
52
60
|
component_margin_bottom: "",
|
|
@@ -93,6 +101,11 @@ export const VAL = {
|
|
|
93
101
|
upper: "STRING",
|
|
94
102
|
width: 400,
|
|
95
103
|
height: 225,
|
|
104
|
+
colorKey: "background_color",
|
|
105
|
+
colorKeyRgba: "component_background_color",
|
|
106
|
+
colorKeyPath: "background_color_1",
|
|
107
|
+
invalidColorKey: "background_color_2",
|
|
108
|
+
invalidColorKeyPath: "background_color_3",
|
|
96
109
|
};
|
|
97
110
|
|
|
98
111
|
export const TRANSFORM = {
|
package/cellUtils/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
|
+
import validateColor from "validate-color";
|
|
3
4
|
|
|
4
5
|
import { toNumberWithDefault, toNumberWithDefaultZero } from "../numberUtils";
|
|
5
6
|
import { transformColorCode as fixColorHexCode } from "../transform";
|
|
@@ -181,8 +182,8 @@ export const getHeight = (width, aspectRatio) => {
|
|
|
181
182
|
* @returns {string} duration
|
|
182
183
|
*/
|
|
183
184
|
|
|
184
|
-
export const getDurationInMinutes = (seconds
|
|
185
|
-
if (seconds
|
|
185
|
+
export const getDurationInMinutes = (seconds?: number): string => {
|
|
186
|
+
if (!seconds) return "00:00";
|
|
186
187
|
const minutes = Math.floor(Number(seconds) / 60);
|
|
187
188
|
const secondsReminder = Math.floor(Number(seconds)) % 60;
|
|
188
189
|
const pad = (num: number): string => `${num < 10 ? "0" : ""}${num}`;
|
|
@@ -343,6 +344,19 @@ export const ifElseFocused = R.curry(
|
|
|
343
344
|
state === "focused" ? focusedValue : defaultValue
|
|
344
345
|
);
|
|
345
346
|
|
|
347
|
+
export const ifElseSelected = R.curry(
|
|
348
|
+
(state: string, selectedValue, defaultValue) => {
|
|
349
|
+
switch (state) {
|
|
350
|
+
case "selected":
|
|
351
|
+
return selectedValue;
|
|
352
|
+
case "focused_selected":
|
|
353
|
+
return selectedValue;
|
|
354
|
+
default:
|
|
355
|
+
return defaultValue;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
|
|
346
360
|
/**
|
|
347
361
|
* Map textAlign values to their corresponding alignSelf values
|
|
348
362
|
* This is so that we could use the same field for both properties
|
|
@@ -430,3 +444,27 @@ export const toResizeMode = (
|
|
|
430
444
|
resizeMode: "contain",
|
|
431
445
|
};
|
|
432
446
|
};
|
|
447
|
+
|
|
448
|
+
type GetColorFromData = {
|
|
449
|
+
data: Record<string, any>;
|
|
450
|
+
valueFromLayout: string;
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
export const getColorFromData = ({
|
|
454
|
+
data,
|
|
455
|
+
valueFromLayout,
|
|
456
|
+
}: GetColorFromData): string => {
|
|
457
|
+
// Temporary hack to fix color validation when alpha is floating point number
|
|
458
|
+
// https://github.com/dreamyguy/validate-color/issues/44
|
|
459
|
+
if (validateColor(valueFromLayout.replace(".00", ""))) {
|
|
460
|
+
return valueFromLayout;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const pathValue = R.path(valueFromLayout.split("."), data);
|
|
464
|
+
|
|
465
|
+
if (pathValue && validateColor(pathValue)) {
|
|
466
|
+
return pathValue;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return valueFromLayout;
|
|
470
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import utc from "dayjs/plugin/utc";
|
|
3
|
+
import timezone from "dayjs/plugin/timezone";
|
|
4
|
+
import isoWeek from "dayjs/plugin/isoWeek";
|
|
5
|
+
|
|
6
|
+
// Extend dayjs with plugins
|
|
7
|
+
dayjs.extend(utc);
|
|
8
|
+
dayjs.extend(timezone);
|
|
9
|
+
dayjs.extend(isoWeek);
|
|
10
|
+
|
|
11
|
+
export { dayjs };
|
package/focusManager/index.ts
CHANGED
|
@@ -86,7 +86,7 @@ export const isFocusable = (component: ZappEntry | ZappUIComponent | any) =>
|
|
|
86
86
|
|
|
87
87
|
type Props = {
|
|
88
88
|
component: ZappUIComponent;
|
|
89
|
-
zappPipesData
|
|
89
|
+
zappPipesData?: ZappPipesData;
|
|
90
90
|
} & Record<string, any>;
|
|
91
91
|
|
|
92
92
|
export const useIsFocusable = (props: Props): void => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
-
exports[`subscriber can be invoked without args 1`] = `
|
|
3
|
+
exports[`functionUtils subscriber can be invoked without args 1`] = `
|
|
4
4
|
Object {
|
|
5
5
|
"handlers": Object {},
|
|
6
6
|
"invokeHandler": [Function],
|
|
@@ -9,7 +9,7 @@ Object {
|
|
|
9
9
|
}
|
|
10
10
|
`;
|
|
11
11
|
|
|
12
|
-
exports[`subscriber returns the original object with additional methods 1`] = `
|
|
12
|
+
exports[`functionUtils subscriber returns the original object with additional methods 1`] = `
|
|
13
13
|
Object {
|
|
14
14
|
"foo": "bar",
|
|
15
15
|
"handlers": Object {},
|