@applicaster/zapp-react-native-ui-components 13.0.0-alpha.1045252438 → 13.0.0-alpha.1124981321

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 (27) hide show
  1. package/Components/BaseFocusable/index.ios.ts +1 -1
  2. package/Components/BaseFocusable/index.tsx +1 -1
  3. package/Components/Focusable/Touchable.tsx +19 -20
  4. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +66 -0
  5. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -1
  6. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -11
  7. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/__tests__/index.test.ts +93 -0
  8. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +1 -1
  9. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  10. package/Components/MasterCell/utils/index.ts +1 -1
  11. package/Components/Screen/hooks.ts +56 -0
  12. package/Components/Screen/index.tsx +13 -39
  13. package/Components/Tabs/Tab.tsx +6 -6
  14. package/Components/TextInputTv/index.tsx +2 -2
  15. package/Components/Transitioner/AnimationManager.js +8 -8
  16. package/Components/Transitioner/Scene.tsx +52 -23
  17. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +59 -43
  18. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +2 -2
  19. package/Components/Transitioner/index.js +8 -4
  20. package/Components/VideoLive/LiveImageManager.ts +27 -1
  21. package/Components/VideoLive/PlayerLiveImageComponent.tsx +11 -5
  22. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +0 -5
  23. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +7 -6
  24. package/Components/VideoModal/ModalAnimation/utils.ts +2 -2
  25. package/Contexts/ScreenContext/index.tsx +3 -2
  26. package/Decorators/ZappPipesDataConnector/__tests__/Hero.js +1 -1
  27. package/package.json +5 -5
@@ -25,7 +25,7 @@ type Props = {
25
25
  };
26
26
 
27
27
  export class BaseFocusable<
28
- T extends Props = Props
28
+ T extends Props = Props,
29
29
  > extends BaseFocusableInterface<T> {
30
30
  constructor(props) {
31
31
  super(props);
@@ -35,7 +35,7 @@ type Props = {
35
35
  };
36
36
 
37
37
  export class BaseFocusable<
38
- T extends Props = Props
38
+ T extends Props = Props,
39
39
  > extends BaseFocusableInterface<T> {
40
40
  constructor(props) {
41
41
  super(props);
@@ -1,25 +1,24 @@
1
1
  import * as React from "react";
2
2
 
3
- type Props =
4
- | {
5
- id: string;
6
- children: React.ReactNode;
7
- onPress?: (ref: FocusManager.TouchableRef) => void;
8
- onPressIn?: (ref: FocusManager.TouchableRef) => void;
9
- onPressOut?: (ref: FocusManager.TouchableRef) => void;
10
- onLongPress?: (ref: FocusManager.TouchableRef) => void;
11
- onFocus?: (
12
- ref: FocusManager.TouchableRef,
13
- options: FocusManager.Android.CallbackOptions
14
- ) => void;
15
- onBlur?: (
16
- ref: FocusManager.TouchableRef,
17
- options: FocusManager.Android.CallbackOptions
18
- ) => void;
19
-
20
- disableFocus?: boolean;
21
- blockFocus?: boolean;
22
- } & Partial<ParentFocus>;
3
+ type Props = {
4
+ id: string;
5
+ children: React.ReactNode;
6
+ onPress?: (ref: FocusManager.TouchableRef) => void;
7
+ onPressIn?: (ref: FocusManager.TouchableRef) => void;
8
+ onPressOut?: (ref: FocusManager.TouchableRef) => void;
9
+ onLongPress?: (ref: FocusManager.TouchableRef) => void;
10
+ onFocus?: (
11
+ ref: FocusManager.TouchableRef,
12
+ options: FocusManager.Android.CallbackOptions
13
+ ) => void;
14
+ onBlur?: (
15
+ ref: FocusManager.TouchableRef,
16
+ options: FocusManager.Android.CallbackOptions
17
+ ) => void;
18
+
19
+ disableFocus?: boolean;
20
+ blockFocus?: boolean;
21
+ } & Partial<ParentFocus>;
23
22
 
24
23
  export class Touchable extends React.Component<Props> {
25
24
  onPress(focusableRef: FocusManager.TouchableRef): void {
@@ -0,0 +1,66 @@
1
+ import {
2
+ BorderContainerView,
3
+ getBorderPadding, // Export for testing (using a double underscore prefix is a common convention)
4
+ } from "../index";
5
+ import * as React from "react";
6
+ import { render } from "@testing-library/react-native";
7
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
8
+ import { View } from "react-native";
9
+
10
+ jest.mock("@applicaster/zapp-react-native-utils/numberUtils", () => ({
11
+ toNumberWithDefaultZero: jest.fn((value) => Number(value) || 0),
12
+ }));
13
+
14
+ describe("BorderContainerView", () => {
15
+ describe("getBorderPadding", () => {
16
+ it("returns 0 for inside", () => {
17
+ expect(getBorderPadding("inside", 10)).toBe(0);
18
+ });
19
+
20
+ it("returns borderWidth / 2 for center", () => {
21
+ expect(getBorderPadding("center", 10)).toBe(5);
22
+ });
23
+
24
+ it("returns borderWidth for outside", () => {
25
+ expect(getBorderPadding("outside", 10)).toBe(10);
26
+ });
27
+
28
+ it("returns borderWidth for invalid position", () => {
29
+ // @ts-ignore
30
+ expect(getBorderPadding("other_value", 10)).toBe(10);
31
+ });
32
+ });
33
+
34
+ it("Border component renders null if no borderPosition", () => {
35
+ const style = { borderWidth: 5, borderRadius: 10, borderColor: "red" };
36
+
37
+ const padding = {
38
+ paddingTop: 2,
39
+ paddingRight: 3,
40
+ paddingBottom: 4,
41
+ paddingLeft: 5,
42
+ };
43
+
44
+ const borderPosition = null;
45
+
46
+ const { queryByTestId } = render(
47
+ <BorderContainerView
48
+ style={style}
49
+ testID="border-container"
50
+ borderPosition={borderPosition}
51
+ borderPaddingTop={toNumberWithDefaultZero(padding.paddingTop)}
52
+ borderPaddingRight={toNumberWithDefaultZero(padding.paddingRight)}
53
+ borderPaddingBottom={toNumberWithDefaultZero(padding.paddingBottom)}
54
+ borderPaddingLeft={toNumberWithDefaultZero(padding.paddingLeft)}
55
+ >
56
+ <View testID="child" />
57
+ </BorderContainerView>
58
+ );
59
+
60
+ const children = queryByTestId("border-container").children;
61
+
62
+ expect(children[1].props.testID).toBe("child");
63
+
64
+ expect(children[0].children.length).toBe(0);
65
+ });
66
+ });
@@ -28,7 +28,10 @@ const styles = StyleSheet.create({
28
28
  },
29
29
  });
30
30
 
31
- const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
31
+ export const getBorderPadding = (
32
+ borderPosition: BorderPosition,
33
+ borderWidth: number
34
+ ) => {
32
35
  switch (borderPosition) {
33
36
  case "inside":
34
37
  return 0;
@@ -38,22 +38,16 @@ function Image({
38
38
 
39
39
  const shouldRenderImg = source && !error;
40
40
 
41
- // Default placeholder image fallback is empty string because it won't rerender
42
- // in case of undefined or {uri: undefined} source
43
- // https://github.com/facebook/react-native/issues/9195
44
- const defaultPlaceHolderImage = React.useMemo(
45
- () => ({ uri: placeholderImage || "" }),
46
- [source, error]
47
- );
41
+ const _source = shouldRenderImg
42
+ ? withDimensions(source)
43
+ : { uri: placeholderImage };
48
44
 
49
45
  return (
50
- // @ts-ignore
51
46
  <MemoizedImage
52
47
  style={style as ImageStyle}
53
48
  onError={React.useCallback(() => setErrorState(true), [])}
54
- source={
55
- shouldRenderImg ? withDimensions(source) : defaultPlaceHolderImage
56
- }
49
+ // as we have defaults as "" for placeholder image, we need to pass undefined to source to not throw warnings
50
+ source={_source?.uri ? _source : undefined}
57
51
  {...R.omit(["source"], otherProps)}
58
52
  />
59
53
  );
@@ -0,0 +1,93 @@
1
+ import { ImageBorderContainer } from "../index";
2
+ import { getImageContainerMarginStyles } from "@applicaster/zapp-react-native-utils/cellUtils";
3
+
4
+ const mockValue = (key) => {
5
+ const values = {
6
+ cell_padding_top: 1,
7
+ cell_padding_left: 2,
8
+ cell_padding_right: 3,
9
+ cell_padding_bottom: 4,
10
+ image_border_size: 5,
11
+ image_focused_border_color: "red",
12
+ image_selected_border_color: "blue",
13
+ image_focused_selected_border_color: "green",
14
+ image_border_color: "black",
15
+ image_corner_radius: 10,
16
+ image_border_position: "outside",
17
+ image_border_padding_top: 6,
18
+ image_border_padding_right: 7,
19
+ image_border_padding_bottom: 8,
20
+ image_border_padding_left: 9,
21
+ image_margin_top: 11,
22
+ image_margin_left: 12,
23
+ image_margin_right: 13,
24
+ image_margin_bottom: 14,
25
+ };
26
+
27
+ return values[key] || 0;
28
+ };
29
+
30
+ describe("ImageBorderContainer", () => {
31
+ it("calculates style properties correctly", () => {
32
+ const marginStyles = getImageContainerMarginStyles({ value: mockValue });
33
+
34
+ expect(marginStyles.marginTop).toBe(11);
35
+ expect(marginStyles.marginLeft).toBe(12);
36
+ expect(marginStyles.marginRight).toBe(13);
37
+ expect(marginStyles.marginBottom).toBe(14);
38
+
39
+ const props = {
40
+ value: mockValue,
41
+ state: "default",
42
+ imageStyles: {},
43
+ };
44
+
45
+ const result = ImageBorderContainer(props);
46
+
47
+ expect(result.style.marginTop).toBe(12); // 11 + 1
48
+ expect(result.style.marginLeft).toBe(14); // 12 + 2
49
+ expect(result.style.marginRight).toBe(16); // 13 + 3
50
+ expect(result.style.marginBottom).toBe(18); // 14 + 4
51
+ expect(result.style.borderWidth).toBe(5);
52
+ expect(result.style.borderColor).toBe("black");
53
+ expect(result.style.borderRadius).toBe(10);
54
+ expect(result.additionalProps.borderPosition).toBe("outside");
55
+ expect(result.additionalProps.borderPaddingTop).toBe(6);
56
+ expect(result.additionalProps.borderPaddingRight).toBe(7);
57
+ expect(result.additionalProps.borderPaddingBottom).toBe(8);
58
+ expect(result.additionalProps.borderPaddingLeft).toBe(9);
59
+ });
60
+
61
+ it("handles focused state correctly", () => {
62
+ const props = {
63
+ value: mockValue,
64
+ state: "focused",
65
+ imageStyles: {},
66
+ };
67
+
68
+ const result = ImageBorderContainer(props);
69
+ expect(result.style.borderColor).toBe("red");
70
+ });
71
+
72
+ it("handles selected state correctly", () => {
73
+ const props = {
74
+ value: mockValue,
75
+ state: "selected",
76
+ imageStyles: {},
77
+ };
78
+
79
+ const result = ImageBorderContainer(props);
80
+ expect(result.style.borderColor).toBe("blue");
81
+ });
82
+
83
+ it("handles focused and selected state correctly", () => {
84
+ const props = {
85
+ value: mockValue,
86
+ state: "focused_selected",
87
+ imageStyles: {},
88
+ };
89
+
90
+ const result = ImageBorderContainer(props);
91
+ expect(result.style.borderColor).toBe("green");
92
+ });
93
+ });
@@ -17,7 +17,7 @@ const SECONDARY_IMAGE_PREFIX = "secondary_image";
17
17
 
18
18
  type ImageSizing = typeof IMAGE_SIZING_FIT | typeof IMAGE_SIZING_FILL;
19
19
 
20
- type ImagePosition = typeof IMAGE_POSITION[keyof typeof IMAGE_POSITION];
20
+ type ImagePosition = (typeof IMAGE_POSITION)[keyof typeof IMAGE_POSITION];
21
21
 
22
22
  export type DisplayMode =
23
23
  | typeof DISPLAY_MODE_FIXED
@@ -39,6 +39,6 @@ describe("image with no source", () => {
39
39
  );
40
40
 
41
41
  const testInstance = testRenderer.root;
42
- expect(testInstance.findByType(Image).props.source).toEqual({ uri: "" });
42
+ expect(testInstance.findByType(Image).props.source).toBeUndefined();
43
43
  });
44
44
  });
@@ -103,7 +103,7 @@ export const useFilterChildren = <
103
103
  item: any;
104
104
  pluginIdentifier: string;
105
105
  };
106
- }
106
+ },
107
107
  >(
108
108
  children: T[]
109
109
  ): T[] => {
@@ -0,0 +1,56 @@
1
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
2
+ import {
3
+ useGetScreenOrientation,
4
+ isOrientationCompatible,
5
+ } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
6
+ import {
7
+ useCurrentScreenData,
8
+ useDimensions,
9
+ useRoute,
10
+ } from "@applicaster/zapp-react-native-utils/reactHooks";
11
+ import { useMemo, useEffect, useState } from "react";
12
+
13
+ export const useWaitForValidOrientation = () => {
14
+ const {
15
+ width: screenWidth,
16
+ height,
17
+ deviceInfo,
18
+ } = useDimensions("screen", {
19
+ fullDimensions: true,
20
+ updateForInactiveScreens: false,
21
+ });
22
+
23
+ const currentScreenData = useCurrentScreenData();
24
+
25
+ const { screenData } = useRoute();
26
+
27
+ const [readyState, setReadyState] = useState(false);
28
+
29
+ const isTablet = deviceInfo?.isTablet;
30
+
31
+ const { appData } = usePickFromState(["appData"]);
32
+ const isTabletPortrait = appData?.isTabletPortrait;
33
+
34
+ const layoutData = useMemo(
35
+ () => ({ isTablet, isTabletPortrait, width: screenWidth, height }),
36
+ [isTablet, isTabletPortrait, screenWidth, height]
37
+ );
38
+
39
+ const targetScreenData =
40
+ currentScreenData || (screenData as any)?.targetScreen || screenData;
41
+
42
+ const orientation = useGetScreenOrientation(targetScreenData);
43
+
44
+ const isReadyForDisplay = isOrientationCompatible({
45
+ orientation,
46
+ layoutData,
47
+ });
48
+
49
+ useEffect(() => {
50
+ if (isReadyForDisplay && !readyState) {
51
+ setReadyState(true);
52
+ }
53
+ }, [readyState, orientation, layoutData]);
54
+
55
+ return readyState;
56
+ };
@@ -2,10 +2,6 @@
2
2
  import React from "react";
3
3
  import { View } from "react-native";
4
4
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
- import {
6
- isOrientationCompatible,
7
- useGetScreenOrientation,
8
- } from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
9
5
 
10
6
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
11
7
  import { getComponentModule } from "@applicaster/zapp-react-native-utils/pluginUtils";
@@ -15,7 +11,6 @@ import {
15
11
  getScreenId,
16
12
  } from "@applicaster/zapp-react-native-utils/navigationUtils";
17
13
  import {
18
- useDimensions,
19
14
  useRoute,
20
15
  useCurrentScreenData,
21
16
  useNavbarState,
@@ -27,6 +22,7 @@ import { getNavigationPluginModule } from "@applicaster/zapp-react-native-app/Ap
27
22
  import { RouteManager } from "../RouteManager";
28
23
  import { useScreenConfiguration } from "../River/useScreenConfiguration";
29
24
  import { isValidColor } from "./utils";
25
+ import { useWaitForValidOrientation } from "./hooks";
30
26
 
31
27
  const screenStyles = {
32
28
  flex: 1,
@@ -50,14 +46,6 @@ export function Screen(_props: Props) {
50
46
  const currentScreenData = useCurrentScreenData();
51
47
  const { backgroundColor } = useScreenConfiguration(currentScreenData.id);
52
48
 
53
- const {
54
- width: screenWidth,
55
- height,
56
- deviceInfo,
57
- } = useDimensions("screen", {
58
- fullDimensions: true,
59
- });
60
-
61
49
  const { screenData, pathname } = useRoute();
62
50
 
63
51
  const currentRiver = useScreenData(
@@ -66,13 +54,6 @@ export function Screen(_props: Props) {
66
54
 
67
55
  const { title } = useNavbarState();
68
56
 
69
- const isTablet = deviceInfo?.isTablet;
70
-
71
- const { appData } = usePickFromState(["appData"]);
72
- const isTabletPortrait = appData?.isTabletPortrait;
73
-
74
- const layoutData = { isTablet, isTabletPortrait, width: screenWidth, height };
75
-
76
57
  const hasMenu = shouldNavBarDisplayMenu(currentRiver, plugins);
77
58
 
78
59
  const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(
@@ -108,30 +89,23 @@ export function Screen(_props: Props) {
108
89
  [theme.app_background_color, backgroundColor]
109
90
  );
110
91
 
111
- const targetScreenData =
112
- currentScreenData || (screenData as any)?.targetScreen || screenData;
113
-
114
- const orientation = useGetScreenOrientation(targetScreenData);
92
+ // Set ready state when screen is rotated to desired orientation
93
+ const isReady = useWaitForValidOrientation();
115
94
 
116
95
  // We prevent rendering of the screen until UI is actually rotated to screen desired orientation.
117
96
  // This saves unnecessary re-renders and user will not see distorted aspect screen.
118
- if (
119
- !isOrientationCompatible({
120
- orientation,
121
- layoutData,
122
- })
123
- ) {
124
- return <View style={style} />;
125
- }
126
-
127
97
  return (
128
98
  <View style={style}>
129
- {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
130
-
131
- <OfflineFallbackScreen>
132
- {/* @TODO RouteManager doesn't use props, can they be removed ? */}
133
- <RouteManager pathname={pathname} screenData={screenData} />
134
- </OfflineFallbackScreen>
99
+ {isReady ? (
100
+ <>
101
+ {navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
102
+
103
+ <OfflineFallbackScreen>
104
+ {/* @TODO RouteManager doesn't use props, can they be removed ? */}
105
+ <RouteManager pathname={pathname} screenData={screenData} />
106
+ </OfflineFallbackScreen>
107
+ </>
108
+ ) : null}
135
109
  </View>
136
110
  );
137
111
  }
@@ -69,8 +69,8 @@ const getStyles = (configuration, selected, focused) => {
69
69
  ? tab_cell_background_color_selected_active
70
70
  : tab_cell_background_color_selected_default
71
71
  : focused
72
- ? tab_cell_background_color_active
73
- : tab_cell_background_color_default,
72
+ ? tab_cell_background_color_active
73
+ : tab_cell_background_color_default,
74
74
  justifyContent: "center",
75
75
  alignItems: "center",
76
76
  position: "relative",
@@ -85,8 +85,8 @@ const getStyles = (configuration, selected, focused) => {
85
85
  ? tab_cell_border_color_active_focused
86
86
  : tab_cell_border_color_active
87
87
  : focused
88
- ? tab_cell_border_color_default_focused
89
- : tab_cell_border_color_default,
88
+ ? tab_cell_border_color_default_focused
89
+ : tab_cell_border_color_default,
90
90
  },
91
91
  label: {
92
92
  fontFamily: text_label_font_family,
@@ -98,8 +98,8 @@ const getStyles = (configuration, selected, focused) => {
98
98
  ? text_label_selected_active_font_color
99
99
  : text_label_selected_default_font_color
100
100
  : focused
101
- ? text_label_active_font_color
102
- : text_label_default_font_color,
101
+ ? text_label_active_font_color
102
+ : text_label_default_font_color,
103
103
  },
104
104
  underline: {
105
105
  position: "absolute",
@@ -94,8 +94,8 @@ function TextInputTV(props: Props, ref) {
94
94
  styles.textAlign === "center"
95
95
  ? styles.textAlign
96
96
  : styles.textAlign === "left"
97
- ? "right"
98
- : "left";
97
+ ? "right"
98
+ : "left";
99
99
 
100
100
  return { style: { ...styles, textAlign } };
101
101
  }
@@ -3,19 +3,19 @@ import { Animated } from "react-native";
3
3
  import { NAV_ACTION_PUSH, NAV_ACTION_BACK } from "./Transitioner";
4
4
 
5
5
  type TransitionConfig = {
6
- duration: number,
7
- easing: any,
6
+ duration: number;
7
+ easing: any;
8
8
  from: {
9
- style: any,
10
- },
9
+ style: any;
10
+ };
11
11
  to: {
12
- style: any,
13
- },
12
+ style: any;
13
+ };
14
14
  };
15
15
 
16
16
  type Props = {
17
- transitionConfig: TransitionConfig,
18
- contentStyle: { [string]: any },
17
+ transitionConfig: TransitionConfig;
18
+ contentStyle: { [string]: any };
19
19
  };
20
20
 
21
21
  /**
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { equals } from "ramda";
2
3
  import { Animated, ViewProps, ViewStyle } from "react-native";
3
4
  import { useSafeAreaFrame } from "react-native-safe-area-context";
4
5
 
@@ -8,6 +9,11 @@ import { PathnameContext } from "../../Contexts/PathnameContext";
8
9
  import { ScreenDataContext } from "../../Contexts/ScreenDataContext";
9
10
  import { ScreenContextProvider } from "../../Contexts/ScreenContext";
10
11
 
12
+ const fullWidthDimensions = {
13
+ height: "100%",
14
+ width: "100%",
15
+ };
16
+
11
17
  type Props = {
12
18
  children: any;
13
19
  style: any;
@@ -20,6 +26,47 @@ type Props = {
20
26
  screenData: NavigationScreenData;
21
27
  };
22
28
 
29
+ export function CurrentScreenContextProvider({
30
+ children,
31
+ ...props
32
+ }: {
33
+ children: any;
34
+ pathname: string;
35
+ isActive: boolean;
36
+ screenData: NavigationScreenData;
37
+ }) {
38
+ const { pathname, isActive = false, screenData } = props;
39
+
40
+ const [initialScreenData, setInitialScreenData] = React.useState(screenData);
41
+
42
+ React.useLayoutEffect(() => {
43
+ const screenDataChanged = !equals(initialScreenData, screenData);
44
+
45
+ if (isActive && screenDataChanged) {
46
+ setInitialScreenData(screenData);
47
+ }
48
+ }, [initialScreenData, screenData, isActive]);
49
+
50
+ return (
51
+ <ScreenDataContext.Provider value={initialScreenData}>
52
+ <PathnameContext.Provider value={pathname}>
53
+ <ScreenContextProvider pathname={pathname}>
54
+ {children}
55
+ </ScreenContextProvider>
56
+ </PathnameContext.Provider>
57
+ </ScreenDataContext.Provider>
58
+ );
59
+ }
60
+
61
+ const withCurrentScreenContext = (Component) =>
62
+ function SceneComponent(props: Props) {
63
+ return (
64
+ <CurrentScreenContextProvider {...props}>
65
+ <Component {...props} />
66
+ </CurrentScreenContextProvider>
67
+ );
68
+ };
69
+
23
70
  /**
24
71
  * Animated View container for any purpose.
25
72
  * Renders given children with given style object.
@@ -32,53 +79,35 @@ type Props = {
32
79
  * @param contentStyle
33
80
  * @param screenUniqueId
34
81
  */
35
- export function Scene({
82
+ function SceneComponent({
36
83
  children,
37
84
  style,
38
85
  pointerEvents = "auto",
39
86
  overlayStyle,
40
87
  animating,
41
88
  contentStyle,
42
- pathname,
43
89
  isActive = false,
44
90
  screenData,
45
91
  }: Props) {
46
- const [initialScreenData, setInitialScreenData] = React.useState(screenData);
47
-
48
92
  useScreenOrientationHandler({
49
93
  screenData,
50
94
  isActive,
51
95
  });
52
96
 
53
- React.useLayoutEffect(() => {
54
- if (isActive) {
55
- setInitialScreenData(screenData);
56
- }
57
- }, [screenData, isActive]);
58
-
59
97
  const frame = useSafeAreaFrame();
60
98
  const isAnimating = animating && overlayStyle;
61
99
 
62
- const dimensions = isAnimating
63
- ? {
64
- width: "100%",
65
- height: "100%",
66
- }
67
- : frame;
100
+ const dimensions = isAnimating ? fullWidthDimensions : frame;
68
101
 
69
102
  return (
70
103
  <Animated.View
71
104
  pointerEvents={pointerEvents}
72
105
  style={[style, contentStyle, dimensions]}
73
106
  >
74
- <ScreenDataContext.Provider value={initialScreenData}>
75
- <PathnameContext.Provider value={pathname}>
76
- <ScreenContextProvider pathname={pathname}>
77
- {children}
78
- </ScreenContextProvider>
79
- </PathnameContext.Provider>
80
- </ScreenDataContext.Provider>
107
+ {children}
81
108
  {isAnimating && <Animated.View style={overlayStyle} />}
82
109
  </Animated.View>
83
110
  );
84
111
  }
112
+
113
+ export const Scene = withCurrentScreenContext(SceneComponent);
@@ -1,63 +1,79 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<Scene /> renders correctly 1`] = `
4
- <ForwardRef(AnimatedComponentWrapper)
5
- pointerEvents="auto"
4
+ <CurrentScreenContextProvider
5
+ contentStyle={
6
+ {
7
+ "paddingBottom": 49,
8
+ }
9
+ }
10
+ overlayStyle={
11
+ {
12
+ "opacity": 1,
13
+ }
14
+ }
6
15
  style={
7
- [
8
- {
9
- "flex": 1,
10
- },
16
+ {
17
+ "flex": 1,
18
+ }
19
+ }
20
+ >
21
+ <SceneComponent
22
+ contentStyle={
11
23
  {
12
24
  "paddingBottom": 49,
13
- },
25
+ }
26
+ }
27
+ overlayStyle={
14
28
  {
15
- "fontScale": 2,
16
- "height": 1334,
17
- "scale": 2,
18
- "statusBarHeight": null,
19
- "width": 750,
20
- },
21
- ]
22
- }
23
- >
24
- <ContextProvider>
25
- <ContextProvider>
26
- <ScreenContextProvider />
27
- </ContextProvider>
28
- </ContextProvider>
29
- </ForwardRef(AnimatedComponentWrapper)>
29
+ "opacity": 1,
30
+ }
31
+ }
32
+ style={
33
+ {
34
+ "flex": 1,
35
+ }
36
+ }
37
+ />
38
+ </CurrentScreenContextProvider>
30
39
  `;
31
40
 
32
41
  exports[`<Scene /> renders the overlay while animating 1`] = `
33
- <ForwardRef(AnimatedComponentWrapper)
34
- pointerEvents="auto"
42
+ <CurrentScreenContextProvider
43
+ animating={true}
44
+ contentStyle={
45
+ {
46
+ "paddingBottom": 49,
47
+ }
48
+ }
49
+ overlayStyle={
50
+ {
51
+ "opacity": 1,
52
+ }
53
+ }
35
54
  style={
36
- [
37
- {
38
- "flex": 1,
39
- },
55
+ {
56
+ "flex": 1,
57
+ }
58
+ }
59
+ >
60
+ <SceneComponent
61
+ animating={true}
62
+ contentStyle={
40
63
  {
41
64
  "paddingBottom": 49,
42
- },
65
+ }
66
+ }
67
+ overlayStyle={
43
68
  {
44
- "height": "100%",
45
- "width": "100%",
46
- },
47
- ]
48
- }
49
- >
50
- <ContextProvider>
51
- <ContextProvider>
52
- <ScreenContextProvider />
53
- </ContextProvider>
54
- </ContextProvider>
55
- <ForwardRef(AnimatedComponentWrapper)
69
+ "opacity": 1,
70
+ }
71
+ }
56
72
  style={
57
73
  {
58
- "opacity": 1,
74
+ "flex": 1,
59
75
  }
60
76
  }
61
77
  />
62
- </ForwardRef(AnimatedComponentWrapper)>
78
+ </CurrentScreenContextProvider>
63
79
  `;
@@ -9,7 +9,7 @@ exports[`<TransitionerComponent /> renders correctly 1`] = `
9
9
  }
10
10
  }
11
11
  >
12
- <Scene
12
+ <SceneComponent
13
13
  isActive={true}
14
14
  screenUniqueId="mocked-uuid"
15
15
  style={{}}
@@ -23,6 +23,6 @@ exports[`<TransitionerComponent /> renders correctly 1`] = `
23
23
  children
24
24
  </Text>
25
25
  </View>
26
- </Scene>
26
+ </SceneComponent>
27
27
  </View>
28
28
  `;
@@ -1,5 +1,4 @@
1
1
  import * as React from "react";
2
- import { useWindowDimensions } from "react-native";
3
2
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
4
3
  import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/layout";
5
4
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
@@ -7,10 +6,15 @@ import { TransitionerComponent } from "./Transitioner";
7
6
 
8
7
  export const Transitioner = (props) => {
9
8
  const navigator = useNavigation();
10
- const { height, width } = useWindowDimensions();
11
9
 
12
- const dimensions = useDimensions("window", { deviceInfo: true });
13
- const isTablet = dimensions?.deviceInfo?.isTablet;
10
+ const {
11
+ width,
12
+ height,
13
+ deviceInfo: { isTablet },
14
+ } = useDimensions("window", {
15
+ updateForInactiveScreens: false,
16
+ deviceInfo: true,
17
+ });
14
18
 
15
19
  const { appData } = usePickFromState(["appData"]);
16
20
  const isTabletPortrait = appData?.isTabletPortrait;
@@ -8,6 +8,8 @@ import { loggerLiveImageManager } from "./loggerHelper";
8
8
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
9
9
  import { Component } from "react";
10
10
 
11
+ const TIMEOUT_FOR_DELAY_CHECK_PLAYER_POSITION = 500; // ms
12
+
11
13
  const { log_debug, log_info, log_error } = loggerLiveImageManager;
12
14
 
13
15
  export type LiveImageManagerEvent =
@@ -62,6 +64,9 @@ export class LiveImageManager implements PlayerLifecycleListener {
62
64
  protected currentlyPlaying: LiveImage | null = null;
63
65
  protected primaryPlayer: Player | null = null;
64
66
 
67
+ private checkPlayerPositionTimeout: ReturnType<typeof setTimeout> | null =
68
+ null;
69
+
65
70
  protected listeners: Record<
66
71
  string,
67
72
  Record<LiveImageManagerEvent, (event?: Record<string, any>) => void>
@@ -118,6 +123,11 @@ export class LiveImageManager implements PlayerLifecycleListener {
118
123
  });
119
124
  };
120
125
 
126
+ private cancelCheckPlayerPositionTimeout = () => {
127
+ clearTimeout(this.checkPlayerPositionTimeout);
128
+ this.checkPlayerPositionTimeout = null;
129
+ };
130
+
121
131
  public onViewportEnter = (item: LiveImage) => {
122
132
  log_debug(
123
133
  `onViewportEnter: live-image ${playerInfo(
@@ -125,7 +135,21 @@ export class LiveImageManager implements PlayerLifecycleListener {
125
135
  )}, primary ${playerInfo(this.primaryPlayer)}`
126
136
  );
127
137
 
128
- this.checkPlayerPosition(item);
138
+ if (!isTV()) {
139
+ // mobile only
140
+ // we have to delay running checkPlayerPosition, because sometimes on fast scrolling we get wrong order onEnter, then onLeave.
141
+ // which could cause select wrong item to play
142
+
143
+ this.cancelCheckPlayerPositionTimeout();
144
+
145
+ this.checkPlayerPositionTimeout = setTimeout(() => {
146
+ this.cancelCheckPlayerPositionTimeout();
147
+
148
+ this.checkPlayerPosition(item);
149
+ }, TIMEOUT_FOR_DELAY_CHECK_PLAYER_POSITION);
150
+ } else {
151
+ this.checkPlayerPosition(item);
152
+ }
129
153
  };
130
154
 
131
155
  public onViewportLeave = (item: LiveImage) => {
@@ -246,6 +270,8 @@ export class LiveImageManager implements PlayerLifecycleListener {
246
270
  };
247
271
 
248
272
  public checkPlayerPosition = (item: LiveImage) => {
273
+ this.cancelCheckPlayerPositionTimeout();
274
+
249
275
  log_debug(
250
276
  `checkPlayerPosition: live-image playerId: ${playerInfo(
251
277
  item.player
@@ -44,7 +44,10 @@ type Props = {
44
44
  };
45
45
 
46
46
  const PlayerLiveImageComponent = (props: Props) => {
47
- const dimensions = useDimensions("screen", { fullDimensions: true });
47
+ const dimensions = useDimensions("screen", {
48
+ fullDimensions: true,
49
+ updateForInactiveScreens: false,
50
+ });
48
51
 
49
52
  const { item, style, playerId } = props;
50
53
 
@@ -135,13 +138,16 @@ const PlayerLiveImageComponent = (props: Props) => {
135
138
  } else {
136
139
  throw new Error("Player factory item is null");
137
140
  }
138
- }, [playerId, item, playerPluginId, screenConfig]);
141
+ }, [playerId, item.id, playerPluginId, screenConfig]);
139
142
 
140
143
  React.useEffect(() => {
141
144
  liveImageItem.setMode = setModeDebounced;
142
145
  }, [setModeDebounced, liveImageItem]);
143
146
 
144
- const { start, end } = React.useMemo(() => getAutoplaySettings(item), [item]);
147
+ const { start, end } = React.useMemo(
148
+ () => getAutoplaySettings(item),
149
+ [item.id]
150
+ );
145
151
 
146
152
  const controller = liveImageItem.getPlayer();
147
153
  const player = usePlayer(playerId);
@@ -174,7 +180,7 @@ const PlayerLiveImageComponent = (props: Props) => {
174
180
 
175
181
  unregister();
176
182
  };
177
- }, [item, liveImageItem]);
183
+ }, [item.id, liveImageItem]);
178
184
 
179
185
  React.useEffect(() => {
180
186
  if (!player) {
@@ -304,7 +310,7 @@ const PlayerLiveImageComponent = (props: Props) => {
304
310
  LiveImageManager.instance.checkPlayerPosition(liveImageItem);
305
311
  }
306
312
  },
307
- [liveImageItem, dimensions]
313
+ [liveImageItem, dimensions.height, dimensions.width]
308
314
  );
309
315
 
310
316
  const platformSpecificProps = isApplePlatform()
@@ -31,11 +31,6 @@ exports[`PlayerLiveImageComponent should render correctly with default props 1`]
31
31
  playerId="player1"
32
32
  resizeMode="cover"
33
33
  screenConfig={{}}
34
- source={
35
- {
36
- "uri": "",
37
- }
38
- }
39
34
  style={{}}
40
35
  />
41
36
  </View>
@@ -100,10 +100,11 @@ export const AnimationView = ({
100
100
  const moveUpValue = additionalData.saveArea
101
101
  ? -insets.top + (isAudioItem ? 0 : progressBarHeight)
102
102
  : isTablet
103
- ? isTabletLandscape && (!isAudioItem || (isAudioItem && inlineAudioPlayer))
104
- ? -tabletLandscapePlayerTopPosition + progressBarHeight
105
- : -130
106
- : -50 + progressBarHeight;
103
+ ? isTabletLandscape &&
104
+ (!isAudioItem || (isAudioItem && inlineAudioPlayer))
105
+ ? -tabletLandscapePlayerTopPosition + progressBarHeight
106
+ : -130
107
+ : -50 + progressBarHeight;
107
108
 
108
109
  const moveComponentHorizontalValue = additionalData.moveValue
109
110
  ? isRTL
@@ -434,8 +435,8 @@ export const AnimationComponent = (props: Props) => {
434
435
  const Component = useAnimation
435
436
  ? AnimationView
436
437
  : style
437
- ? View
438
- : React.Fragment;
438
+ ? View
439
+ : React.Fragment;
439
440
 
440
441
  if (
441
442
  additionalData.extraAnimation &&
@@ -108,8 +108,8 @@ export const calculateAnimationValue = (animationType, dragPosition, data) => {
108
108
  : { forWidth: 3, forHeight: 3 }
109
109
  : { forWidth: 2, forHeight: 2 }
110
110
  : isTabletLandscape
111
- ? { forWidth: 8, forHeight: 6.5 }
112
- : { forWidth: 3.5, forHeight: 2 };
111
+ ? { forWidth: 8, forHeight: 6.5 }
112
+ : { forWidth: 3.5, forHeight: 2 };
113
113
 
114
114
  const minWidth =
115
115
  isAudioItem && !inlineAudioPlayer ? minimisedHeight : minimisedWidth;
@@ -99,8 +99,9 @@ export function ScreenContextProvider({
99
99
  const isNested = useNestedNavigationContext();
100
100
  const currentScreenScreenData = useCurrentScreenData();
101
101
 
102
- const screenNavBarStateRef =
103
- useRef<null | ReturnType<typeof createStore>>(null);
102
+ const screenNavBarStateRef = useRef<null | ReturnType<typeof createStore>>(
103
+ null
104
+ );
104
105
 
105
106
  const getScreenNavBarState = useCallback(() => {
106
107
  if (screenNavBarStateRef.current !== null) {
@@ -2,7 +2,7 @@ import React, { useEffect } from "react";
2
2
  import { View } from "react-native";
3
3
 
4
4
  type Props = {
5
- onLoadFinished: () => void,
5
+ onLoadFinished: () => void;
6
6
  };
7
7
 
8
8
  export function Hero(props: Props) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-ui-components",
3
- "version": "13.0.0-alpha.1045252438",
3
+ "version": "13.0.0-alpha.1124981321",
4
4
  "description": "Applicaster Zapp React Native ui components for the Quick Brick App",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -34,10 +34,10 @@
34
34
  "redux-mock-store": "^1.5.3"
35
35
  },
36
36
  "dependencies": {
37
- "@applicaster/applicaster-types": "13.0.0-alpha.1045252438",
38
- "@applicaster/zapp-react-native-bridge": "13.0.0-alpha.1045252438",
39
- "@applicaster/zapp-react-native-redux": "13.0.0-alpha.1045252438",
40
- "@applicaster/zapp-react-native-utils": "13.0.0-alpha.1045252438",
37
+ "@applicaster/applicaster-types": "13.0.0-alpha.1124981321",
38
+ "@applicaster/zapp-react-native-bridge": "13.0.0-alpha.1124981321",
39
+ "@applicaster/zapp-react-native-redux": "13.0.0-alpha.1124981321",
40
+ "@applicaster/zapp-react-native-utils": "13.0.0-alpha.1124981321",
41
41
  "promise": "^8.3.0",
42
42
  "react-router-native": "^5.1.2",
43
43
  "url": "^0.11.0",