@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5053228432 → 12.0.0-alpha.5061452310

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 (33) hide show
  1. package/Components/Cell/Cell.tsx +2 -2
  2. package/Components/Cell/CellWithFocusable.tsx +21 -14
  3. package/Components/Cell/TvOSCellComponent.tsx +2 -2
  4. package/Components/FocusableCell/index.tsx +7 -7
  5. package/Components/HookRenderer/index.tsx +1 -3
  6. package/Components/Image/utils/index.ts +21 -11
  7. package/Components/Layout/TV/__tests__/index.test.tsx +1 -13
  8. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +14 -1
  9. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  10. package/Components/MasterCell/utils/index.ts +18 -7
  11. package/Components/PlayerContainer/PlayerContainer.tsx +11 -8
  12. package/Components/PlayerImageBackground/index.tsx +34 -8
  13. package/Components/River/__tests__/componentsMap.test.js +7 -0
  14. package/Components/Screen/__tests__/Screen.test.tsx +5 -1
  15. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +10 -2
  16. package/Components/Screen/index.tsx +15 -13
  17. package/Components/Tabs/TV/styles.ts +1 -1
  18. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +25 -5
  19. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  20. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +335 -0
  21. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +144 -0
  22. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +438 -0
  23. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +143 -0
  24. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  25. package/Components/VideoModal/ModalAnimation/utils.ts +299 -0
  26. package/Components/VideoModal/PlayerWrapper.tsx +64 -41
  27. package/Components/VideoModal/VideoModal.tsx +8 -25
  28. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +6 -1
  29. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +246 -166
  30. package/Components/VideoModal/utils.ts +3 -3
  31. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  32. package/package.json +5 -5
  33. package/Components/VideoModal/hooks/usePosition.ts +0 -50
@@ -202,7 +202,7 @@ export class CellComponent extends React.Component<Props> {
202
202
  groupId,
203
203
  isFocusable = true,
204
204
  skipFocusManagerRegistration,
205
- behavior
205
+ behavior,
206
206
  } = this.props;
207
207
 
208
208
  const { id } = item;
@@ -263,7 +263,7 @@ export class CellComponent extends React.Component<Props> {
263
263
  item,
264
264
  focused: this.isCellFocused(focused),
265
265
  scrollTo: this.scrollToIndex(event),
266
- behavior
266
+ behavior,
267
267
  }}
268
268
  />
269
269
  );
@@ -35,18 +35,18 @@ const useCellFocusedState = (groupId: string, id: string) => {
35
35
  };
36
36
 
37
37
  type Props = {
38
- item: ZappEntry,
39
- CellRenderer: React.ComponentType<{ item: Object, state: string }>,
40
- id: string,
41
- groupId: string,
42
- onFocus: Function,
43
- index: number,
44
- scrollTo: Function,
45
- preferredFocus?: boolean,
46
- skipFocusManagerRegistration?: boolean,
47
- isFocusable?: boolean,
48
- behavior: Behavior,
49
- selected?: boolean,
38
+ item: ZappEntry;
39
+ CellRenderer: React.ComponentType<{ item: Object; state: string }>;
40
+ id: string;
41
+ groupId: string;
42
+ onFocus: Function;
43
+ index: number;
44
+ scrollTo: Function;
45
+ preferredFocus?: boolean;
46
+ skipFocusManagerRegistration?: boolean;
47
+ isFocusable?: boolean;
48
+ behavior: Behavior;
49
+ selected?: boolean;
50
50
  };
51
51
 
52
52
  export function CellWithFocusable(props: Props) {
@@ -62,11 +62,18 @@ export function CellWithFocusable(props: Props) {
62
62
  skipFocusManagerRegistration,
63
63
  isFocusable,
64
64
  behavior,
65
- selected
65
+ selected,
66
66
  } = props;
67
67
 
68
68
  const isFocused = useCellFocusedState(groupId, id);
69
- const state = useCellState({ id: item.id, behavior, focused: isFocused, selected });
69
+
70
+ const state = useCellState({
71
+ id: item.id,
72
+ behavior,
73
+ focused: isFocused,
74
+ selected,
75
+ });
76
+
70
77
  const [focusedButtonId, setFocusedButtonId] = React.useState(undefined);
71
78
 
72
79
  // for horizontal scrolling
@@ -249,7 +249,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
249
249
  hasReceivedFocus,
250
250
  groupId,
251
251
  isFocusable,
252
- behavior
252
+ behavior,
253
253
  } = this.props;
254
254
 
255
255
  const { id } = item;
@@ -310,7 +310,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
310
310
  item,
311
311
  focused: this.props.focused || selectedCell || focused,
312
312
  scrollTo: this.scrollTo,
313
- behavior
313
+ behavior,
314
314
  }}
315
315
  />
316
316
  );
@@ -3,12 +3,12 @@ import { Platform } from "react-native";
3
3
  import { useCellState } from "../MasterCell/utils";
4
4
 
5
5
  type Props = {
6
- index: number,
7
- item: ZappEntry,
8
- focused: boolean,
9
- scrollTo?: (index: number) => void,
10
- CellRenderer: React.ComponentType<{ item: Object, state: string }>,
11
- behavior: Behavior
6
+ index: number;
7
+ item: ZappEntry;
8
+ focused: boolean;
9
+ scrollTo?: (index: number) => void;
10
+ CellRenderer: React.ComponentType<{ item: Object; state: string }>;
11
+ behavior: Behavior;
12
12
  };
13
13
 
14
14
  const isAndroid = Platform.OS === "android";
@@ -20,7 +20,7 @@ export function FocusableCell(props: Props) {
20
20
  CellRenderer,
21
21
  focused,
22
22
  scrollTo = null,
23
- behavior
23
+ behavior,
24
24
  } = props;
25
25
 
26
26
  const state = useCellState({ id: item.id, behavior, focused });
@@ -1,3 +1 @@
1
- import { HookRenderer as HookRendererComponent } from "./HookRenderer";
2
-
3
- export const HookRenderer = HookRendererComponent;
1
+ export { HookRenderer } from "./HookRenderer";
@@ -1,19 +1,29 @@
1
+ import FastImage, { Source } from "@applicaster/react-native-fast-image";
1
2
  import { useMemo } from "react";
3
+ import { ImageRequireSource } from "react-native";
2
4
 
3
- export const useImageSource = (source) => {
5
+ function getSourceCache(cache: Source["cache"]) {
6
+ return cache || FastImage.cacheControl.web;
7
+ }
8
+
9
+ export const useImageSource = (source?: Source | ImageRequireSource) => {
4
10
  return useMemo(() => {
5
- if (typeof source === "number") {
6
- return source;
11
+ if (source) {
12
+ if (typeof source === "number") {
13
+ return source;
14
+ }
15
+
16
+ return {
17
+ uri: source.uri,
18
+ headers: source.headers,
19
+ priority: source.priority,
20
+ cache: getSourceCache(source?.cache),
21
+ maxWidth: source.maxWidth,
22
+ maxHeight: source.maxHeight,
23
+ };
7
24
  }
8
25
 
9
- return {
10
- uri: source.uri,
11
- headers: source.headers,
12
- priority: source.priority,
13
- cache: source.cache,
14
- maxWidth: source.maxWidth,
15
- maxHeight: source.maxHeight,
16
- };
26
+ return undefined;
17
27
  }, [
18
28
  ...(typeof source === "object"
19
29
  ? [
@@ -12,19 +12,7 @@ jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
12
12
  useRiverInitialState: () => ({}),
13
13
  }));
14
14
 
15
- jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
16
- useCurrentScreenData: jest.fn(() => ({
17
- navigations: [
18
- {
19
- category: "menu",
20
- navigation_type: "menu",
21
- styles: {
22
- hide_secondary_level_menu: false,
23
- },
24
- },
25
- ],
26
- })),
27
- }));
15
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen");
28
16
 
29
17
  const Layout = require("..").default;
30
18
 
@@ -177,10 +177,23 @@ const getPlayerConfig = (player_screen_id, actionIdentifier) => {
177
177
  const positionAbsolute: ViewStyle = { position: "absolute" };
178
178
 
179
179
  const LiveImageComponent = (props: LiveImageProps) => {
180
- const { item, style, cellUUID, imageKey, actionIdentifier, state } = props;
180
+ const {
181
+ item,
182
+ style: componentStyle,
183
+ cellUUID,
184
+ imageKey,
185
+ actionIdentifier,
186
+ state,
187
+ } = props;
181
188
 
182
189
  const component = useUIComponentContext();
183
190
 
191
+ // Fix for blinking on state change
192
+ const style = React.useMemo(
193
+ () => ({ ...componentStyle, backgroundColor: "transparent" }),
194
+ [componentStyle]
195
+ );
196
+
184
197
  const componentType = React.useMemo(
185
198
  () => getPropComponentType(component),
186
199
  [component?.id]
@@ -23,7 +23,11 @@ export const Toggle = (props: Props) => {
23
23
  iosBackgroundColor,
24
24
  } = style;
25
25
 
26
- const isSelected = React.useMemo(() => (cellState === "selected"), [item?.id, cellState]);
26
+ const isSelected = React.useMemo(
27
+ () => cellState === "selected",
28
+ [item?.id, cellState]
29
+ );
30
+
27
31
  const actionContext = useActions(action?.identifier);
28
32
 
29
33
  const [actionState, setActionState] = useState(
@@ -125,13 +125,24 @@ export const useFilterChildren = <
125
125
 
126
126
  const filteredChildren = children.filter((child) => {
127
127
  const item = child.props.item;
128
- const action = actions.actions[child.props.pluginIdentifier];
128
+ const actionIdentifier = child.props.pluginIdentifier;
129
+ const action = actions.actions[actionIdentifier];
130
+
129
131
  // context value of specific plugin
130
- const currentValue = action.module.context._currentValue;
132
+ if (action?.module && action.module.context) {
133
+ const currentValue = action.module.context._currentValue;
134
+
135
+ return currentValue?.isActionAvailable
136
+ ? currentValue.isActionAvailable(item)
137
+ : true;
138
+ }
139
+
140
+ masterCellLogger.error({
141
+ message: `Action plugin for ${actionIdentifier} could not be found, check the configuration of your action button`,
142
+ data: { item, action: child.props.pluginIdentifier },
143
+ });
131
144
 
132
- return currentValue?.isActionAvailable
133
- ? currentValue.isActionAvailable(item)
134
- : true;
145
+ return false;
135
146
  });
136
147
 
137
148
  return filteredChildren;
@@ -285,8 +296,8 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
285
296
  }
286
297
  }
287
298
  }, [behavior]);
288
-
289
- useEffect(() => {
299
+
300
+ useEffect(() => {
290
301
  if (!behavior) {
291
302
  return;
292
303
  }
@@ -54,6 +54,8 @@ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
54
54
  import { usePlayNextOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayNextOverlay";
55
55
  import { PlayNextState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/OverlayObserver/OverlaysObserver";
56
56
 
57
+ import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
58
+
57
59
  type Props = {
58
60
  Player: React.ComponentType<any>;
59
61
  PlayerLoadingView?: React.ComponentType<any>; // 👀 we are not receiving this prop
@@ -145,13 +147,7 @@ const renderApplePlayer = ({
145
147
  }) => {
146
148
  const { title, summary } = item || {};
147
149
 
148
- if (!isApplePlatform() || !player) {
149
- return null;
150
- }
151
-
152
- // TODO: Check if it is not breaking default player logic on tvos
153
- // Also if needs to add on tvos brightcove player for QB player
154
- if (player.supportsNativeControls()) {
150
+ if (!isAppleTV() || !player) {
155
151
  return null;
156
152
  }
157
153
 
@@ -604,6 +600,9 @@ const PlayerContainerComponent = (props: Props) => {
604
600
  pluginConfiguration,
605
601
  };
606
602
 
603
+ const { isActiveGesture, startComponentsAnimation } =
604
+ useModalAnimationContext();
605
+
607
606
  return (
608
607
  <PlayerStateContext.Provider value={value}>
609
608
  <PlayerContainerContextProvider
@@ -647,7 +646,11 @@ const PlayerContainerComponent = (props: Props) => {
647
646
  autoplay={true}
648
647
  controls={false}
649
648
  disableCastAction={disableCastAction}
650
- docked={navigator.isVideoModalDocked()}
649
+ docked={
650
+ navigator.isVideoModalDocked() &&
651
+ !startComponentsAnimation &&
652
+ !isActiveGesture
653
+ }
651
654
  entry={item}
652
655
  fullscreen={mode === VideoModalMode.FULLSCREEN}
653
656
  inline={inline}
@@ -1,38 +1,64 @@
1
1
  import React, { PropsWithChildren } from "react";
2
- import { ImageBackground } from "react-native";
2
+ import { ImageBackground, View } from "react-native";
3
3
 
4
4
  import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
5
+ import {
6
+ AnimationComponent,
7
+ ComponentAnimationType,
8
+ useModalAnimationContext,
9
+ PlayerAnimationStateEnum,
10
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
5
11
 
6
12
  type Props = PropsWithChildren<{
7
13
  entry: ZappEntry;
8
14
  style?: { [K: string]: any };
9
15
  imageStyle?: { [K: string]: any };
10
16
  imageKey?: string;
17
+ defaultImageDimensions?: { [K: string]: any };
11
18
  }>;
12
19
 
20
+ const imageSize = { width: "100%", height: "100%" };
21
+
13
22
  const PlayerImageBackgroundComponent = ({
14
23
  entry,
15
24
  children,
16
25
  style,
17
26
  imageStyle,
18
27
  imageKey,
28
+ defaultImageDimensions,
19
29
  }: Props) => {
20
30
  const source = React.useMemo(
21
31
  () => ({ uri: imageSrcFromMediaItem(entry, imageKey) }),
22
32
  [imageKey, entry]
23
33
  );
24
34
 
35
+ const { playerAnimationState } = useModalAnimationContext();
36
+
25
37
  if (!source) return <>{children}</>;
26
38
 
27
39
  return (
28
- <ImageBackground
29
- resizeMode="cover"
30
- style={style}
31
- imageStyle={imageStyle}
32
- source={source}
40
+ <View
41
+ style={
42
+ playerAnimationState === PlayerAnimationStateEnum.maximaze
43
+ ? defaultImageDimensions
44
+ : style
45
+ }
33
46
  >
34
- {children}
35
- </ImageBackground>
47
+ <AnimationComponent
48
+ style={style}
49
+ animationType={ComponentAnimationType.player}
50
+ additionalData={defaultImageDimensions}
51
+ >
52
+ <ImageBackground
53
+ resizeMode="cover"
54
+ style={imageSize}
55
+ imageStyle={imageStyle}
56
+ source={source}
57
+ >
58
+ {children}
59
+ </ImageBackground>
60
+ </AnimationComponent>
61
+ </View>
36
62
  );
37
63
  };
38
64
 
@@ -6,6 +6,12 @@ import configureStore from "redux-mock-store";
6
6
 
7
7
  import * as themeUtils from "@applicaster/zapp-react-native-utils/theme";
8
8
 
9
+ // silences the errors related to React-native-gesture-handler in tests
10
+ jest.mock(
11
+ "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation",
12
+ () => () => null
13
+ );
14
+
9
15
  jest.mock("react-native-safe-area-context", () => ({
10
16
  useSafeAreaInsets: () => ({ top: 50 }),
11
17
  }));
@@ -83,6 +89,7 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
83
89
  useNavigation: jest.fn(() => ({ currentRoute: "/river/A1234" })),
84
90
  useRoute: jest.fn(() => ({ pathname: "/river/A1234", screenData })),
85
91
  useProfilerLogging: jest.fn(),
92
+ useIsScreenActive: jest.fn().mockReturnValue(true),
86
93
  }));
87
94
 
88
95
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { View } from "react-native";
3
- import { create, act } from "react-test-renderer";
3
+ import { act, create } from "react-test-renderer";
4
4
 
5
5
  const Mocked_RouteManager = jest.fn((props) => (
6
6
  <View testID="routeManager" {...props} />
@@ -69,12 +69,16 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
69
69
  isNavBarVisible: mockIsNavBarVisible,
70
70
  useRoute: jest.fn(() => ({
71
71
  pathname: "/river/testId",
72
+ screenData: { id: "testId" },
72
73
  })),
73
74
  useNavigation: jest.fn(() => ({
74
75
  canGoBack: () => false,
75
76
  currentRoute: "/river/testId",
77
+ activeRiver: { id: "testId" },
78
+ screenData: { id: "testId" },
76
79
  data: { screen: { id: "testId" } },
77
80
  })),
81
+ useIsScreenActive: jest.fn().mockReturnValue(true),
78
82
  }));
79
83
 
80
84
  jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
@@ -19,7 +19,11 @@ exports[`<Screen Component /> when the navbar should be hidden renders correctly
19
19
  <View>
20
20
  <View
21
21
  pathname="/river/testId"
22
- screenData={Object {}}
22
+ screenData={
23
+ Object {
24
+ "id": "testId",
25
+ }
26
+ }
23
27
  testID="routeManager"
24
28
  />
25
29
  </View>
@@ -45,7 +49,11 @@ exports[`<Screen Component /> when the navbar should show renders correctly 1`]
45
49
  <View>
46
50
  <View
47
51
  pathname="/river/testId"
48
- screenData={Object {}}
52
+ screenData={
53
+ Object {
54
+ "id": "testId",
55
+ }
56
+ }
49
57
  testID="routeManager"
50
58
  />
51
59
  </View>
@@ -23,6 +23,17 @@ import { RouteManager } from "../RouteManager";
23
23
  import { useScreenConfiguration } from "../River/useScreenConfiguration";
24
24
  import { useScreenOrientationHandler } from "./orientationHandler";
25
25
 
26
+ const getScreenId = (
27
+ screenData: LegacyNavigationScreenData,
28
+ fallbackRiver: ZappRiver
29
+ ) => {
30
+ return screenData?.targetScreen
31
+ ? screenData?.targetScreen?.id ?? fallbackRiver.id
32
+ : "screen_id" in screenData
33
+ ? screenData.screen_id
34
+ : fallbackRiver.id;
35
+ };
36
+
26
37
  function colorIsNotTransparent(color) {
27
38
  const layers = R.compose(
28
39
  R.map(R.trim),
@@ -41,17 +52,11 @@ function isValidColor(string) {
41
52
  }
42
53
 
43
54
  type Props = {
44
- pathname: string;
45
- screenData: Record<string, any>;
46
55
  setToggleMenu: () => void;
47
56
  setCloseMenu: () => void;
48
57
  };
49
58
 
50
- export function Screen({
51
- setToggleMenu,
52
- setCloseMenu,
53
- screenData: _screenData,
54
- }: Props) {
59
+ export function Screen({ setToggleMenu, setCloseMenu }: Props) {
55
60
  const theme = useTheme<BaseThemePropertiesMobile>();
56
61
  const navigation = useNavigation();
57
62
  const { plugins } = usePickFromState(["plugins"]);
@@ -68,10 +73,7 @@ export function Screen({
68
73
  const routeData = useRoute();
69
74
 
70
75
  const currentRiver = useScreenData(
71
- (routeData.screenData as ZappRiver)?.targetScreen
72
- ? (routeData.screenData as ZappRiver)?.targetScreen?.id ??
73
- navigation.activeRiver.id
74
- : (routeData.screenData as ZappRiver)?.screen_id
76
+ getScreenId(routeData.screenData, navigation.activeRiver)
75
77
  );
76
78
 
77
79
  const { title } = useNavbarState();
@@ -135,9 +137,9 @@ export function Screen({
135
137
  {...{ ...(navBarProps as MobileNavBarPluginProps), id: pathname }}
136
138
  />
137
139
 
138
- <OfflineFallbackScreen screenData={screenData}>
140
+ <OfflineFallbackScreen screenData={targetScreenData}>
139
141
  {/* @TODO RouteManager doesn't use props, can they be removed ? */}
140
- <RouteManager pathname={pathname} screenData={_screenData} />
142
+ <RouteManager pathname={pathname} screenData={screenData} />
141
143
  </OfflineFallbackScreen>
142
144
  </View>
143
145
  </ScreenContextProvider>
@@ -78,7 +78,7 @@ export const getStyles = (config): Partial<TVTabsStyles> => {
78
78
  },
79
79
  tvos: {
80
80
  marginTop: -1,
81
- }
81
+ },
82
82
  }),
83
83
  },
84
84
  tabsBackground: {
@@ -4,8 +4,13 @@ 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
6
  import { ThemeContext } from "@applicaster/zapp-react-native-utils/theme";
7
+ import * as navigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
7
8
 
8
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";
9
14
 
10
15
  const seekTo = jest.fn();
11
16
 
@@ -18,12 +23,25 @@ jest.mock(
18
23
  })
19
24
  );
20
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
+
21
36
  const Wrapper = ({ children }: { children: React.ReactChildren }) => (
22
- <ThemeContext.Provider
23
- value={{ themes: { myTheme: {} }, selectedThemeId: "myTheme" }}
24
- >
25
- {children}
26
- </ThemeContext.Provider>
37
+ /* @ts-ignore */
38
+ <Provider store={store}>
39
+ <ThemeContext.Provider
40
+ value={{ themes: { myTheme: {} }, selectedThemeId: "myTheme" }}
41
+ >
42
+ {children}
43
+ </ThemeContext.Provider>
44
+ </Provider>
27
45
  );
28
46
 
29
47
  describe("PlayerLiveImageComponent", () => {
@@ -46,6 +64,8 @@ describe("PlayerLiveImageComponent", () => {
46
64
  controller: new Player({}),
47
65
  Component: () => null,
48
66
  }));
67
+
68
+ jest.spyOn(navigationHooks, "useIsScreenActive").mockReturnValue(true);
49
69
  });
50
70
 
51
71
  it("should render correctly with default props", () => {
@@ -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
+ };