@applicaster/zapp-react-native-utils 12.0.0-alpha.4584840011 → 12.0.0-alpha.4620237415

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 (35) hide show
  1. package/analyticsUtils/AnalyticPlayerListener.ts +141 -0
  2. package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
  3. package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
  4. package/appUtils/accessibilityManager/index.ts +12 -12
  5. package/appUtils/platform/platformUtils.ts +35 -2
  6. package/appUtils/playerManager/index.ts +9 -27
  7. package/appUtils/playerManager/playerFactory.ts +1 -1
  8. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
  9. package/appUtils/playerManager/playerNative.ts +1 -1
  10. package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -1
  11. package/appUtils/playerManager/usePlayerState.tsx +19 -34
  12. package/arrayUtils/index.ts +11 -0
  13. package/cellUtils/index.ts +33 -0
  14. package/focusManager/FocusManager.ts +20 -5
  15. package/focusManager/__tests__/FocusManager.test.ts +0 -1
  16. package/functionUtils/index.ts +38 -0
  17. package/manifestUtils/defaultManifestConfigurations/player.js +121 -0
  18. package/manifestUtils/keys.js +2 -1
  19. package/navigationUtils/index.ts +1 -1
  20. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +66 -0
  21. package/numberUtils/__tests__/toPositiveNumberWithDefaultZero.test.ts +66 -0
  22. package/numberUtils/index.ts +20 -0
  23. package/package.json +2 -2
  24. package/playerUtils/configurationGenerator.ts +120 -0
  25. package/reactHooks/cell-click/index.ts +17 -3
  26. package/reactHooks/debugging/__tests__/index.test.js +81 -11
  27. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +69 -6
  28. package/reactHooks/feed/useBatchLoading.ts +52 -28
  29. package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +134 -0
  30. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -0
  31. package/reactHooks/navigation/index.ts +1 -1
  32. package/reactHooks/player/useAutoSeek.tsx +9 -7
  33. package/reactHooks/resolvers/useCellResolver.ts +5 -1
  34. package/reactHooks/useConfiguration.ts +2 -2
  35. package/testUtils/index.tsx +42 -9
@@ -0,0 +1,141 @@
1
+ import { PlayerAnalyticsTracker } from "./playerAnalyticsTracker";
2
+ import { Player } from "../appUtils/playerManager/player";
3
+ import { AD_EVENT, GENERAL_EVENT, PLAYBACK_EVENT, QOS_EVENT } from "./events";
4
+
5
+ type Props = {
6
+ analyticsTracker: any;
7
+ player: any;
8
+ };
9
+
10
+ /* eslint brace-style: 0 */ // --> OFF
11
+ export class AnalyticPlayerListener
12
+ implements QuickBrickPlayer.SharedPlayerCallBacks
13
+ {
14
+ static analyticsListenerID = "analytics-listener";
15
+ private analyticsTracker: PlayerAnalyticsTracker;
16
+ private player: Player;
17
+
18
+ private onVideoLoadCalled; // android only
19
+
20
+ constructor(props: Props) {
21
+ this.analyticsTracker = props.analyticsTracker;
22
+ this.player = props.player;
23
+ }
24
+
25
+ handleAnalyticEvent = (event, data = {}) => {
26
+ if (this.analyticsTracker?.handleAnalyticEvent) {
27
+ this.analyticsTracker?.handleAnalyticEvent?.(event, {
28
+ ...data,
29
+ player_type: this.player.playerPluginId,
30
+ });
31
+ }
32
+ };
33
+
34
+ onVideoEnd = () => {
35
+ this.handleAnalyticEvent(PLAYBACK_EVENT.complete);
36
+ };
37
+
38
+ onError = (err: Error) => {
39
+ this.handleAnalyticEvent(PLAYBACK_EVENT.error, err); // TODO: Check error format
40
+ };
41
+
42
+ onPlayerPause = (event) => {
43
+ this.handleAnalyticEvent(PLAYBACK_EVENT.pause, event);
44
+ };
45
+
46
+ onPlayerResume = (event) => {
47
+ this.handleAnalyticEvent(PLAYBACK_EVENT.play, event);
48
+ };
49
+
50
+ onPlayerSeekComplete = (event) => {
51
+ this.handleAnalyticEvent(PLAYBACK_EVENT.seek_complete, event);
52
+ };
53
+
54
+ onPlayerSeekStart = (event) => {
55
+ this.handleAnalyticEvent(PLAYBACK_EVENT.seek_start, event);
56
+ };
57
+
58
+ onBufferStart = (event) => {
59
+ this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_start, event);
60
+ };
61
+
62
+ onBufferComplete = (event) => {
63
+ this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_complete, event);
64
+ };
65
+
66
+ // Not implemented
67
+ onPlayerStop = (event) => {
68
+ this.handleAnalyticEvent(PLAYBACK_EVENT.stop, event);
69
+ };
70
+
71
+ // TODO: Implement method on native
72
+ onVideoBandwidthUpdate = (event) => {
73
+ this.handleAnalyticEvent(QOS_EVENT.bitrate_changed, event);
74
+ };
75
+
76
+ onAdBegin = (event) => {
77
+ this.handleAnalyticEvent(AD_EVENT.ad_start, event);
78
+ };
79
+
80
+ onAdBreakBegin = (event) => {
81
+ this.handleAnalyticEvent(AD_EVENT.ad_break_start, event);
82
+ };
83
+
84
+ onAdBreakEnd = (event) => {
85
+ this.handleAnalyticEvent(AD_EVENT.ad_break_completed, event);
86
+ };
87
+
88
+ onAdEnd = (event) => {
89
+ this.handleAnalyticEvent(AD_EVENT.ad_complete, event);
90
+ };
91
+
92
+ onAdError = (event) => {
93
+ this.handleAnalyticEvent(AD_EVENT.ad_error, event);
94
+ };
95
+
96
+ onAdRequest = (event) => {
97
+ this.handleAnalyticEvent(AD_EVENT.ad_request, event);
98
+ };
99
+
100
+ onAdClicked = (event) => {
101
+ this.handleAnalyticEvent(AD_EVENT.ad_clicked, event);
102
+ };
103
+
104
+ onAdTapped = (event) => {
105
+ this.handleAnalyticEvent(AD_EVENT.ad_tapped, event);
106
+ };
107
+
108
+ onVideoLoad = () => {
109
+ // if (!this.state.isAd && !this.onVideoLoadCalled) {
110
+ if (!this.player.isAd() && !this.onVideoLoadCalled) {
111
+ this.onVideoLoadCalled = true;
112
+
113
+ this.handleAnalyticEvent(PLAYBACK_EVENT.load_video, {
114
+ player_type: this.player.playerPluginId,
115
+ });
116
+ }
117
+ };
118
+
119
+ onPlayerSessionEnd = () => {
120
+ // TODO: Implement method on native android, ios exists
121
+ this.handleAnalyticEvent(GENERAL_EVENT.session_end);
122
+ };
123
+
124
+ onPlayerSessionStart = () => {
125
+ // TODO: Implement method on native android, ios exists
126
+
127
+ this.handleAnalyticEvent(GENERAL_EVENT.session_start);
128
+ };
129
+
130
+ onPlayerNext = ({ nativeEvent }) => {
131
+ // TODO: Implement method on native
132
+
133
+ this.handleAnalyticEvent(PLAYBACK_EVENT.next, nativeEvent);
134
+ };
135
+
136
+ onPlayerPrevious = ({ nativeEvent }) => {
137
+ // TODO: Implement method on native
138
+
139
+ this.handleAnalyticEvent(PLAYBACK_EVENT.previous, nativeEvent);
140
+ };
141
+ }
@@ -0,0 +1,90 @@
1
+ import {
2
+ PlayerAnalyticsTracker,
3
+ PlayerAnalyticsTrackerI,
4
+ } from "./playerAnalyticsTracker";
5
+ import {
6
+ PlayerLifecycleListener,
7
+ PlayerManager,
8
+ } from "../appUtils/playerManager";
9
+ import { Player } from "../appUtils/playerManager/player";
10
+ import { AnalyticPlayerListener } from "./AnalyticPlayerListener";
11
+ import { PlayerRole } from "../appUtils/playerManager/conts";
12
+
13
+ enum PlayerAnalyticsTrackerType {
14
+ PlayerAnalyticsTracker = "PlayerAnalyticsTracker",
15
+ }
16
+
17
+ class PlayerAnalyticsTrackerFactory {
18
+ createTracker(type: PlayerAnalyticsTrackerType) {
19
+ switch (type) {
20
+ case PlayerAnalyticsTrackerType.PlayerAnalyticsTracker:
21
+ return new PlayerAnalyticsTracker();
22
+ default:
23
+ return new PlayerAnalyticsTracker();
24
+ }
25
+ }
26
+ }
27
+
28
+ export class PlayerAnalyticsManager implements PlayerLifecycleListener {
29
+ static instance: PlayerAnalyticsManager;
30
+ private trackers: Record<string, PlayerAnalyticsTrackerI> = {};
31
+ private trackerFactory: PlayerAnalyticsTrackerFactory;
32
+
33
+ public getTracker(
34
+ id: string,
35
+ type: PlayerAnalyticsTrackerType = PlayerAnalyticsTrackerType.PlayerAnalyticsTracker
36
+ ): PlayerAnalyticsTrackerI {
37
+ if (!this.trackers[id]) {
38
+ this.trackers[id] = this.trackerFactory.createTracker(type);
39
+ }
40
+
41
+ return this.trackers[id];
42
+ }
43
+
44
+ constructor(trackerFactory: PlayerAnalyticsTrackerFactory) {
45
+ this.trackerFactory = trackerFactory;
46
+ this.startListenPlayerManager();
47
+ }
48
+
49
+ static getInstance(
50
+ trackerFactory: PlayerAnalyticsTrackerFactory = new PlayerAnalyticsTrackerFactory()
51
+ ): PlayerAnalyticsManager {
52
+ if (!PlayerAnalyticsManager.instance) {
53
+ PlayerAnalyticsManager.instance = new PlayerAnalyticsManager(
54
+ trackerFactory
55
+ );
56
+ }
57
+
58
+ return PlayerAnalyticsManager.instance;
59
+ }
60
+
61
+ private startListenPlayerManager() {
62
+ PlayerManager.getInstance().addLifecycleListener(this);
63
+ }
64
+
65
+ // PlayerLifecycleListener
66
+
67
+ onRegistered = (player: Player) => {
68
+ if (player.playerRole !== PlayerRole.Primary) {
69
+ return;
70
+ }
71
+
72
+ const tracker = this.getTracker(player.playerId);
73
+ tracker.setPlayer(player);
74
+
75
+ const analyticsListener = new AnalyticPlayerListener({
76
+ analyticsTracker: tracker,
77
+ player,
78
+ });
79
+
80
+ player.addListener({
81
+ id: AnalyticPlayerListener.analyticsListenerID,
82
+ listener: analyticsListener,
83
+ });
84
+ };
85
+
86
+ onUnRegistered = (player: Player) => {
87
+ player.removeListener(AnalyticPlayerListener.analyticsListenerID);
88
+ delete this.trackers[player.playerId];
89
+ };
90
+ }
@@ -5,16 +5,25 @@ import { postAnalyticEvent } from "./manager";
5
5
  import { isLive } from "../playerUtils";
6
6
  import { extensionsEvents } from "./AnalyticsEvents/helper";
7
7
  import { AD_EVENT, EVENT_TYPES, PLAYER_DISPLAY_STATES } from "./events";
8
+ import { Player } from "../appUtils/playerManager/player";
8
9
 
9
- export class AnalyticsTracker {
10
+ export interface PlayerAnalyticsTrackerI {
11
+ handleAnalyticEvent(
12
+ event: QuickBrickPlayer.Events,
13
+ eventData?: QuickBrickPlayer.EventData
14
+ ): void;
15
+ setPlayer(player: Player): void;
16
+ }
17
+
18
+ export class PlayerAnalyticsTracker implements PlayerAnalyticsTrackerI {
10
19
  adTime: number | string;
11
20
  mediaTime: number | string;
12
- entry: ZappEntry;
13
21
  eventType: string;
14
22
  playerState: QuickBrickPlayer.NativePlayerState;
23
+ private player: Player;
15
24
 
16
- constructor(entry: any) {
17
- this.entry = entry;
25
+ setPlayer(player: Player) {
26
+ this.player = player;
18
27
  }
19
28
 
20
29
  isAdBreak(event: string): boolean {
@@ -30,6 +39,10 @@ export class AnalyticsTracker {
30
39
  return !!isAdBreak;
31
40
  }
32
41
 
42
+ get entry(): ZappEntry {
43
+ return this.player?.getEntry();
44
+ }
45
+
33
46
  getDuration(
34
47
  event: QuickBrickPlayer.Events,
35
48
  eventData: QuickBrickPlayer.EventData
@@ -215,13 +228,9 @@ export class AnalyticsTracker {
215
228
 
216
229
  handleAnalyticEvent(
217
230
  event: QuickBrickPlayer.Events,
218
- eventData: QuickBrickPlayer.EventData = {},
219
- entry: ZappEntry,
220
- playerState = null
231
+ eventData: QuickBrickPlayer.EventData = {}
221
232
  ) {
222
- this.entry = entry;
223
- this.playerState = playerState;
224
-
233
+ this.playerState = this.player?.getState();
225
234
  postAnalyticEvent(event, this.prepareAnalyticPayload(event, eventData));
226
235
  }
227
236
  }
@@ -85,18 +85,7 @@ export class AccessibilityManager {
85
85
  textToRead = localizedMessage;
86
86
  }
87
87
 
88
- if (this.ttsManager) {
89
- logger.debug({
90
- message: `Reading text: ${textToRead}`,
91
- data: {
92
- textToRead,
93
- text,
94
- keyOfLocalizedText,
95
- },
96
- });
97
-
98
- this.ttsManager.readText(textToRead);
99
- }
88
+ this.ttsManager && this.ttsManager.readText(textToRead);
100
89
 
101
90
  this.announcements$.next({
102
91
  message: textToRead,
@@ -138,6 +127,17 @@ export class AccessibilityManager {
138
127
  };
139
128
  }
140
129
 
130
+ public getInputAccessibilityProps(inputName: string): AccessibilityProps {
131
+ return {
132
+ accessibilityLabel: inputName,
133
+ accessibilityHint: `Enter text into ${inputName}`,
134
+ "aria-label": inputName,
135
+ "aria-description": `Enter text into ${inputName}`,
136
+ accessibilityRole: "textbox" as AccessibilityRole,
137
+ "aria-role": "textbox",
138
+ };
139
+ }
140
+
141
141
  public getLocalizedMessage(key: string): string | void {
142
142
  if (!key) return;
143
143
 
@@ -1,6 +1,10 @@
1
1
  import { BehaviorSubject } from "rxjs";
2
2
 
3
3
  import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
4
+ import {
5
+ isLgPlatform,
6
+ isSamsungPlatform,
7
+ } from "@applicaster/zapp-react-dom-app/App/Loader/utils/platform";
4
8
 
5
9
  import { PLATFORM_KEYS, PLATFORMS } from "./const";
6
10
  import { createLogger, utilsLogger } from "../../logger";
@@ -25,8 +29,7 @@ const { log_debug } = createLogger({
25
29
  parent: utilsLogger,
26
30
  });
27
31
 
28
- export const getUserAgent = () =>
29
- window?.navigator?.userAgent || global?.navigator?.userAgent;
32
+ export const getUserAgent = () => window?.navigator?.userAgent;
30
33
 
31
34
  /** VIZIO UTILS */
32
35
 
@@ -39,6 +42,8 @@ export const getUserAgent = () =>
39
42
  export const isVizioPlatform = () => {
40
43
  const userAgent = getUserAgent();
41
44
 
45
+ if (!userAgent) return false;
46
+
42
47
  const isVizioAgent =
43
48
  userAgent.includes(PLATFORMS.vizio) ||
44
49
  userAgent.includes(PLATFORMS.smartcast) ||
@@ -178,6 +183,34 @@ export class TTSManager {
178
183
  }
179
184
 
180
185
  readText(text: string) {
186
+ if (isSamsungPlatform() && window.speechSynthesis) {
187
+ const utterance = new SpeechSynthesisUtterance(text);
188
+ window.speechSynthesis.speak(utterance);
189
+ }
190
+
191
+ if (isLgPlatform() && window.webOS?.service) {
192
+ try {
193
+ window.webOS.service.request("luna://com.webos.service.tts", {
194
+ method: "speak",
195
+ onFailure(error: any) {
196
+ log_debug("There was a failure setting up webOS TTS service", {
197
+ error,
198
+ });
199
+ },
200
+ onSuccess(response: any) {
201
+ log_debug("webOS TTS service is configured successfully", {
202
+ response,
203
+ });
204
+ },
205
+ parameters: {
206
+ text,
207
+ },
208
+ });
209
+ } catch (error) {
210
+ log_debug("webOS TTS service error", { error });
211
+ }
212
+ }
213
+
181
214
  if (!window.VIZIO?.Chromevox) return;
182
215
 
183
216
  window.VIZIO.Chromevox.play(text);
@@ -3,7 +3,6 @@ import * as R from "ramda";
3
3
  import { subscriber } from "../../functionUtils";
4
4
  import { utilsLogger, createLogger } from "../../logger";
5
5
  import { Player } from "./player";
6
- import { AnalyticsTracker } from "../../analyticsUtils/analyticsTracker";
7
6
  import {
8
7
  hasTizenAPIs,
9
8
  subscribeToDeviceNetworkChanges,
@@ -37,7 +36,10 @@ export interface PlayerLifecycleListener {
37
36
  }
38
37
 
39
38
  type PlayersMap = {
40
- [id: string]: { player: Player; isInViewport: boolean };
39
+ [id: string]: {
40
+ player: Player;
41
+ isInViewport: boolean;
42
+ };
41
43
  };
42
44
 
43
45
  export class PlayerManager {
@@ -46,7 +48,6 @@ export class PlayerManager {
46
48
  private playersMap: PlayersMap = {};
47
49
  private manager = subscriber();
48
50
  private castingReceiver: Player;
49
- private analyticsTracker: AnalyticsTracker;
50
51
  private lifecycleListeners: PlayerLifecycleListener[] = [];
51
52
 
52
53
  public static getInstance() {
@@ -57,7 +58,7 @@ export class PlayerManager {
57
58
  return PlayerManager.instance;
58
59
  }
59
60
 
60
- public getActivePlayerId = (): String | null => {
61
+ public getActivePlayerId = (): string | null => {
61
62
  return this.activePlayerId;
62
63
  };
63
64
 
@@ -102,7 +103,10 @@ export class PlayerManager {
102
103
  }
103
104
 
104
105
  private addPlayer = (id: string, player: Player) => {
105
- this.playersMap[id] = { player: player, isInViewport: false };
106
+ this.playersMap[id] = {
107
+ player: player,
108
+ isInViewport: false,
109
+ };
106
110
  };
107
111
 
108
112
  public isPlayerRegistered = (id: string) => !!this.playersMap[id];
@@ -251,28 +255,6 @@ export class PlayerManager {
251
255
  this.castingReceiver = null;
252
256
  }
253
257
 
254
- /**
255
- * Handles analytic events, generates a new analytics tracker and transmits events
256
- * Passes entry data, and playerState to the tracker for additional context
257
- * The end result is an event formatted and passed to AnalyticsBridge.postEvent
258
- * When we get the synthetic event we need to pull the data in the e object
259
- * e contains the return value from native
260
- */
261
- public handleAnalyticEvent(event, data = {}, entry: ZappEntry) {
262
- const playerState = this.getActivePlayer()?.getState();
263
-
264
- if (!this.analyticsTracker?.handleAnalyticEvent) {
265
- this.analyticsTracker = new AnalyticsTracker(entry);
266
- }
267
-
268
- this.analyticsTracker?.handleAnalyticEvent?.(
269
- event,
270
- data,
271
- entry,
272
- playerState
273
- );
274
- }
275
-
276
258
  /**
277
259
  * Method retrieves the instantiated player instance controller
278
260
  * Instance controller exposes methods like getContentDuration, getContentPosision
@@ -46,7 +46,7 @@ export const playerFactory = (
46
46
 
47
47
  // TODO: Fix type:
48
48
  const playerProtocol: PlayerConstructor =
49
- playerPlugin?.module?.playerProtocol(config?.screenConfig);
49
+ playerPlugin?.module?.playerProtocol(config?.screenConfig, config?.entry);
50
50
 
51
51
  const playerView = (playerProtocol as any)?.Component || null;
52
52
 
@@ -0,0 +1,57 @@
1
+ import { useState, useEffect, useCallback } from "react";
2
+ import { Player } from "../player";
3
+ import { usePlayer } from "../usePlayer";
4
+ import { useThrottle } from "../../../functionUtils";
5
+
6
+ /**
7
+ *
8
+ * @param listenerId - A unique identifier for the listener.
9
+ * @param playerId - (Optional) The ID of the player to observe.
10
+ * @returns The current playback time in seconds.
11
+ */
12
+ export const usePlayerCurrentTime = (
13
+ listenerId: string,
14
+ playerId?: string
15
+ ): number | null => {
16
+ const [currentTime, setCurrentTime] = useState<number | null>(null);
17
+ const player: Player = usePlayer(playerId);
18
+
19
+ const updateCurrentTime = useCallback(() => {
20
+ if (player) {
21
+ const newCurrentTime = player.getContentPosition();
22
+
23
+ setCurrentTime((prevTime) =>
24
+ prevTime !== newCurrentTime ? newCurrentTime : prevTime
25
+ );
26
+ }
27
+ }, [player]);
28
+
29
+ const { throttledFunction: throttledUpdate, cancel } = useThrottle(
30
+ updateCurrentTime,
31
+ 1000
32
+ );
33
+
34
+ useEffect(() => {
35
+ if (player) {
36
+ throttledUpdate();
37
+
38
+ const listeners = {
39
+ onVideoProgress: throttledUpdate,
40
+ };
41
+
42
+ const id = `${listenerId}_player_current_time`;
43
+
44
+ player.addListener({
45
+ id: id,
46
+ listener: listeners,
47
+ });
48
+
49
+ return () => {
50
+ player.removeListener(id);
51
+ cancel();
52
+ };
53
+ }
54
+ }, [player, listenerId, throttledUpdate, cancel]);
55
+
56
+ return currentTime;
57
+ };
@@ -61,7 +61,7 @@ export class PlayerNative extends Player {
61
61
  currentPlayerComponent = (): ReactComponent<PlayerProps> =>
62
62
  this.playerComponent?.current;
63
63
 
64
- getState = () => this.currentPlayerComponent()?.["getState"]?.() ?? {};
64
+ getState = () => this.playerState;
65
65
 
66
66
  // TODO: Try to add module interface
67
67
  invokeNativeFunction = (funcName: string, ...params: any[]): boolean => {
@@ -58,7 +58,7 @@ export const usePlayerControllerSetup = ({
58
58
  }
59
59
 
60
60
  if (!playerManager) {
61
- return null;
61
+ return;
62
62
  }
63
63
 
64
64
  if (playerManager.isPlayerRegistered(playerId)) {
@@ -16,24 +16,28 @@ export const usePlayerState = (
16
16
  listenerId: string,
17
17
  playerId?: string
18
18
  ): PlayerState => {
19
- const [currentTime, setCurrentTime] = React.useState<number>(null);
20
- const [duration, setDuration] = React.useState<number>(null);
21
- const [seekableDuration, setSeekableDuration] = React.useState<number>(null);
22
- const [isLive, setIsLive] = React.useState<boolean>(null);
23
- const [isBuffering, setIsBuffering] = React.useState<boolean>(false);
24
- const [isReadyToPlay, setIsReadyToPlay] = React.useState<boolean>(false);
25
- const [isPaused, setIsPaused] = React.useState<boolean>(false);
19
+ const [state, setState] = React.useState<PlayerState>({
20
+ currentTime: null,
21
+ duration: null,
22
+ seekableDuration: null,
23
+ isLive: null,
24
+ isPaused: null,
25
+ isBuffering: false,
26
+ isReadyToPlay: false,
27
+ });
26
28
 
27
29
  const player: Player = usePlayer(playerId);
28
30
 
29
31
  const onPlayerChangeState = React.useCallback(() => {
30
- setIsBuffering(player.isBuffering());
31
- setIsReadyToPlay(player.isReadyToPlay);
32
- setCurrentTime(player.getContentPosition());
33
- setDuration(player.getDuration());
34
- setSeekableDuration(player.getSeekableDuration());
35
- setIsLive(player.isLive());
36
- setIsPaused(player.isPaused());
32
+ setState({
33
+ currentTime: player.getContentPosition(),
34
+ duration: player.getDuration(),
35
+ seekableDuration: player.getSeekableDuration(),
36
+ isLive: player.isLive(),
37
+ isPaused: player.isPaused(),
38
+ isBuffering: player.isBuffering(),
39
+ isReadyToPlay: player.isReadyToPlay(),
40
+ });
37
41
  }, [player]);
38
42
 
39
43
  React.useEffect(() => {
@@ -55,24 +59,5 @@ export const usePlayerState = (
55
59
  }
56
60
  }, [player]);
57
61
 
58
- return React.useMemo(
59
- () => ({
60
- currentTime,
61
- duration,
62
- seekableDuration,
63
- isLive,
64
- isPaused,
65
- isBuffering,
66
- isReadyToPlay,
67
- }),
68
- [
69
- currentTime,
70
- duration,
71
- seekableDuration,
72
- isLive,
73
- isPaused,
74
- isBuffering,
75
- isReadyToPlay,
76
- ]
77
- );
62
+ return state;
78
63
  };
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable unused-imports/no-unused-vars */
2
2
  import * as R from "ramda";
3
+ import { invariant } from "@applicaster/zapp-react-native-utils/errorUtils";
3
4
 
4
5
  /**
5
6
  * shifts an array by the given offset. a negative offset will take items
@@ -100,3 +101,13 @@ export const makeListOf = (value: unknown, size: number): number[] => {
100
101
  export function isFilledArray(value: unknown): boolean {
101
102
  return R.is(Array, value) && R.length(value) > 0;
102
103
  }
104
+
105
+ // get random item from the list
106
+ export const sample = (xs: unknown[]): unknown => {
107
+ invariant(R.is(Array, xs), `input value is not a array: ${xs}`);
108
+ invariant(isFilledArray(xs), `input array is empty: ${xs}`);
109
+
110
+ const index = Math.floor(Math.random() * xs.length);
111
+
112
+ return xs[index];
113
+ };
@@ -468,3 +468,36 @@ export const getColorFromData = ({
468
468
 
469
469
  return valueFromLayout;
470
470
  };
471
+
472
+ export const getImageStyles = ({
473
+ image,
474
+ value,
475
+ }: {
476
+ image: any;
477
+ value: any;
478
+ }) => {
479
+ return {
480
+ width: image.imageContentWidth,
481
+ height: image.imageContentHeight,
482
+ aspectRatio: image.aspectRatio,
483
+ borderRadius: toNumberWithDefaultZero(value("image_corner_radius")),
484
+ };
485
+ };
486
+
487
+ export const getImageContainerPaddingStyles = ({ value }: { value: any }) => {
488
+ return {
489
+ paddingTop: value("image_padding_top"),
490
+ paddingBottom: value("image_padding_bottom"),
491
+ paddingLeft: value("image_padding_left"),
492
+ paddingRight: value("image_padding_right"),
493
+ };
494
+ };
495
+
496
+ export const getImageContainerMarginStyles = ({ value }: { value: any }) => {
497
+ return {
498
+ marginTop: value("image_margin_top"),
499
+ marginBottom: value("image_margin_bottom"),
500
+ marginLeft: value("image_margin_left"),
501
+ marginRight: value("image_margin_right"),
502
+ };
503
+ };