@applicaster/zapp-react-native-ui-components 12.0.0-rc.5 → 12.0.0-rc.50

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 (95) hide show
  1. package/Components/AnimatedInOut/index.tsx +18 -10
  2. package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
  3. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
  4. package/Components/AudioPlayer/helpers.tsx +1 -0
  5. package/Components/Cell/Cell.tsx +2 -2
  6. package/Components/Cell/CellWithFocusable.tsx +21 -14
  7. package/Components/Cell/TvOSCellComponent.tsx +2 -2
  8. package/Components/ErrorScreen/index.tsx +1 -1
  9. package/Components/FocusableCell/index.tsx +7 -7
  10. package/Components/FocusableList/FocusableChild.ts +24 -14
  11. package/Components/FocusableList/FocusableItem.tsx +1 -1
  12. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  13. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  14. package/Components/FocusableList/index.tsx +26 -25
  15. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
  16. package/Components/HandlePlayable/HandlePlayable.tsx +1 -1
  17. package/Components/HookRenderer/index.tsx +1 -6
  18. package/Components/Image/Image.android.tsx +3 -22
  19. package/Components/Image/Image.tsx +18 -2
  20. package/Components/Image/utils/index.ts +39 -0
  21. package/Components/Layout/TV/__tests__/index.test.tsx +1 -34
  22. package/Components/Layout/TV/index.tsx +20 -36
  23. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +0 -1
  24. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
  25. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  26. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  27. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
  28. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  29. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  30. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +8 -10
  31. package/Components/MasterCell/utils/index.ts +16 -5
  32. package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
  33. package/Components/ModalComponent/Button/Item.tsx +82 -0
  34. package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
  35. package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
  36. package/Components/ModalComponent/Button/assets.ts +4 -0
  37. package/Components/ModalComponent/Button/index.tsx +111 -0
  38. package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
  39. package/Components/ModalComponent/Header/Title.tsx +70 -0
  40. package/Components/ModalComponent/Header/assets.ts +4 -0
  41. package/Components/ModalComponent/Header/index.tsx +71 -0
  42. package/Components/ModalComponent/Header/utils.ts +79 -0
  43. package/Components/ModalComponent/index.tsx +38 -0
  44. package/Components/ModalComponent/utils.ts +126 -0
  45. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +4 -7
  46. package/Components/PlayerContainer/PlayerContainer.tsx +56 -29
  47. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  48. package/Components/PlayerImageBackground/index.tsx +34 -8
  49. package/Components/River/ComponentsMap/ComponentsMap.tsx +25 -0
  50. package/Components/River/River.tsx +8 -5
  51. package/Components/River/TV/River.tsx +2 -2
  52. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  53. package/Components/River/__tests__/componentsMap.test.js +7 -0
  54. package/Components/River/useScreenConfiguration.ts +5 -6
  55. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
  56. package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
  57. package/Components/RouteManager/index.tsx +5 -3
  58. package/Components/Screen/__tests__/Screen.test.tsx +48 -36
  59. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +20 -6
  60. package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
  61. package/Components/Screen/index.tsx +92 -44
  62. package/Components/Screen/navigationHandler.ts +0 -82
  63. package/Components/Screen/orientationHandler.ts +1 -1
  64. package/Components/ScreenResolver/index.tsx +1 -1
  65. package/Components/Tabs/TV/styles.ts +1 -1
  66. package/Components/TrackedView/index.tsx +6 -3
  67. package/Components/VideoLive/LiveImageManager.ts +42 -13
  68. package/Components/VideoLive/PlayerLiveImageComponent.tsx +81 -72
  69. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  70. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +22 -27
  71. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  72. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +335 -0
  73. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +144 -0
  74. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +438 -0
  75. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +143 -0
  76. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  77. package/Components/VideoModal/ModalAnimation/utils.ts +299 -0
  78. package/Components/VideoModal/PlayerDetails.tsx +3 -0
  79. package/Components/VideoModal/PlayerWrapper.tsx +119 -95
  80. package/Components/VideoModal/VideoModal.tsx +8 -25
  81. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +0 -2
  82. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +8 -3
  83. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +242 -166
  84. package/Components/VideoModal/hooks/useModalSize.ts +13 -4
  85. package/Components/VideoModal/utils.ts +3 -3
  86. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
  87. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
  88. package/Components/ZappUIComponent/index.tsx +22 -12
  89. package/Components/index.js +2 -0
  90. package/Contexts/ScreenContext/index.tsx +1 -4
  91. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  92. package/Decorators/RiverResolver/index.tsx +0 -4
  93. package/Decorators/index.tsx +0 -4
  94. package/package.json +5 -6
  95. package/Components/VideoModal/hooks/usePosition.ts +0 -50
@@ -17,9 +17,12 @@ type TrackedViewProps = {
17
17
  export const TrackedView = memo(function TrackedView(props: TrackedViewProps) {
18
18
  const children = props.children;
19
19
 
20
- const onPositionUpdated = useCallback((event: TrackedViewNativeProps) => {
21
- props?.onPositionUpdated(event.nativeEvent);
22
- }, []);
20
+ const onPositionUpdated = useCallback(
21
+ (event: TrackedViewNativeProps) => {
22
+ props.onPositionUpdated(event.nativeEvent);
23
+ },
24
+ [props.onPositionUpdated]
25
+ );
23
26
 
24
27
  if (isTV()) {
25
28
  return <View {...(props as any)}>{children}</View>;
@@ -6,6 +6,7 @@ import {
6
6
  import { setUserCellPlayerMutedPreference } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/userCellPlayerMutedPreference";
7
7
  import { loggerLiveImageManager } from "./loggerHelper";
8
8
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
9
+ import { Component } from "react";
9
10
 
10
11
  const { log_debug, log_info, log_error } = loggerLiveImageManager;
11
12
 
@@ -36,7 +37,8 @@ type Position = {
36
37
  type LiveImageProps = {
37
38
  player: Player;
38
39
  playerId: string;
39
- setMode: (type: LiveImageType) => void;
40
+ setMode?: (type: LiveImageType) => void;
41
+ component: Component;
40
42
  // TODO: ...primary, powerCell, tvGallery, etc.
41
43
  // type: string;
42
44
  };
@@ -164,7 +166,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
164
166
 
165
167
  // Fake close event, because we unmount native view
166
168
  item.player?.onPlayerClose();
167
- item.setMode(LiveImageType.Image);
169
+ item.setMode?.(LiveImageType.Image);
168
170
 
169
171
  if (item === this.currentlyPlaying) {
170
172
  this.currentlyPlaying = null;
@@ -191,8 +193,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
191
193
  }
192
194
 
193
195
  this.currentlyPlaying = item;
194
-
195
- item.setMode(LiveImageType.Video);
196
+ item.setMode?.(LiveImageType.Video);
196
197
 
197
198
  if (item.player.playerState.isReadyToPlay) {
198
199
  item.player.play();
@@ -209,6 +210,11 @@ export class LiveImageManager implements PlayerLifecycleListener {
209
210
  this.pauseItem(item);
210
211
  };
211
212
 
213
+ public onLiveImageCompleted = (_item: LiveImage) => {
214
+ // TODO: Notify listeners that player has died
215
+ // Do not try look for new playable, otherwise you will restart video finish to play
216
+ };
217
+
212
218
  public muteAll = () => {
213
219
  log_debug("muteAll");
214
220
 
@@ -235,6 +241,14 @@ export class LiveImageManager implements PlayerLifecycleListener {
235
241
  const playerItem = this.findNextPlayableItem();
236
242
 
237
243
  if (playerItem) {
244
+ if (!playerItem.isFullyVisible) {
245
+ log_error(
246
+ `checkPlayerPosition: trying to start playback currently invisible item: ${playerInfo(
247
+ playerItem.player
248
+ )}`
249
+ );
250
+ }
251
+
238
252
  // Will also check if it's item that already playing
239
253
  this.playLiveImage(playerItem);
240
254
  }
@@ -242,7 +256,6 @@ export class LiveImageManager implements PlayerLifecycleListener {
242
256
 
243
257
  onPrimaryPlayerClosed = () => {
244
258
  const playerItem = this.findNextPlayableItem();
245
-
246
259
  log_info("onPrimaryPlayerClosed: starting to play visible live-image");
247
260
 
248
261
  this.currentlyPlaying = null;
@@ -356,12 +369,12 @@ export class LiveImageManager implements PlayerLifecycleListener {
356
369
  // TODO: This branch was used when we had play button to toggle replay.
357
370
  // Now we mute button instead but we keep it just in case.
358
371
  if (IS_ALLOWED_REPLAY && !isTV() && isCurrentItemEnded) {
359
- this.currentlyPlaying.player.seekTo(0);
360
- this.currentlyPlaying.player.pause();
372
+ this.currentlyPlaying?.player.seekTo(0);
373
+ this.currentlyPlaying?.player.pause();
361
374
 
362
375
  // TODO: ...Maybe player some other item
363
376
  } else {
364
- item.setMode(LiveImageType.Image);
377
+ item.setMode?.(LiveImageType.Image);
365
378
  }
366
379
 
367
380
  // Prevent receiving onEnd event from both `close` and `end` player events
@@ -377,10 +390,25 @@ export class LiveImageManager implements PlayerLifecycleListener {
377
390
  };
378
391
 
379
392
  onLiveImageError = (item: LiveImage, error: Error) => {
380
- item.setMode(LiveImageType.Image);
393
+ item.setMode?.(LiveImageType.Image);
381
394
 
382
- if (this.currentlyPlaying === item) {
395
+ const currentItem = this.currentlyPlaying;
396
+
397
+ log_debug(
398
+ `onLiveImageError: error: ${error.message}, live-image ${playerInfo(
399
+ item.player
400
+ )}, currentPlayingId: ${
401
+ this.currentlyPlaying?.playerId
402
+ }, primaryPlayerId: ${this.primaryPlayer?.playerId}`
403
+ );
404
+
405
+ if (currentItem === item) {
383
406
  this.currentlyPlaying = null;
407
+
408
+ log_debug(
409
+ `onLiveImageError: currentitem: ${currentItem.playerId} was removed`
410
+ );
411
+
384
412
  // TODO: ...Maybe player some other item
385
413
  }
386
414
 
@@ -389,8 +417,8 @@ export class LiveImageManager implements PlayerLifecycleListener {
389
417
  event: {
390
418
  item,
391
419
  error,
392
- playerId: this.currentlyPlaying?.playerId,
393
- primaryPlayerId: this.primaryPlayer?.playerId,
420
+ playerId: currentItem?.playerId,
421
+ primaryPlayerId: currentItem?.playerId,
394
422
  },
395
423
  });
396
424
  };
@@ -446,12 +474,13 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
446
474
  };
447
475
 
448
476
  readonly playerId: string;
477
+ readonly component: Component;
449
478
 
450
479
  constructor(props: LiveImageProps) {
451
480
  this.player = props.player;
452
481
  this.setMode = props.setMode;
453
482
  this.playerId = this.player.playerId;
454
-
483
+ this.component = props.component;
455
484
  this.player.addListener({ id: "live-image", listener: this });
456
485
  }
457
486
 
@@ -1,7 +1,6 @@
1
1
  // /// <reference types="@applicaster/applicaster-types" />
2
2
  //
3
3
  import * as React from "react";
4
- import * as R from "ramda";
5
4
 
6
5
  import { AppState, AppStateStatus } from "react-native";
7
6
  import { TrackedView } from "../TrackedView";
@@ -9,14 +8,13 @@ import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/l
9
8
 
10
9
  import { playerFactory } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerFactory";
11
10
 
12
- import { BufferAnimation } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/BufferAnimation";
13
11
  import {
14
12
  isApplePlatform,
15
13
  isTV,
16
14
  } from "@applicaster/zapp-react-native-utils/reactUtils";
17
15
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
18
16
  import { getUserCellPlayerMutedPreference } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/userCellPlayerMutedPreference";
19
- import { PlayerImageBackground } from "../PlayerImageBackground";
17
+ import Image from "@applicaster/zapp-react-native-ui-components/Components/MasterCell/DefaultComponents/Image";
20
18
  import { LiveImage, LiveImageManager, LiveImageType } from "./LiveImageManager";
21
19
 
22
20
  import { AnimatedInOut } from "@applicaster/zapp-react-native-ui-components/Components/AnimatedInOut";
@@ -25,12 +23,13 @@ import { loggerLiveImageComponent } from "./loggerHelper";
25
23
  import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
26
24
  import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/conts";
27
25
  import { getAutoplaySettings } from "./utils";
26
+ import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
27
+ import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
28
28
 
29
29
  const { log_error } = loggerLiveImageComponent;
30
30
 
31
- const isMeasurement = (item) => {
32
- return item.id.startsWith("pre-measurement-");
33
- };
31
+ const isMeasurement = (item: ZappEntry) =>
32
+ isString(item.id) && item.id.startsWith("pre-measurement-");
34
33
 
35
34
  type Props = {
36
35
  item: ZappEntry;
@@ -40,12 +39,13 @@ type Props = {
40
39
  imageKey?: string;
41
40
  screenConfig: Record<string, any>;
42
41
  audioMutedByDefault?: boolean;
42
+ uri: string; // cover image url
43
43
  };
44
44
 
45
45
  const PlayerLiveImageComponent = (props: Props) => {
46
46
  const dimensions = useDimensions("screen", { fullDimensions: true });
47
47
 
48
- const { item, style, playerId, imageKey } = props;
48
+ const { item, style, playerId } = props;
49
49
 
50
50
  const timeoutRef = React.useRef<NodeJS.Timeout | null>(null);
51
51
  const [mode, setMode] = React.useState(LiveImageType.Image);
@@ -55,20 +55,17 @@ const PlayerLiveImageComponent = (props: Props) => {
55
55
  isTV() ? false : getUserCellPlayerMutedPreference(props.audioMutedByDefault)
56
56
  );
57
57
 
58
- const [PlayerViewComponent, setPlayerViewComponent] = React.useState(null);
59
- const Player: any = PlayerViewComponent || BufferAnimation;
60
-
61
58
  const ref = React.useRef(null);
62
59
 
63
- const liveImageItem: LiveImage = React.useMemo(() => {
64
- /**
65
- * Debounced setMode function.
66
- *
67
- * Sets to video with a 1sec delay. Set to image happens immediately and cancels the timeout.
68
- *
69
- * @param type video or image
70
- */
71
- const setModeDebounced = (type: LiveImageType) => {
60
+ /**
61
+ * Debounced setMode function.
62
+ *
63
+ * Sets to video with a 1sec delay. Set to image happens immediately and cancels the timeout.
64
+ *
65
+ * @param type video or image
66
+ */
67
+ const setModeDebounced = React.useCallback(
68
+ (type: LiveImageType) => {
72
69
  if (type === LiveImageType.Video) {
73
70
  if (type === mode) {
74
71
  return;
@@ -80,45 +77,45 @@ const PlayerLiveImageComponent = (props: Props) => {
80
77
  } else {
81
78
  if (timeoutRef.current) {
82
79
  clearTimeout(timeoutRef.current);
80
+ timeoutRef.current = null;
83
81
  }
84
82
 
85
83
  setMode(type);
86
84
  }
87
- };
85
+ },
86
+ [mode]
87
+ );
88
+
89
+ const { screenConfig, playerPluginId } = props;
88
90
 
91
+ const liveImageItem: LiveImage = React.useMemo(() => {
89
92
  const playerFactoryItem = playerFactory({
90
93
  player: ref,
91
- listener: props as Record<string, any>,
92
94
  playerId,
93
95
  autoplay: false,
94
96
  entry: item,
95
- muted,
96
- playerPluginId: props?.playerPluginId,
97
- screenConfig: props?.screenConfig,
97
+ muted, // Initial muted state, not needed in dependencies
98
+ playerPluginId: playerPluginId,
99
+ screenConfig: screenConfig,
98
100
  playerRole: PlayerRole.Cell,
99
101
  });
100
102
 
101
- const Component = playerFactoryItem?.Component;
102
-
103
- if (!R.isNil(Component) && Component !== PlayerViewComponent) {
104
- setPlayerViewComponent(Component);
105
- }
106
-
107
103
  if (playerFactoryItem) {
108
104
  return new LiveImage({
109
105
  playerId,
110
106
  player: playerFactoryItem.controller,
111
- setMode: setModeDebounced,
107
+ component: playerFactoryItem?.Component,
112
108
  });
113
109
  } else {
114
110
  throw new Error("Player factory item is null");
115
111
  }
116
- }, [playerId, item?.id]);
112
+ }, [playerId, item, playerPluginId, screenConfig]);
117
113
 
118
- const { start, end } = React.useMemo(
119
- () => getAutoplaySettings(item),
120
- [item?.id]
121
- );
114
+ React.useEffect(() => {
115
+ liveImageItem.setMode = setModeDebounced;
116
+ }, [setModeDebounced, liveImageItem]);
117
+
118
+ const { start, end } = React.useMemo(() => getAutoplaySettings(item), [item]);
122
119
 
123
120
  const controller = liveImageItem.getPlayer();
124
121
  const player = usePlayer(playerId);
@@ -127,6 +124,10 @@ const PlayerLiveImageComponent = (props: Props) => {
127
124
  ref.current = component;
128
125
  };
129
126
 
127
+ const onLiveImageCompleted = React.useCallback(() => {
128
+ LiveImageManager.instance.onLiveImageCompleted(liveImageItem);
129
+ }, [liveImageItem]);
130
+
130
131
  const onStop = React.useCallback(() => {
131
132
  LiveImageManager.instance.pauseLiveImage(liveImageItem);
132
133
  }, [liveImageItem]);
@@ -140,13 +141,14 @@ const PlayerLiveImageComponent = (props: Props) => {
140
141
  const unregister = LiveImageManager.instance.register(liveImageItem);
141
142
 
142
143
  return () => {
143
- unregister();
144
-
145
144
  if (timeoutRef.current) {
146
145
  clearTimeout(timeoutRef.current);
146
+ timeoutRef.current = null;
147
147
  }
148
+
149
+ unregister();
148
150
  };
149
- }, [liveImageItem, item]);
151
+ }, [item, liveImageItem]);
150
152
 
151
153
  React.useEffect(() => {
152
154
  if (!player) {
@@ -178,11 +180,12 @@ const PlayerLiveImageComponent = (props: Props) => {
178
180
  setMuted(false);
179
181
  },
180
182
  onPlayerClose: () => {
183
+ onLiveImageCompleted();
181
184
  setIsReady(false);
182
185
  },
183
186
  },
184
187
  });
185
- }, [player, onStop]);
188
+ }, [player, onStop, onLiveImageCompleted, start, end]);
186
189
 
187
190
  React.useEffect(() => {
188
191
  const subscription = AppState.addEventListener(
@@ -233,40 +236,48 @@ const PlayerLiveImageComponent = (props: Props) => {
233
236
  };
234
237
  }
235
238
  }
236
- }, [liveImageItem]);
237
-
238
- const onPositionUpdated = (data) => {
239
- const normalizedRect = (rect) => {
240
- if (!rect) {
241
- return null;
242
- }
239
+ }, [liveImageItem, playerId, controller]);
240
+
241
+ const onPositionUpdated = React.useCallback(
242
+ (data) => {
243
+ const normalizedRect = (rect: {
244
+ left: number;
245
+ right: number;
246
+ top: number;
247
+ bottom: number;
248
+ }) => {
249
+ if (!rect) {
250
+ return null;
251
+ }
243
252
 
244
- const { left, right, top, bottom } = rect;
253
+ const { left, right, top, bottom } = rect;
245
254
 
246
- return {
247
- left: left / dimensions.width,
248
- right: right / dimensions.width,
249
- top: top / dimensions.height,
250
- bottom: bottom / dimensions.height,
251
- centerX: (left + right) / 2 / dimensions.width,
252
- centerY: (top + bottom) / 2 / dimensions.height,
255
+ return {
256
+ left: left / dimensions.width,
257
+ right: right / dimensions.width,
258
+ top: top / dimensions.height,
259
+ bottom: bottom / dimensions.height,
260
+ centerX: (left + right) / 2 / dimensions.width,
261
+ centerY: (top + bottom) / 2 / dimensions.height,
262
+ };
253
263
  };
254
- };
255
264
 
256
- liveImageItem.position = normalizedRect(data.rect);
265
+ liveImageItem.position = normalizedRect(data.rect);
257
266
 
258
- const newIsFullyVisible = !!data.rect;
267
+ const newIsFullyVisible = !!data.rect;
259
268
 
260
- if (liveImageItem.isFullyVisible !== newIsFullyVisible) {
261
- liveImageItem.isFullyVisible = newIsFullyVisible;
269
+ if (liveImageItem.isFullyVisible !== newIsFullyVisible) {
270
+ liveImageItem.isFullyVisible = newIsFullyVisible;
262
271
 
263
- liveImageItem.isFullyVisible
264
- ? LiveImageManager.instance.onViewportEnter(liveImageItem)
265
- : LiveImageManager.instance.onViewportLeave(liveImageItem);
266
- } else {
267
- LiveImageManager.instance.checkPlayerPosition(liveImageItem);
268
- }
269
- };
272
+ liveImageItem.isFullyVisible
273
+ ? LiveImageManager.instance.onViewportEnter(liveImageItem)
274
+ : LiveImageManager.instance.onViewportLeave(liveImageItem);
275
+ } else {
276
+ LiveImageManager.instance.checkPlayerPosition(liveImageItem);
277
+ }
278
+ },
279
+ [liveImageItem, dimensions]
280
+ );
270
281
 
271
282
  const platformSpecificProps = isApplePlatform()
272
283
  ? { hideNativeControls: true, controls: false }
@@ -276,7 +287,9 @@ const PlayerLiveImageComponent = (props: Props) => {
276
287
  const isVideoMode = mode === LiveImageType.Video;
277
288
  const isBackgroundImageVisible = isImageMode || !isReadyToPlay;
278
289
  const videoStyles = { ...style, position: "absolute", zIndex: -100 };
290
+ const Player: any = liveImageItem.component || BufferAnimation;
279
291
 
292
+ // TODO: nice to have - resolve imageKey when imageUrl is not provided
280
293
  return (
281
294
  <TrackedView
282
295
  testId={`tracked-view-${playerId}-${item.title}`}
@@ -301,11 +314,7 @@ const PlayerLiveImageComponent = (props: Props) => {
301
314
  animationConfig={overlayFadeIn}
302
315
  staticStyles={style}
303
316
  >
304
- <PlayerImageBackground
305
- entry={item}
306
- style={style}
307
- imageKey={imageKey}
308
- />
317
+ <Image {...props} resizeMode="cover" style={style} applyMaxSize />
309
318
  </AnimatedInOut>
310
319
  }
311
320
  </TrackedView>
@@ -3,7 +3,14 @@ import * as React from "react";
3
3
  import { render } from "@testing-library/react-native";
4
4
  import * as PlayerFactory from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerFactory";
5
5
  import { Player } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/player";
6
+ import { ThemeContext } from "@applicaster/zapp-react-native-utils/theme";
7
+ import * as navigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
8
+
6
9
  import { PlayerLiveImage } from "../PlayerLiveImageComponent";
10
+ import * as useNavigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
11
+
12
+ import { Provider } from "react-redux";
13
+ import configureStore from "redux-mock-store";
7
14
 
8
15
  const seekTo = jest.fn();
9
16
 
@@ -16,6 +23,27 @@ jest.mock(
16
23
  })
17
24
  );
18
25
 
26
+ jest
27
+ .spyOn(useNavigationHooks, "useNavigation")
28
+ .mockImplementation(() => ({ videoModalState: { visible: false } }));
29
+
30
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation");
31
+
32
+ const mockStore = configureStore();
33
+
34
+ const store = mockStore({ appData: {} });
35
+
36
+ const Wrapper = ({ children }: { children: React.ReactChildren }) => (
37
+ /* @ts-ignore */
38
+ <Provider store={store}>
39
+ <ThemeContext.Provider
40
+ value={{ themes: { myTheme: {} }, selectedThemeId: "myTheme" }}
41
+ >
42
+ {children}
43
+ </ThemeContext.Provider>
44
+ </Provider>
45
+ );
46
+
19
47
  describe("PlayerLiveImageComponent", () => {
20
48
  const defaultProps = {
21
49
  item: { id: "1", title: "Test", type: { value: "" } },
@@ -36,10 +64,16 @@ describe("PlayerLiveImageComponent", () => {
36
64
  controller: new Player({}),
37
65
  Component: () => null,
38
66
  }));
67
+
68
+ jest.spyOn(navigationHooks, "useIsScreenActive").mockReturnValue(true);
39
69
  });
40
70
 
41
71
  it("should render correctly with default props", () => {
42
- const component = render(<PlayerLiveImage {...defaultProps} />);
72
+ const component = render(
73
+ <Wrapper>
74
+ <PlayerLiveImage {...defaultProps} />
75
+ </Wrapper>
76
+ );
43
77
 
44
78
  expect(component.toJSON()).toMatchSnapshot();
45
79
  });
@@ -53,7 +87,11 @@ describe("PlayerLiveImageComponent", () => {
53
87
  },
54
88
  };
55
89
 
56
- render(<PlayerLiveImage {...props} />);
90
+ render(
91
+ <Wrapper>
92
+ <PlayerLiveImage {...props} />
93
+ </Wrapper>
94
+ );
57
95
 
58
96
  // TODO: implement this test
59
97
  });
@@ -67,7 +105,11 @@ describe("PlayerLiveImageComponent", () => {
67
105
  },
68
106
  };
69
107
 
70
- render(<PlayerLiveImage {...props} />);
108
+ render(
109
+ <Wrapper>
110
+ <PlayerLiveImage {...props} />
111
+ </Wrapper>
112
+ );
71
113
 
72
114
  // TODO: implement this test
73
115
  });
@@ -13,35 +13,30 @@ exports[`PlayerLiveImageComponent should render correctly with default props 1`]
13
13
  }
14
14
  }
15
15
  >
16
- <View
17
- accessibilityIgnoresInvertColors={true}
18
- style={Object {}}
19
- >
20
- <Image
21
- resizeMode="cover"
22
- source={
23
- Object {
24
- "uri": undefined,
25
- }
16
+ <Image
17
+ applyMaxSize={true}
18
+ audioMutedByDefault={false}
19
+ imageKey="image1"
20
+ item={
21
+ Object {
22
+ "id": "1",
23
+ "title": "Test",
24
+ "type": Object {
25
+ "value": "",
26
+ },
26
27
  }
27
- style={
28
- Array [
29
- Object {
30
- "bottom": 0,
31
- "left": 0,
32
- "position": "absolute",
33
- "right": 0,
34
- "top": 0,
35
- },
36
- Object {
37
- "height": undefined,
38
- "width": undefined,
39
- },
40
- undefined,
41
- ]
28
+ }
29
+ onError={[Function]}
30
+ playerId="player1"
31
+ resizeMode="cover"
32
+ screenConfig={Object {}}
33
+ source={
34
+ Object {
35
+ "uri": "",
42
36
  }
43
- />
44
- </View>
37
+ }
38
+ style={Object {}}
39
+ />
45
40
  </View>
46
41
  </TrackedView>
47
42
  `;
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+ import { Animated, ViewStyle } from "react-native";
3
+
4
+ import {
5
+ useModalAnimationContext,
6
+ PlayerAnimationStateEnum,
7
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
8
+ import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks";
9
+
10
+ type Props = {
11
+ style: ViewStyle[];
12
+ children: React.ReactNode;
13
+ };
14
+
15
+ export const AnimatedPlayerModalWrapper = (props: Props) => {
16
+ const {
17
+ playerAnimationState,
18
+ animatedValues: { lastScrollY, dragScrollY, translateYOffset },
19
+ modalSnapPoints,
20
+ setStartComponentsAnimation,
21
+ lastScrollYValue,
22
+ } = useModalAnimationContext();
23
+
24
+ const { height: screenHeight } = useDimensions("screen", {
25
+ deviceInfo: true,
26
+ });
27
+
28
+ const interpolateConfig: Animated.InterpolationConfigType = React.useMemo(
29
+ () => ({
30
+ inputRange: modalSnapPoints,
31
+ outputRange: modalSnapPoints,
32
+ extrapolate: "clamp",
33
+ }),
34
+ [modalSnapPoints]
35
+ );
36
+
37
+ let translateY;
38
+
39
+ if (playerAnimationState === PlayerAnimationStateEnum.drag_player) {
40
+ translateY = translateYOffset.interpolate(interpolateConfig);
41
+ } else {
42
+ const reverseLastScrollY = Animated.multiply(
43
+ new Animated.Value(-1),
44
+ lastScrollY
45
+ );
46
+
47
+ translateY = Animated.add(
48
+ translateYOffset,
49
+ Animated.add(dragScrollY, reverseLastScrollY)
50
+ ).interpolate(interpolateConfig);
51
+ }
52
+
53
+ React.useEffect(() => {
54
+ const translateYListener = translateY.addListener(({ value }) => {
55
+ const preparedValue = Math.round(
56
+ Math.abs(value) - lastScrollYValue.current
57
+ );
58
+
59
+ (playerAnimationState === PlayerAnimationStateEnum.minimize ||
60
+ playerAnimationState === PlayerAnimationStateEnum.maximaze) &&
61
+ setStartComponentsAnimation(
62
+ preparedValue >= Math.round((screenHeight * 10) / 100)
63
+ );
64
+ });
65
+
66
+ return () => {
67
+ translateYListener && translateY.removeListener(translateYListener);
68
+ };
69
+ }, [playerAnimationState]);
70
+
71
+ return (
72
+ <Animated.View
73
+ style={[props.style, { transform: [{ translateY: translateY }] }]}
74
+ >
75
+ {props.children}
76
+ </Animated.View>
77
+ );
78
+ };