@applicaster/zapp-react-native-ui-components 12.0.0-alpha.7725319213 → 12.0.0-alpha.7843515010

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 (148) hide show
  1. package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
  2. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
  3. package/Components/AudioPlayer/helpers.tsx +1 -0
  4. package/Components/BaseFocusable/index.tsx +3 -3
  5. package/Components/Cell/Cell.tsx +32 -11
  6. package/Components/Cell/CellWithFocusable.tsx +40 -19
  7. package/Components/Cell/TvOSCellComponent.tsx +5 -9
  8. package/Components/ErrorScreen/index.tsx +1 -1
  9. package/Components/Focusable/Focusable.tsx +6 -1
  10. package/Components/FocusableCell/index.tsx +7 -7
  11. package/Components/FocusableGroup/index.tsx +11 -2
  12. package/Components/FocusableList/FocusableChild.ts +24 -14
  13. package/Components/FocusableList/FocusableItem.tsx +1 -1
  14. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  15. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  16. package/Components/FocusableList/index.tsx +26 -25
  17. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +6 -3
  18. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  19. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  20. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  21. package/Components/HookRenderer/index.tsx +1 -6
  22. package/Components/Image/Image.android.tsx +3 -25
  23. package/Components/Image/Image.tsx +18 -2
  24. package/Components/Image/utils/index.ts +39 -0
  25. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  26. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  27. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  28. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  29. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  30. package/Components/Layout/TV/__tests__/index.test.tsx +20 -108
  31. package/Components/Layout/TV/index.tsx +15 -84
  32. package/Components/Layout/TV/index.web.tsx +33 -0
  33. package/Components/Layout/index.tsx +15 -11
  34. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
  35. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +86 -116
  36. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  37. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  38. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  39. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  40. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  41. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +57 -0
  42. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  43. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +37 -2
  44. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  45. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  46. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  47. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  48. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  49. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  50. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  51. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  52. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +6 -14
  53. package/Components/MasterCell/dataAdapter.ts +21 -7
  54. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  55. package/Components/MasterCell/index.tsx +5 -3
  56. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  57. package/Components/MasterCell/utils/index.ts +33 -35
  58. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  59. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  60. package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
  61. package/Components/ModalComponent/Button/Item.tsx +82 -0
  62. package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
  63. package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
  64. package/Components/ModalComponent/Button/assets.ts +4 -0
  65. package/Components/ModalComponent/Button/index.tsx +111 -0
  66. package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
  67. package/Components/ModalComponent/Header/Title.tsx +70 -0
  68. package/Components/ModalComponent/Header/assets.ts +4 -0
  69. package/Components/ModalComponent/Header/index.tsx +71 -0
  70. package/Components/ModalComponent/Header/utils.ts +79 -0
  71. package/Components/ModalComponent/index.tsx +38 -0
  72. package/Components/ModalComponent/utils.ts +126 -0
  73. package/Components/Navigator/StackNavigator.tsx +57 -0
  74. package/Components/Navigator/index.ts +1 -0
  75. package/Components/PlayerContainer/PlayerContainer.tsx +65 -32
  76. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  77. package/Components/PlayerImageBackground/index.tsx +34 -8
  78. package/Components/River/ComponentsMap/ComponentsMap.tsx +97 -2
  79. package/Components/River/RefreshControl.tsx +55 -1
  80. package/Components/River/TV/River.tsx +2 -25
  81. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  82. package/Components/River/__tests__/componentsMap.test.js +7 -0
  83. package/Components/River/useScreenConfiguration.ts +2 -2
  84. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
  85. package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
  86. package/Components/RouteManager/index.tsx +4 -2
  87. package/Components/Screen/TV/hooks/index.ts +3 -0
  88. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  89. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  90. package/Components/Screen/TV/index.web.tsx +165 -0
  91. package/Components/Screen/__tests__/Screen.test.tsx +91 -74
  92. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +24 -6
  93. package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
  94. package/Components/Screen/index.tsx +110 -53
  95. package/Components/Screen/navigationHandler.ts +5 -82
  96. package/Components/ScreenResolver/index.tsx +10 -13
  97. package/Components/Tabs/TV/styles.ts +1 -1
  98. package/Components/Transitioner/Scene.tsx +32 -26
  99. package/Components/Transitioner/Transitioner.tsx +38 -43
  100. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +12 -4
  101. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  102. package/Components/VideoLive/LiveImageManager.ts +30 -3
  103. package/Components/VideoLive/PlayerLiveImageComponent.tsx +79 -48
  104. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  105. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +21 -29
  106. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +66 -0
  107. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +360 -0
  108. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +165 -0
  109. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +456 -0
  110. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +182 -0
  111. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  112. package/Components/VideoModal/ModalAnimation/utils.ts +302 -0
  113. package/Components/VideoModal/PlayerDetails.tsx +6 -3
  114. package/Components/VideoModal/PlayerWrapper.tsx +77 -47
  115. package/Components/VideoModal/VideoModal.tsx +23 -40
  116. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +0 -2
  117. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +12 -1
  118. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +312 -166
  119. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  120. package/Components/VideoModal/utils.ts +8 -5
  121. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
  122. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  123. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
  124. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  125. package/Components/Viewport/utils.js +1 -1
  126. package/Components/ZappUIComponent/index.tsx +22 -12
  127. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  128. package/Components/index.js +2 -0
  129. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  130. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  131. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  132. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  133. package/Contexts/FocusableGroupContext/index.ts +7 -0
  134. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  135. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  136. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  137. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  138. package/Contexts/ScreenContext/index.tsx +95 -29
  139. package/Contexts/ScreenDataContext/index.tsx +20 -0
  140. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  141. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  142. package/events/index.ts +1 -0
  143. package/package.json +5 -5
  144. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  145. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  146. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  147. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  148. /package/Contexts/{index.js → index.ts} +0 -0
@@ -4,10 +4,7 @@ import * as R from "ramda";
4
4
 
5
5
  import { AnimationManager } from "./AnimationManager";
6
6
  import { Scene } from "./Scene";
7
- import {
8
- getScreenOrientation,
9
- isOrientationCompatible,
10
- } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
7
+
11
8
  import {
12
9
  getSceneByRoute,
13
10
  shouldSkipAnimationForPreviousWebViewScene,
@@ -169,21 +166,23 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
169
166
 
170
167
  transitionWithoutAnimation(resetState) {
171
168
  const { scenes } = this.state;
172
-
173
- const newState = resetState
174
- ? {
175
- scenes: [prepareScene(this.props.children)],
176
- to: {
177
- index: 0,
178
- },
179
- }
180
- : {
181
- scenes: R.update(
182
- scenes.length - 1,
183
- prepareScene(this.props.children),
184
- scenes
185
- ),
186
- };
169
+ const newScene = prepareScene(this.props.children);
170
+ const isLastSceneHook = scenes[scenes.length - 1].key.startsWith("/hook");
171
+
172
+ let newState;
173
+
174
+ if (resetState) {
175
+ newState = {
176
+ scenes: [newScene],
177
+ to: { index: 0 },
178
+ };
179
+ } else if (isLastSceneHook || scenes.length === 1) {
180
+ newState = { scenes: R.update(scenes.length - 1, newScene, scenes) };
181
+ } else {
182
+ newState = {
183
+ scenes: R.compose(R.tail, R.append(newScene))(scenes),
184
+ };
185
+ }
187
186
 
188
187
  this.setState(newState);
189
188
  }
@@ -277,33 +276,25 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
277
276
  // so only a single scene should be rendered,
278
277
  // instead of dangerous nulls or duplicates.
279
278
  renderSingleScene({ scenes, to }) {
280
- const { contentStyle, layoutData } = this.props;
281
- const screenData = scenes[to.index]?.children?.props?.screenData;
282
-
283
- const orientation = getScreenOrientation({
284
- screenData,
285
- layoutData,
286
- });
279
+ const { contentStyle } = this.props;
280
+ const screenData = this.props.children.props.screenData;
281
+ const pathname = scenes[to.index].key;
287
282
 
288
- // We prevent rendering of the screen until UI is actually rotated to screen desired orientation.
289
- // This saves unnecessary re-renders and user will not see distorted aspect screen.
290
283
  return (
291
284
  <View style={styles.container}>
292
- {scenes[to.index] &&
293
- isOrientationCompatible({
294
- orientation,
295
- layoutData,
296
- }) && (
297
- <Scene
298
- {...{ style: to.style }}
299
- pathname={scenes[to.index].key}
300
- contentStyle={contentStyle}
301
- key={scenes[to.index].key}
302
- screenUniqueId={scenes[to.index].screenUniqueId}
303
- >
304
- {scenes[to.index].children}
305
- </Scene>
306
- )}
285
+ {scenes[to.index] && (
286
+ <Scene
287
+ {...{ style: to.style }}
288
+ pathname={pathname}
289
+ isActive={pathname === this.props.navigator.currentRoute}
290
+ contentStyle={contentStyle}
291
+ key={scenes[to.index].key}
292
+ screenUniqueId={scenes[to.index].screenUniqueId}
293
+ screenData={screenData}
294
+ >
295
+ {scenes[to.index].children}
296
+ </Scene>
297
+ )}
307
298
  </View>
308
299
  );
309
300
  }
@@ -319,12 +310,14 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
319
310
  <Scene
320
311
  key={scenes[from.index].key}
321
312
  pathname={scenes[from.index].key}
313
+ isActive={scenes[from.index].key === this.props.navigator.currentRoute}
322
314
  style={from.style}
323
315
  pointerEvents="none"
324
316
  overlayStyle={from.overlayStyle}
325
317
  animating={this.state.animating}
326
318
  contentStyle={contentStyle}
327
319
  screenUniqueId={scenes[from.index].screenUniqueId}
320
+ screenData={scenes[from.index].children.props.screenData}
328
321
  >
329
322
  {scenes[from.index].children}
330
323
  </Scene>
@@ -333,12 +326,14 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
333
326
  const toScene = scenes[to.index] && (
334
327
  <Scene
335
328
  pathname={scenes[to.index].key}
329
+ isActive={scenes[to.index].key === this.props.navigator.currentRoute}
336
330
  key={scenes[to.index].key}
337
331
  style={to.style}
338
332
  overlayStyle={to.overlayStyle}
339
333
  animating={this.state.animating}
340
334
  contentStyle={contentStyle}
341
335
  screenUniqueId={scenes[to.index].screenUniqueId}
336
+ screenData={this.props.children.props.screenData}
342
337
  >
343
338
  {scenes[to.index].children}
344
339
  </Scene>
@@ -21,7 +21,11 @@ exports[`<Scene /> renders correctly 1`] = `
21
21
  ]
22
22
  }
23
23
  >
24
- <ContextProvider />
24
+ <ContextProvider>
25
+ <ContextProvider>
26
+ <ScreenContextProvider />
27
+ </ContextProvider>
28
+ </ContextProvider>
25
29
  </ForwardRef(AnimatedComponentWrapper)>
26
30
  `;
27
31
 
@@ -37,13 +41,17 @@ exports[`<Scene /> renders the overlay while animating 1`] = `
37
41
  "paddingBottom": 49,
38
42
  },
39
43
  Object {
40
- "height": 1334,
41
- "width": 750,
44
+ "height": "100%",
45
+ "width": "100%",
42
46
  },
43
47
  ]
44
48
  }
45
49
  >
46
- <ContextProvider />
50
+ <ContextProvider>
51
+ <ContextProvider>
52
+ <ScreenContextProvider />
53
+ </ContextProvider>
54
+ </ContextProvider>
47
55
  <ForwardRef(AnimatedComponentWrapper)
48
56
  style={
49
57
  Object {
@@ -10,6 +10,7 @@ exports[`<TransitionerComponent /> renders correctly 1`] = `
10
10
  }
11
11
  >
12
12
  <Scene
13
+ isActive={true}
13
14
  screenUniqueId="mocked-uuid"
14
15
  >
15
16
  <View
@@ -10,7 +10,11 @@ import { Component } from "react";
10
10
 
11
11
  const { log_debug, log_info, log_error } = loggerLiveImageManager;
12
12
 
13
- export type LiveImageManagerEvent = "onEnd" | "onStart" | "onError";
13
+ export type LiveImageManagerEvent =
14
+ | "onEnd"
15
+ | "onStart"
16
+ | "onError"
17
+ | "onDestroy";
14
18
 
15
19
  export type LiveImageManagerListenerData = {
16
20
  id: string;
@@ -102,6 +106,16 @@ export class LiveImageManager implements PlayerLifecycleListener {
102
106
  }
103
107
 
104
108
  this.items = this.items.filter((i) => i !== item);
109
+
110
+ this.invokeListenersUpdate({
111
+ callbackName: "onDestroy",
112
+ event: {
113
+ item,
114
+ playerId: this.currentlyPlaying?.playerId,
115
+ primaryPlayerId: this.primaryPlayer?.playerId,
116
+ entry: item.getPlayer().getEntry(),
117
+ },
118
+ });
105
119
  };
106
120
 
107
121
  public onViewportEnter = (item: LiveImage) => {
@@ -351,6 +365,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
351
365
  item,
352
366
  playerId: this.currentlyPlaying?.playerId,
353
367
  primaryPlayerId: this.primaryPlayer?.playerId,
368
+ entry: item.getPlayer().getEntry(),
354
369
  },
355
370
  });
356
371
  };
@@ -369,8 +384,8 @@ export class LiveImageManager implements PlayerLifecycleListener {
369
384
  // TODO: This branch was used when we had play button to toggle replay.
370
385
  // Now we mute button instead but we keep it just in case.
371
386
  if (IS_ALLOWED_REPLAY && !isTV() && isCurrentItemEnded) {
372
- this.currentlyPlaying.player.seekTo(0);
373
- this.currentlyPlaying.player.pause();
387
+ this.currentlyPlaying?.player.seekTo(0);
388
+ this.currentlyPlaying?.player.pause();
374
389
 
375
390
  // TODO: ...Maybe player some other item
376
391
  } else {
@@ -385,6 +400,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
385
400
  item,
386
401
  playerId: this.currentlyPlaying?.playerId,
387
402
  primaryPlayerId: this.primaryPlayer?.playerId,
403
+ entry: item.getPlayer().getEntry(),
388
404
  },
389
405
  });
390
406
  };
@@ -419,6 +435,7 @@ export class LiveImageManager implements PlayerLifecycleListener {
419
435
  error,
420
436
  playerId: currentItem?.playerId,
421
437
  primaryPlayerId: currentItem?.playerId,
438
+ entry: item.getPlayer().getEntry(),
422
439
  },
423
440
  });
424
441
  };
@@ -508,6 +525,16 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
508
525
  LiveImageManager.instance.onLiveImageError(this, error);
509
526
  };
510
527
 
528
+ onVideoError = (error: Error) => {
529
+ log_error(
530
+ `onVideoError: live-image, fail to play, switching back to image. error: ${
531
+ error?.message
532
+ }, ${playerInfo(this.player)}`
533
+ );
534
+
535
+ LiveImageManager.instance.onLiveImageError(this, error);
536
+ };
537
+
511
538
  onVideoEnd = (_event) => {
512
539
  log_info(
513
540
  `onVideoEnd: live-image, video completed, switching back to image, ${playerInfo(
@@ -2,7 +2,7 @@
2
2
  //
3
3
  import * as React from "react";
4
4
 
5
- import { AppState, AppStateStatus } from "react-native";
5
+ import { AppState, AppStateStatus, View } from "react-native";
6
6
  import { TrackedView } from "../TrackedView";
7
7
  import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/layout";
8
8
 
@@ -14,7 +14,7 @@ import {
14
14
  } from "@applicaster/zapp-react-native-utils/reactUtils";
15
15
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
16
16
  import { getUserCellPlayerMutedPreference } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/userCellPlayerMutedPreference";
17
- import { PlayerImageBackground } from "../PlayerImageBackground";
17
+ import Image from "@applicaster/zapp-react-native-ui-components/Components/MasterCell/DefaultComponents/Image";
18
18
  import { LiveImage, LiveImageManager, LiveImageType } from "./LiveImageManager";
19
19
 
20
20
  import { AnimatedInOut } from "@applicaster/zapp-react-native-ui-components/Components/AnimatedInOut";
@@ -25,8 +25,9 @@ import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/player
25
25
  import { getAutoplaySettings } from "./utils";
26
26
  import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
27
27
  import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
28
+ import { useEffect } from "react";
28
29
 
29
- const { log_error } = loggerLiveImageComponent;
30
+ const { log_error, log_debug } = loggerLiveImageComponent;
30
31
 
31
32
  const isMeasurement = (item: ZappEntry) =>
32
33
  isString(item.id) && item.id.startsWith("pre-measurement-");
@@ -39,12 +40,13 @@ type Props = {
39
40
  imageKey?: string;
40
41
  screenConfig: Record<string, any>;
41
42
  audioMutedByDefault?: boolean;
43
+ uri: string; // cover image url
42
44
  };
43
45
 
44
46
  const PlayerLiveImageComponent = (props: Props) => {
45
47
  const dimensions = useDimensions("screen", { fullDimensions: true });
46
48
 
47
- const { item, style, playerId, imageKey } = props;
49
+ const { item, style, playerId } = props;
48
50
 
49
51
  const timeoutRef = React.useRef<NodeJS.Timeout | null>(null);
50
52
  const [mode, setMode] = React.useState(LiveImageType.Image);
@@ -55,6 +57,7 @@ const PlayerLiveImageComponent = (props: Props) => {
55
57
  );
56
58
 
57
59
  const ref = React.useRef(null);
60
+ const trackViewRef = React.useRef(null);
58
61
 
59
62
  /**
60
63
  * Debounced setMode function.
@@ -63,24 +66,51 @@ const PlayerLiveImageComponent = (props: Props) => {
63
66
  *
64
67
  * @param type video or image
65
68
  */
66
- const setModeDebounced = React.useCallback((type: LiveImageType) => {
67
- if (type === LiveImageType.Video) {
68
- if (type === mode) {
69
- return;
70
- }
69
+ const setModeDebounced = React.useCallback(
70
+ (type: LiveImageType) => {
71
+ if (type === LiveImageType.Video) {
72
+ if (type === mode) {
73
+ return;
74
+ }
75
+
76
+ timeoutRef.current = setTimeout(() => {
77
+ setMode(type);
78
+ }, 1000) as unknown as NodeJS.Timeout;
79
+ } else {
80
+ if (timeoutRef.current) {
81
+ clearTimeout(timeoutRef.current);
82
+ timeoutRef.current = null;
83
+ }
71
84
 
72
- timeoutRef.current = setTimeout(() => {
73
85
  setMode(type);
74
- }, 1000) as unknown as NodeJS.Timeout;
75
- } else {
76
- if (timeoutRef.current) {
77
- clearTimeout(timeoutRef.current);
78
- timeoutRef.current = null;
79
86
  }
87
+ },
88
+ [mode]
89
+ );
90
+
91
+ const entryToViewIdMapping = React.useRef({});
80
92
 
81
- setMode(type);
93
+ useEffect(() => {
94
+ const nativeTag = trackViewRef.current?._nativeTag?.toString();
95
+
96
+ if (!nativeTag) {
97
+ return;
98
+ }
99
+
100
+ const prevValue = entryToViewIdMapping.current[nativeTag];
101
+
102
+ if (prevValue && prevValue.itemId !== item.id) {
103
+ if (mode === LiveImageType.Video) {
104
+ log_debug(
105
+ "Entry to view association changed, stopping current live image"
106
+ );
107
+
108
+ setMode(LiveImageType.Image);
109
+ }
82
110
  }
83
- }, [mode]);
111
+
112
+ entryToViewIdMapping.current[nativeTag] = { itemId: item.id };
113
+ }, [trackViewRef.current, item.id, mode]);
84
114
 
85
115
  const { screenConfig, playerPluginId } = props;
86
116
 
@@ -100,7 +130,7 @@ const PlayerLiveImageComponent = (props: Props) => {
100
130
  return new LiveImage({
101
131
  playerId,
102
132
  player: playerFactoryItem.controller,
103
- component: playerFactoryItem?.Component
133
+ component: playerFactoryItem?.Component,
104
134
  });
105
135
  } else {
106
136
  throw new Error("Player factory item is null");
@@ -111,10 +141,7 @@ const PlayerLiveImageComponent = (props: Props) => {
111
141
  liveImageItem.setMode = setModeDebounced;
112
142
  }, [setModeDebounced, liveImageItem]);
113
143
 
114
- const { start, end } = React.useMemo(
115
- () => getAutoplaySettings(item),
116
- [item]
117
- );
144
+ const { start, end } = React.useMemo(() => getAutoplaySettings(item), [item]);
118
145
 
119
146
  const controller = liveImageItem.getPlayer();
120
147
  const player = usePlayer(playerId);
@@ -239,7 +266,12 @@ const PlayerLiveImageComponent = (props: Props) => {
239
266
 
240
267
  const onPositionUpdated = React.useCallback(
241
268
  (data) => {
242
- const normalizedRect = (rect: {left: number, right: number, top: number, bottom: number }) => {
269
+ const normalizedRect = (rect: {
270
+ left: number;
271
+ right: number;
272
+ top: number;
273
+ bottom: number;
274
+ }) => {
243
275
  if (!rect) {
244
276
  return null;
245
277
  }
@@ -283,37 +315,36 @@ const PlayerLiveImageComponent = (props: Props) => {
283
315
  const videoStyles = { ...style, position: "absolute", zIndex: -100 };
284
316
  const Player: any = liveImageItem.component || BufferAnimation;
285
317
 
318
+ // TODO: nice to have - resolve imageKey when imageUrl is not provided
286
319
  return (
287
320
  <TrackedView
288
321
  testId={`tracked-view-${playerId}-${item.title}`}
289
322
  onPositionUpdated={onPositionUpdated}
290
323
  >
291
- {isVideoMode && (
292
- <Player
293
- autoplay={false}
294
- ref={_assignRoot}
295
- entry={item} // Must be passed first in list
296
- style={videoStyles}
297
- playerId={playerId}
298
- muted={muted}
299
- listener={controller?.getListener()}
300
- resizeMode={"cover"}
301
- {...platformSpecificProps}
302
- />
303
- )}
304
- {
305
- <AnimatedInOut
306
- visible={isBackgroundImageVisible}
307
- animationConfig={overlayFadeIn}
308
- staticStyles={style}
309
- >
310
- <PlayerImageBackground
311
- entry={item}
312
- style={style}
313
- imageKey={imageKey}
324
+ <View ref={trackViewRef}>
325
+ {isVideoMode && (
326
+ <Player
327
+ autoplay={false}
328
+ ref={_assignRoot}
329
+ entry={item} // Must be passed first in list
330
+ style={videoStyles}
331
+ playerId={playerId}
332
+ muted={muted}
333
+ listener={controller?.getListener()}
334
+ resizeMode={"cover"}
335
+ {...platformSpecificProps}
314
336
  />
315
- </AnimatedInOut>
316
- }
337
+ )}
338
+ {
339
+ <AnimatedInOut
340
+ visible={isBackgroundImageVisible}
341
+ animationConfig={overlayFadeIn}
342
+ staticStyles={style}
343
+ >
344
+ <Image {...props} resizeMode="cover" style={style} applyMaxSize />
345
+ </AnimatedInOut>
346
+ }
347
+ </View>
317
348
  </TrackedView>
318
349
  );
319
350
  };
@@ -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
  });
@@ -5,41 +5,33 @@ exports[`PlayerLiveImageComponent should render correctly with default props 1`]
5
5
  onPositionUpdated={[Function]}
6
6
  testId="tracked-view-player1-Test"
7
7
  >
8
- <View
9
- collapsable={false}
10
- style={
11
- Object {
12
- "opacity": 1,
13
- }
14
- }
15
- >
8
+ <View>
16
9
  <View
17
- accessibilityIgnoresInvertColors={true}
18
- style={Object {}}
10
+ collapsable={false}
11
+ style={
12
+ Object {
13
+ "opacity": 1,
14
+ }
15
+ }
19
16
  >
20
17
  <Image
21
- resizeMode="cover"
22
- source={
18
+ applyMaxSize={true}
19
+ audioMutedByDefault={false}
20
+ imageKey="image1"
21
+ item={
23
22
  Object {
24
- "uri": undefined,
25
- }
26
- }
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,
23
+ "id": "1",
24
+ "title": "Test",
25
+ "type": Object {
26
+ "value": "",
39
27
  },
40
- undefined,
41
- ]
28
+ }
42
29
  }
30
+ onError={[Function]}
31
+ playerId="player1"
32
+ resizeMode="cover"
33
+ screenConfig={Object {}}
34
+ style={Object {}}
43
35
  />
44
36
  </View>
45
37
  </View>
@@ -0,0 +1,66 @@
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
+
9
+ type Props = {
10
+ style: ViewStyle[];
11
+ children: React.ReactNode;
12
+ };
13
+
14
+ export const AnimatedPlayerModalWrapper = (props: Props) => {
15
+ const {
16
+ playerAnimationState,
17
+ animatedValues: { lastScrollY, dragScrollY, translateYOffset },
18
+ modalSnapPoints,
19
+ setStartComponentsAnimation,
20
+ } = useModalAnimationContext();
21
+
22
+ const interpolateConfig: Animated.InterpolationConfigType = React.useMemo(
23
+ () => ({
24
+ inputRange: modalSnapPoints,
25
+ outputRange: modalSnapPoints,
26
+ extrapolate: "clamp",
27
+ }),
28
+ [modalSnapPoints]
29
+ );
30
+
31
+ let translateY;
32
+
33
+ if (playerAnimationState === PlayerAnimationStateEnum.drag_player) {
34
+ translateY = translateYOffset.interpolate(interpolateConfig);
35
+ } else {
36
+ const reverseLastScrollY = Animated.multiply(
37
+ new Animated.Value(-1),
38
+ lastScrollY
39
+ );
40
+
41
+ translateY = Animated.add(
42
+ translateYOffset,
43
+ Animated.add(dragScrollY, reverseLastScrollY)
44
+ ).interpolate(interpolateConfig);
45
+ }
46
+
47
+ React.useEffect(() => {
48
+ const translateYListener = translateY.addListener(() => {
49
+ (playerAnimationState === PlayerAnimationStateEnum.minimize ||
50
+ playerAnimationState === PlayerAnimationStateEnum.maximaze) &&
51
+ setStartComponentsAnimation(true);
52
+ });
53
+
54
+ return () => {
55
+ translateYListener && translateY.removeListener(translateYListener);
56
+ };
57
+ }, [playerAnimationState]);
58
+
59
+ return (
60
+ <Animated.View
61
+ style={[props.style, { transform: [{ translateY: translateY }] }]}
62
+ >
63
+ {props.children}
64
+ </Animated.View>
65
+ );
66
+ };