@applicaster/zapp-react-native-ui-components 12.0.0-alpha.7431051513 → 12.0.0-alpha.7528906949

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 (85) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +5 -5
  3. package/Components/Cell/CellWithFocusable.tsx +23 -9
  4. package/Components/Cell/TvOSCellComponent.tsx +3 -7
  5. package/Components/Focusable/Focusable.tsx +6 -1
  6. package/Components/FocusableGroup/index.tsx +11 -2
  7. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  8. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  9. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  10. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  11. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  12. package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
  13. package/Components/Layout/TV/index.tsx +12 -11
  14. package/Components/Layout/TV/index.web.tsx +33 -0
  15. package/Components/Layout/index.tsx +15 -11
  16. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  17. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +51 -16
  18. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  19. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  20. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  21. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  22. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  23. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  24. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  25. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  26. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  27. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  28. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  29. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  30. package/Components/MasterCell/utils/index.ts +17 -30
  31. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  32. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  33. package/Components/Navigator/StackNavigator.tsx +54 -0
  34. package/Components/Navigator/index.ts +1 -0
  35. package/Components/PlayerContainer/PlayerContainer.tsx +17 -6
  36. package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
  37. package/Components/River/RefreshControl.tsx +55 -1
  38. package/Components/River/TV/River.tsx +0 -23
  39. package/Components/RouteManager/index.tsx +2 -2
  40. package/Components/Screen/TV/hooks/index.ts +3 -0
  41. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  42. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  43. package/Components/Screen/TV/index.web.tsx +186 -0
  44. package/Components/Screen/__tests__/Screen.test.tsx +3 -0
  45. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +4 -0
  46. package/Components/Screen/index.tsx +64 -25
  47. package/Components/ScreenResolver/index.tsx +3 -1
  48. package/Components/Transitioner/Scene.tsx +24 -19
  49. package/Components/Transitioner/Transitioner.tsx +21 -28
  50. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
  51. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  52. package/Components/VideoLive/LiveImageManager.ts +28 -1
  53. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +0 -5
  54. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +59 -42
  55. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +46 -32
  56. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +98 -81
  57. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +23 -4
  58. package/Components/VideoModal/ModalAnimation/utils.ts +6 -4
  59. package/Components/VideoModal/PlayerDetails.tsx +3 -3
  60. package/Components/VideoModal/PlayerWrapper.tsx +67 -60
  61. package/Components/VideoModal/VideoModal.tsx +17 -17
  62. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +7 -0
  63. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +32 -12
  64. package/Components/VideoModal/utils.ts +5 -2
  65. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  66. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  67. package/Components/Viewport/utils.js +1 -1
  68. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  69. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  70. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  71. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  72. package/Contexts/FocusableGroupContext/index.ts +7 -0
  73. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  74. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  75. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  76. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  77. package/Contexts/ScreenContext/index.tsx +94 -25
  78. package/Contexts/ScreenDataContext/index.tsx +20 -0
  79. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  80. package/events/index.ts +1 -0
  81. package/package.json +5 -5
  82. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  83. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  84. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  85. /package/Contexts/{index.js → index.ts} +0 -0
@@ -10,6 +10,8 @@ const mock_navBarVisibleFlag = true;
10
10
 
11
11
  const mockIsNavBarVisible = jest.fn(() => mock_navBarVisibleFlag);
12
12
 
13
+ const mockIsOrientationCompatible = jest.fn(() => true);
14
+
13
15
  jest.mock("react-native-safe-area-context", () => ({
14
16
  useSafeAreaInsets: () => ({ top: 44 }),
15
17
  }));
@@ -21,6 +23,7 @@ jest.mock("../../RouteManager", () => ({
21
23
  jest.mock(
22
24
  "@applicaster/zapp-react-native-utils/appUtils/orientationHelper",
23
25
  () => ({
26
+ isOrientationCompatible: mockIsOrientationCompatible,
24
27
  allowedOrientationsForScreen: jest.fn(),
25
28
  getOrientation: jest.fn(),
26
29
  getScreenOrientation: jest.fn(),
@@ -11,7 +11,9 @@ exports[`<Screen Component /> when the navbar should be hidden renders correctly
11
11
  }
12
12
  >
13
13
  <View
14
+ hasMenu={false}
14
15
  id="/river/testId"
16
+ pathname="/river/testId"
15
17
  selected="testId"
16
18
  testID="navBar"
17
19
  title=""
@@ -41,7 +43,9 @@ exports[`<Screen Component /> when the navbar should show renders correctly 1`]
41
43
  }
42
44
  >
43
45
  <View
46
+ hasMenu={false}
44
47
  id="/river/testId"
48
+ pathname="/river/testId"
45
49
  selected="testId"
46
50
  testID="navBar"
47
51
  title=""
@@ -3,10 +3,17 @@ import React from "react";
3
3
  import * as R from "ramda";
4
4
  import { View } from "react-native";
5
5
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
6
+ import {
7
+ isOrientationCompatible,
8
+ useGetScreenOrientation,
9
+ } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
6
10
 
7
11
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
8
12
  import { getComponentModule } from "@applicaster/zapp-react-native-utils/pluginUtils";
9
- import { getNavigationPropsV2 } from "@applicaster/zapp-react-native-utils/navigationUtils";
13
+ import {
14
+ getNavigationPropsV2,
15
+ resolveNavigationPlugin,
16
+ } from "@applicaster/zapp-react-native-utils/navigationUtils";
10
17
  import { useSafeAreaInsets } from "react-native-safe-area-context";
11
18
  import {
12
19
  useDimensions,
@@ -52,11 +59,11 @@ function isValidColor(string) {
52
59
  }
53
60
 
54
61
  type Props = {
55
- setToggleMenu: () => void;
56
- setCloseMenu: () => void;
62
+ pathname: unknown;
63
+ screenData: unknown;
57
64
  };
58
65
 
59
- export function Screen({ setToggleMenu, setCloseMenu }: Props) {
66
+ export function Screen(_props: Props) {
60
67
  const theme = useTheme<BaseThemePropertiesMobile>();
61
68
  const navigation = useNavigation();
62
69
  const { plugins } = usePickFromState(["plugins"]);
@@ -66,30 +73,53 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
66
73
  const { backgroundColor } = useScreenConfiguration(currentScreenData.id);
67
74
  const insets = useSafeAreaInsets();
68
75
 
69
- const { width: screenWidth } = useDimensions("screen", {
76
+ const {
77
+ width: screenWidth,
78
+ height,
79
+ deviceInfo,
80
+ } = useDimensions("screen", {
70
81
  fullDimensions: true,
71
82
  });
72
83
 
73
- const routeData = useRoute();
84
+ const { screenData, pathname } = useRoute();
74
85
 
75
86
  const currentRiver = useScreenData(
76
- getScreenId(routeData.screenData, navigation.activeRiver)
87
+ getScreenId(screenData, navigation.activeRiver)
77
88
  );
78
89
 
79
90
  const { title } = useNavbarState();
80
91
 
81
- const navBarProps = React.useMemo(
82
- () => ({
83
- ...getNavigationPropsV2({
84
- currentRiver,
85
- title,
86
- category: "nav_bar",
87
- }),
88
- setToggleMenu,
89
- setCloseMenu,
90
- }),
91
- [currentRiver, setCloseMenu, setToggleMenu]
92
- );
92
+ const isTablet = deviceInfo?.isTablet;
93
+
94
+ const { appData } = usePickFromState(["appData"]);
95
+ const isTabletPortrait = appData?.isTabletPortrait;
96
+
97
+ const layoutData = { isTablet, isTabletPortrait, width: screenWidth, height };
98
+
99
+ const hasMenu =
100
+ resolveNavigationPlugin({
101
+ category: "menu",
102
+ navigations: currentRiver.navigations,
103
+ plugins,
104
+ })?.identifier === "quick_brick_side_menu";
105
+
106
+ const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(() => {
107
+ const props = getNavigationPropsV2({
108
+ currentRiver,
109
+ title,
110
+ category: "nav_bar",
111
+ });
112
+
113
+ if (props) {
114
+ return {
115
+ ...props,
116
+ id: pathname,
117
+ pathname: pathname,
118
+ };
119
+ }
120
+
121
+ return null;
122
+ }, [currentRiver, pathname]);
93
123
 
94
124
  const NavBar = React.useMemo(
95
125
  () =>
@@ -97,12 +127,10 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
97
127
  "nav_bar",
98
128
  currentRiver,
99
129
  plugins
100
- ) as React.ComponentType<MobileNavBarPluginProps>,
130
+ ) as React.ComponentType<MobileNavBarPluginProps & { hasMenu: boolean }>,
101
131
  []
102
132
  );
103
133
 
104
- const { screenData, pathname } = routeData;
105
-
106
134
  const offlinePlugin = getComponentModule({
107
135
  plugins,
108
136
  components: {},
@@ -130,12 +158,23 @@ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
130
158
 
131
159
  useScreenOrientationHandler({ screenData: targetScreenData });
132
160
 
161
+ const orientation = useGetScreenOrientation(targetScreenData);
162
+
163
+ // We prevent rendering of the screen until UI is actually rotated to screen desired orientation.
164
+ // This saves unnecessary re-renders and user will not see distorted aspect screen.
165
+ if (
166
+ !isOrientationCompatible({
167
+ orientation,
168
+ layoutData,
169
+ })
170
+ ) {
171
+ return null;
172
+ }
173
+
133
174
  return (
134
175
  <ScreenContextProvider pathname={pathname}>
135
176
  <View style={style}>
136
- <NavBar
137
- {...{ ...(navBarProps as MobileNavBarPluginProps), id: pathname }}
138
- />
177
+ {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
139
178
 
140
179
  <OfflineFallbackScreen screenData={targetScreenData}>
141
180
  {/* @TODO RouteManager doesn't use props, can they be removed ? */}
@@ -32,12 +32,13 @@ type Props = {
32
32
  nextFocusLeft?: React.Ref<any>;
33
33
  nextFocusUp?: React.Ref<any>;
34
34
  };
35
+ groupId?: string;
35
36
  };
36
37
 
37
38
  export function ScreenResolverComponent(props: Props) {
38
39
  useScreenAnalytics(props);
39
40
 
40
- const { screenType, screenId, screenData } = props;
41
+ const { screenType, screenId, screenData, groupId } = props;
41
42
 
42
43
  const { hookPlugin } = screenData || {};
43
44
 
@@ -81,6 +82,7 @@ export function ScreenResolverComponent(props: Props) {
81
82
  item={screenData}
82
83
  mode={mode === "PIP" ? "PIP" : "FULLSCREEN"}
83
84
  isModal={false}
85
+ groupId={groupId}
84
86
  />
85
87
  );
86
88
  }
@@ -7,6 +7,7 @@ import {
7
7
  } from "react-native";
8
8
  import { useSafeAreaFrame } from "react-native-safe-area-context";
9
9
  import { PathnameContext } from "../../Contexts/PathnameContext";
10
+ import { ScreenDataContext } from "../../Contexts/ScreenDataContext";
10
11
 
11
12
  type Props = {
12
13
  children: any;
@@ -16,6 +17,8 @@ type Props = {
16
17
  animating: boolean;
17
18
  contentStyle: ViewStyle;
18
19
  pathname: string;
20
+ isActive: boolean;
21
+ screenData: NavigationScreenData;
19
22
  };
20
23
 
21
24
  /**
@@ -38,34 +41,36 @@ export function Scene({
38
41
  animating,
39
42
  contentStyle,
40
43
  pathname,
44
+ isActive = false,
45
+ screenData,
41
46
  }: Props) {
47
+ const [initialScreenData, setInitialScreenData] = React.useState(screenData);
48
+
49
+ React.useLayoutEffect(() => {
50
+ if (isActive) {
51
+ setInitialScreenData(screenData);
52
+ }
53
+ }, [screenData, isActive]);
54
+
42
55
  const frame = useSafeAreaFrame();
43
56
  const { height, width } = useWindowDimensions();
44
57
 
45
- if (overlayStyle && animating) {
46
- // Safe frame takes too long to update,
47
- // so we can not rely on it there, and use height and width instead.
48
- return (
49
- <Animated.View
50
- pointerEvents={pointerEvents}
51
- style={[style, contentStyle, { height, width }]}
52
- >
53
- <PathnameContext.Provider value={pathname}>
54
- {children}
55
- </PathnameContext.Provider>
56
- <Animated.View style={overlayStyle} />
57
- </Animated.View>
58
- );
59
- }
58
+ // Safe frame takes too long to update,
59
+ // so we can not rely on it there, and use height and width instead.
60
+ const isAnimating = animating && overlayStyle;
61
+ const dimensions = isAnimating ? { height, width } : frame;
60
62
 
61
63
  return (
62
64
  <Animated.View
63
65
  pointerEvents={pointerEvents}
64
- style={[style, contentStyle, frame]}
66
+ style={[style, contentStyle, dimensions]}
65
67
  >
66
- <PathnameContext.Provider value={pathname}>
67
- {children}
68
- </PathnameContext.Provider>
68
+ <ScreenDataContext.Provider value={initialScreenData}>
69
+ <PathnameContext.Provider value={pathname}>
70
+ {children}
71
+ </PathnameContext.Provider>
72
+ </ScreenDataContext.Provider>
73
+ {isAnimating && <Animated.View style={overlayStyle} />}
69
74
  </Animated.View>
70
75
  );
71
76
  }
@@ -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,
@@ -277,33 +274,25 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
277
274
  // so only a single scene should be rendered,
278
275
  // instead of dangerous nulls or duplicates.
279
276
  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
- });
277
+ const { contentStyle } = this.props;
278
+ const screenData = this.props.children.props.screenData;
279
+ const pathname = scenes[to.index].key;
287
280
 
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
281
  return (
291
282
  <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
- )}
283
+ {scenes[to.index] && (
284
+ <Scene
285
+ {...{ style: to.style }}
286
+ pathname={pathname}
287
+ isActive={pathname === this.props.navigator.currentRoute}
288
+ contentStyle={contentStyle}
289
+ key={scenes[to.index].key}
290
+ screenUniqueId={scenes[to.index].screenUniqueId}
291
+ screenData={screenData}
292
+ >
293
+ {scenes[to.index].children}
294
+ </Scene>
295
+ )}
307
296
  </View>
308
297
  );
309
298
  }
@@ -319,12 +308,14 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
319
308
  <Scene
320
309
  key={scenes[from.index].key}
321
310
  pathname={scenes[from.index].key}
311
+ isActive={scenes[from.index].key === this.props.navigator.currentRoute}
322
312
  style={from.style}
323
313
  pointerEvents="none"
324
314
  overlayStyle={from.overlayStyle}
325
315
  animating={this.state.animating}
326
316
  contentStyle={contentStyle}
327
317
  screenUniqueId={scenes[from.index].screenUniqueId}
318
+ screenData={scenes[from.index].children.props.screenData}
328
319
  >
329
320
  {scenes[from.index].children}
330
321
  </Scene>
@@ -333,12 +324,14 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
333
324
  const toScene = scenes[to.index] && (
334
325
  <Scene
335
326
  pathname={scenes[to.index].key}
327
+ isActive={scenes[to.index].key === this.props.navigator.currentRoute}
336
328
  key={scenes[to.index].key}
337
329
  style={to.style}
338
330
  overlayStyle={to.overlayStyle}
339
331
  animating={this.state.animating}
340
332
  contentStyle={contentStyle}
341
333
  screenUniqueId={scenes[to.index].screenUniqueId}
334
+ screenData={this.props.children.props.screenData}
342
335
  >
343
336
  {scenes[to.index].children}
344
337
  </Scene>
@@ -21,7 +21,9 @@ exports[`<Scene /> renders correctly 1`] = `
21
21
  ]
22
22
  }
23
23
  >
24
- <ContextProvider />
24
+ <ContextProvider>
25
+ <ContextProvider />
26
+ </ContextProvider>
25
27
  </ForwardRef(AnimatedComponentWrapper)>
26
28
  `;
27
29
 
@@ -43,7 +45,9 @@ exports[`<Scene /> renders the overlay while animating 1`] = `
43
45
  ]
44
46
  }
45
47
  >
46
- <ContextProvider />
48
+ <ContextProvider>
49
+ <ContextProvider />
50
+ </ContextProvider>
47
51
  <ForwardRef(AnimatedComponentWrapper)
48
52
  style={
49
53
  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
  };
@@ -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(
@@ -30,11 +30,6 @@ exports[`PlayerLiveImageComponent should render correctly with default props 1`]
30
30
  playerId="player1"
31
31
  resizeMode="cover"
32
32
  screenConfig={Object {}}
33
- source={
34
- Object {
35
- "uri": "",
36
- }
37
- }
38
33
  style={Object {}}
39
34
  />
40
35
  </View>
@@ -9,6 +9,8 @@ import {
9
9
  TapGestureHandler,
10
10
  GestureHandlerRootView,
11
11
  } from "react-native-gesture-handler";
12
+
13
+ import { PlayerContainerContext } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainerContext";
12
14
  import {
13
15
  useModalAnimationContext,
14
16
  PlayerAnimationStateEnum,
@@ -58,6 +60,8 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
58
60
  setLastSnap,
59
61
  } = useModalAnimationContext();
60
62
 
63
+ const { isLanguageOverlayVisible } = React.useContext(PlayerContainerContext);
64
+
61
65
  const { maximiseVideoModal, minimiseVideoModal, videoModalState } =
62
66
  useNavigation();
63
67
 
@@ -69,6 +73,11 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
69
73
  playerAnimationState !== PlayerAnimationStateEnum.minimize &&
70
74
  playerAnimationState !== PlayerAnimationStateEnum.maximaze;
71
75
 
76
+ const isEnablePanGesture =
77
+ !isLanguageOverlayVisible &&
78
+ isNotMinimizeMaximazeAnimation &&
79
+ (isMaximazedModal || isMinimizedModal);
80
+
72
81
  const isAudioItem = React.useMemo(
73
82
  () =>
74
83
  videoModalState.item?.content?.type?.includes?.("audio") ||
@@ -91,58 +100,68 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
91
100
  { useNativeDriver: true }
92
101
  );
93
102
 
94
- const onHandlerStateChange = ({ nativeEvent }) => {
95
- if (nativeEvent.oldState === State.ACTIVE && scrollPosition.current === 0) {
96
- // eslint-disable-next-line prefer-const
97
- const { velocityY, translationY } = nativeEvent;
98
- const dragToss = 0.05;
103
+ const onHandlerStateChange = React.useCallback(
104
+ ({ nativeEvent }) => {
105
+ if (
106
+ nativeEvent.oldState === State.ACTIVE &&
107
+ scrollPosition.current === 0
108
+ ) {
109
+ // eslint-disable-next-line prefer-const
110
+ const { velocityY, translationY } = nativeEvent;
111
+ const dragToss = 0.05;
99
112
 
100
- const preparedTranslationY =
101
- Math.abs(translationY) - lastScrollYValue.current;
113
+ const preparedTranslationY =
114
+ Math.abs(translationY) - lastScrollYValue.current;
102
115
 
103
- if (videoModalState.mode === "MAXIMIZED") {
104
- const endOffsetY =
105
- lastSnap + preparedTranslationY + dragToss * velocityY + 150;
116
+ if (videoModalState.mode === "MAXIMIZED") {
117
+ const endOffsetY =
118
+ lastSnap + preparedTranslationY + dragToss * velocityY + 150;
106
119
 
107
- let destSnapPoint = modalSnapPoints[0];
120
+ let destSnapPoint = modalSnapPoints[0];
108
121
 
109
- for (const snapPoint of modalSnapPoints) {
110
- const distFromSnap = Math.abs(snapPoint - endOffsetY);
122
+ for (const snapPoint of modalSnapPoints) {
123
+ const distFromSnap = Math.abs(snapPoint - endOffsetY);
111
124
 
112
- if (distFromSnap < Math.abs(destSnapPoint - endOffsetY)) {
113
- destSnapPoint = snapPoint;
125
+ if (distFromSnap < Math.abs(destSnapPoint - endOffsetY)) {
126
+ destSnapPoint = snapPoint;
127
+ }
114
128
  }
115
- }
116
129
 
117
- setLastSnap(destSnapPoint);
130
+ setLastSnap(destSnapPoint);
118
131
 
119
- if (destSnapPoint === modalSnapPoints[0]) {
120
- translateYOffset.extractOffset();
121
- translateYOffset.setValue(preparedTranslationY);
122
- translateYOffset.flattenOffset();
123
- dragScrollY.setValue(0);
132
+ if (destSnapPoint === modalSnapPoints[0]) {
133
+ translateYOffset.extractOffset();
134
+ translateYOffset.setValue(preparedTranslationY);
135
+ translateYOffset.flattenOffset();
136
+ dragScrollY.setValue(0);
124
137
 
125
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
138
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
139
+ } else {
140
+ setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
141
+ }
126
142
  } else {
127
- setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
143
+ if (translationY < 0) {
144
+ // from mini to full
145
+ setLastSnap(modalSnapPoints[0]);
146
+
147
+ translateYOffset.setValue(
148
+ modalSnapPoints[1] - preparedTranslationY
149
+ );
150
+
151
+ dragScrollY.setValue(0);
152
+
153
+ setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
154
+ } else {
155
+ resetPlayerAnimationState();
156
+ }
128
157
  }
129
158
  } else {
130
- if (translationY < 0) {
131
- // from mini to full
132
- setLastSnap(modalSnapPoints[0]);
133
- translateYOffset.setValue(modalSnapPoints[1] - preparedTranslationY);
134
- dragScrollY.setValue(0);
135
-
136
- setPlayerAnimationState(PlayerAnimationStateEnum.maximaze);
137
- } else {
159
+ playerAnimationState === PlayerAnimationStateEnum.drag_scroll &&
138
160
  resetPlayerAnimationState();
139
- }
140
161
  }
141
- } else {
142
- playerAnimationState === PlayerAnimationStateEnum.drag_scroll &&
143
- resetPlayerAnimationState();
144
- }
145
- };
162
+ },
163
+ [lastSnap, modalSnapPoints, playerAnimationState, videoModalState.mode]
164
+ );
146
165
 
147
166
  const onScroll = React.useCallback(({ nativeEvent }) => {
148
167
  scrollPosition.current = nativeEvent.contentOffset.y;
@@ -290,15 +309,13 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
290
309
  >
291
310
  <View pointerEvents="box-none">
292
311
  <PanGestureHandler
293
- enabled={
294
- (isMaximazedModal || isMinimizedModal) &&
295
- isNotMinimizeMaximazeAnimation
296
- }
312
+ enabled={isEnablePanGesture}
297
313
  ref={panHandlerRef}
298
314
  simultaneousHandlers={[scrollRef, tapHandlerRef]}
299
315
  shouldCancelWhenOutside={isMaximazedModal}
300
316
  onGestureEvent={onGestureEvent}
301
317
  onHandlerStateChange={onHandlerStateChange}
318
+ activeOffsetY={[-5, 5]}
302
319
  >
303
320
  <Animated.View>
304
321
  <NativeViewGestureHandler