@applicaster/zapp-react-native-ui-components 12.0.0-alpha.7539319320 → 12.0.0-alpha.7623722215

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 (91) 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/Cell/Cell.tsx +2 -2
  5. package/Components/Cell/CellWithFocusable.tsx +21 -14
  6. package/Components/Cell/TvOSCellComponent.tsx +2 -2
  7. package/Components/ErrorScreen/index.tsx +1 -1
  8. package/Components/FocusableCell/index.tsx +7 -7
  9. package/Components/FocusableList/FocusableChild.ts +24 -14
  10. package/Components/FocusableList/FocusableItem.tsx +1 -1
  11. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  12. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  13. package/Components/FocusableList/index.tsx +26 -25
  14. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
  15. package/Components/HandlePlayable/HandlePlayable.tsx +1 -1
  16. package/Components/HookRenderer/index.tsx +1 -6
  17. package/Components/Image/Image.android.tsx +3 -26
  18. package/Components/Image/Image.tsx +18 -2
  19. package/Components/Image/utils/index.ts +39 -0
  20. package/Components/Layout/TV/__tests__/index.test.tsx +1 -34
  21. package/Components/Layout/TV/index.tsx +20 -36
  22. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +80 -106
  23. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
  24. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  25. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  26. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
  27. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  28. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  29. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +8 -10
  30. package/Components/MasterCell/utils/index.ts +16 -5
  31. package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
  32. package/Components/ModalComponent/Button/Item.tsx +82 -0
  33. package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
  34. package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
  35. package/Components/ModalComponent/Button/assets.ts +4 -0
  36. package/Components/ModalComponent/Button/index.tsx +111 -0
  37. package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
  38. package/Components/ModalComponent/Header/Title.tsx +70 -0
  39. package/Components/ModalComponent/Header/assets.ts +4 -0
  40. package/Components/ModalComponent/Header/index.tsx +71 -0
  41. package/Components/ModalComponent/Header/utils.ts +79 -0
  42. package/Components/ModalComponent/index.tsx +38 -0
  43. package/Components/ModalComponent/utils.ts +126 -0
  44. package/Components/PlayerContainer/PlayerContainer.tsx +56 -29
  45. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  46. package/Components/PlayerImageBackground/index.tsx +34 -8
  47. package/Components/River/ComponentsMap/ComponentsMap.tsx +25 -0
  48. package/Components/River/River.tsx +8 -5
  49. package/Components/River/TV/River.tsx +2 -2
  50. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  51. package/Components/River/__tests__/componentsMap.test.js +7 -0
  52. package/Components/River/useScreenConfiguration.ts +2 -2
  53. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
  54. package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
  55. package/Components/RouteManager/index.tsx +5 -3
  56. package/Components/Screen/__tests__/Screen.test.tsx +88 -74
  57. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +20 -6
  58. package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
  59. package/Components/Screen/index.tsx +92 -44
  60. package/Components/Screen/navigationHandler.ts +0 -82
  61. package/Components/ScreenResolver/index.tsx +1 -1
  62. package/Components/Tabs/TV/styles.ts +1 -1
  63. package/Components/TrackedView/index.tsx +6 -3
  64. package/Components/VideoLive/LiveImageManager.ts +42 -13
  65. package/Components/VideoLive/PlayerLiveImageComponent.tsx +81 -72
  66. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  67. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +22 -27
  68. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  69. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +335 -0
  70. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +144 -0
  71. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +438 -0
  72. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +143 -0
  73. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  74. package/Components/VideoModal/ModalAnimation/utils.ts +299 -0
  75. package/Components/VideoModal/PlayerDetails.tsx +3 -0
  76. package/Components/VideoModal/PlayerWrapper.tsx +68 -43
  77. package/Components/VideoModal/VideoModal.tsx +8 -25
  78. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +0 -2
  79. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +5 -1
  80. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +246 -166
  81. package/Components/VideoModal/utils.ts +3 -3
  82. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
  83. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
  84. package/Components/ZappUIComponent/index.tsx +22 -12
  85. package/Components/index.js +2 -0
  86. package/Contexts/ScreenContext/index.tsx +1 -4
  87. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  88. package/Decorators/RiverResolver/index.tsx +0 -4
  89. package/Decorators/index.tsx +0 -4
  90. package/package.json +5 -6
  91. package/Components/VideoModal/hooks/usePosition.ts +0 -50
@@ -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
 
@@ -125,25 +113,4 @@ describe("Layout TV", () => {
125
113
  expect(bgComponent.props.testProp).toBe(true);
126
114
  });
127
115
  });
128
-
129
- describe("props passed to navbar component", () => {
130
- it("passes correct props", () => {
131
- const { getByTestId } = render(
132
- <Layout
133
- Components={Components}
134
- ComponentsExtraProps={{
135
- NavBar: { testProp: true },
136
- }}
137
- >
138
- <Screen />
139
- </Layout>,
140
- { wrapper }
141
- );
142
-
143
- const navbarComponent = getByTestId("nav-bar-component");
144
- expect(navbarComponent.props.isVisible).toBe(isNavBarVisible);
145
- expect(navbarComponent.props.menuPlugin).toBe(plugins[0]);
146
- expect(navbarComponent.props.testProp).toBe(true);
147
- });
148
- });
149
116
  });
@@ -1,10 +1,7 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
3
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
4
- import {
5
- useIsNavBarVisible,
6
- useNavigation,
7
- } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
4
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
8
5
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
9
6
 
10
7
  import {
@@ -43,7 +40,7 @@ type Props = {
43
40
  const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
44
41
  const navigator = useNavigation();
45
42
  const theme = useTheme();
46
- const { title } = useNavbarState();
43
+ const { title, visible } = useNavbarState();
47
44
  const { activeRiver, currentRoute } = navigator;
48
45
 
49
46
  const {
@@ -80,10 +77,22 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
80
77
  []
81
78
  );
82
79
 
83
- const navBarVisible = useIsNavBarVisible();
84
-
85
80
  const [navBarReady, setNavBarReady] = React.useState(false);
86
81
 
82
+ const navBarContainer = (
83
+ <NavBarContainer isVisible={visible} onReady={setNavBarReady}>
84
+ <NavBar
85
+ ref={ComponentsExtraProps?.NavBar?.ref}
86
+ isVisible={visible && !routeIsPlayerScreen(currentRoute)}
87
+ menuPlugin={menuPlugin}
88
+ navigationProps={navigationProps}
89
+ {...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
90
+ />
91
+ </NavBarContainer>
92
+ );
93
+
94
+ const isTVOS = isApplePlatform();
95
+
87
96
  if (!appReady) {
88
97
  return null;
89
98
  }
@@ -96,18 +105,7 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
96
105
  backgroundImageUrl={backgroundImageUrl}
97
106
  {...R.omit(["ref"])(ComponentsExtraProps?.Background)}
98
107
  >
99
- {!isApplePlatform() ? (
100
- <NavBarContainer isVisible={navBarVisible} onReady={setNavBarReady}>
101
- <NavBar
102
- ref={ComponentsExtraProps?.NavBar?.ref}
103
- isVisible={navBarVisible && !routeIsPlayerScreen(currentRoute)}
104
- menuPlugin={menuPlugin}
105
- navigationProps={navigationProps}
106
- {...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
107
- />
108
- </NavBarContainer>
109
- ) : null}
110
-
108
+ {!isTVOS ? navBarContainer : null}
111
109
  {navBarReady ? (
112
110
  /**
113
111
  Need to re-mount this component on each navigation event
@@ -118,23 +116,9 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
118
116
  <ScreenContainer>{children}</ScreenContainer>
119
117
  </PathnameContext.Provider>
120
118
  ) : null}
121
-
122
- {isApplePlatform() ? (
123
- /**
124
- Render NavBarContainer over ScreenContainer to let tvOS native
125
- focus manager works correctly to focus nav bar items first
126
- **/
127
- <NavBarContainer isVisible={navBarVisible} onReady={setNavBarReady}>
128
- <NavBar
129
- ref={ComponentsExtraProps?.NavBar?.ref}
130
- isVisible={navBarVisible && !routeIsPlayerScreen(currentRoute)}
131
- menuPlugin={menuPlugin}
132
- navigationProps={navigationProps}
133
- {...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
134
- />
135
- </NavBarContainer>
136
- ) : null}
137
-
119
+ {/** Render NavBarContainer over ScreenContainer to let tvOS native
120
+ focus manager works correctly to focus nav bar items first **/}
121
+ {isTVOS ? navBarContainer : null}
138
122
  <TransitionOverlay />
139
123
  </Components.Background>
140
124
  </ScreenLayoutContextProvider>
@@ -7,50 +7,24 @@ type BorderPosition = "inside" | "outside" | "center";
7
7
  interface Props {
8
8
  style: ImageStyle | ViewStyle;
9
9
  borderPosition: BorderPosition;
10
+ borderPaddingTop: number;
11
+ borderPaddingRight: number;
12
+ borderPaddingBottom: number;
13
+ borderPaddingLeft: number;
10
14
  children: React.ReactNode;
11
15
  }
12
16
 
13
- const MARGINS = [
14
- "marginTop",
15
- "marginBottom",
16
- "marginLeft",
17
- "marginRight",
18
- ] as const;
17
+ const COLOR_TRANSPARENT = "transparent";
19
18
 
20
- const PADDINGS = [
21
- "paddingTop",
22
- "paddingBottom",
23
- "paddingLeft",
24
- "paddingRight",
25
- ] as const;
19
+ const styles = StyleSheet.create({
20
+ container: {
21
+ overflow: "visible",
22
+ borderWidth: 0,
23
+ borderColor: COLOR_TRANSPARENT,
24
+ },
25
+ });
26
26
 
27
- const isPercentageValue = (value: number | string) => {
28
- if (typeof value === "string" && value.endsWith("%")) {
29
- return true;
30
- }
31
-
32
- return false;
33
- };
34
-
35
- const calculateContainerSpacing = ({
36
- spacingKey,
37
- style,
38
- additionalSpacing,
39
- }: {
40
- spacingKey: typeof MARGINS[number] | typeof PADDINGS[number];
41
- style: ImageStyle | ViewStyle;
42
- additionalSpacing: number;
43
- }) => {
44
- return {
45
- [spacingKey]:
46
- toNumberWithDefaultZero(style[spacingKey]) + additionalSpacing,
47
- };
48
- };
49
-
50
- const getContainerBorderWidth = (
51
- borderPosition: BorderPosition,
52
- borderWidth
53
- ) => {
27
+ const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
54
28
  switch (borderPosition) {
55
29
  case "inside":
56
30
  return 0;
@@ -63,103 +37,103 @@ const getContainerBorderWidth = (
63
37
  }
64
38
  };
65
39
 
66
- const getContainerPadding = (borderPosition: BorderPosition, borderWidth) => {
67
- switch (borderPosition) {
68
- case "inside":
69
- return borderWidth;
70
- case "center":
71
- return borderWidth / 2;
72
- case "outside":
73
- return 0;
74
- default:
75
- return 0;
76
- }
77
- };
78
-
79
40
  const InsideBorder = ({
80
41
  style,
42
+ padding,
81
43
  borderPosition,
82
44
  }: {
83
45
  style: ViewStyle;
46
+ padding: ViewStyle;
84
47
  borderPosition: BorderPosition;
85
48
  }) => {
86
- if (borderPosition === "outside") {
49
+ if (!borderPosition) {
87
50
  return null;
88
51
  }
89
52
 
90
- const insideBorderWidth =
91
- borderPosition === "center" ? style.borderWidth / 2 : style.borderWidth;
53
+ const borderWidth = toNumberWithDefaultZero(style.borderWidth);
54
+ const containerPadding = getBorderPadding(borderPosition, borderWidth);
55
+
56
+ const initialBorderRadius = toNumberWithDefaultZero(style.borderRadius);
57
+ const paddingTop = toNumberWithDefaultZero(padding.paddingTop);
58
+ const paddingRight = toNumberWithDefaultZero(padding.paddingRight);
59
+ const paddingBottom = toNumberWithDefaultZero(padding.paddingBottom);
60
+ const paddingLeft = toNumberWithDefaultZero(padding.paddingLeft);
61
+
62
+ const borderRadiusStyle = initialBorderRadius
63
+ ? {
64
+ borderTopLeftRadius:
65
+ initialBorderRadius +
66
+ containerPadding +
67
+ Math.min(paddingTop, paddingLeft),
68
+ borderTopRightRadius:
69
+ initialBorderRadius +
70
+ containerPadding +
71
+ Math.min(paddingTop, paddingRight),
72
+ borderBottomLeftRadius:
73
+ initialBorderRadius +
74
+ containerPadding +
75
+ Math.min(paddingBottom, paddingLeft),
76
+ borderBottomRightRadius:
77
+ initialBorderRadius +
78
+ containerPadding +
79
+ Math.min(paddingBottom, paddingRight),
80
+ }
81
+ : {
82
+ borderTopLeftRadius: 0,
83
+ borderTopRightRadius: 0,
84
+ borderBottomLeftRadius: 0,
85
+ borderBottomRightRadius: 0,
86
+ };
92
87
 
93
88
  return (
94
89
  <View
95
90
  style={{
96
91
  ...StyleSheet.absoluteFillObject,
97
- borderWidth: insideBorderWidth,
92
+ right: -containerPadding - paddingRight,
93
+ bottom: -containerPadding - paddingBottom,
94
+ left: -containerPadding - paddingLeft,
95
+ top: -containerPadding - paddingTop,
96
+ borderWidth,
98
97
  borderColor: style.borderColor,
99
- borderRadius: style.borderRadius,
98
+ ...borderRadiusStyle,
100
99
  }}
101
100
  />
102
101
  );
103
102
  };
104
103
 
105
104
  export const BorderContainerView = (props: Props) => {
106
- const { style, children } = props;
107
- // Return border position prop after re-implementing
108
- const borderPosition = "outside";
109
-
110
- const borderWidth = toNumberWithDefaultZero(style.borderWidth);
111
- const spacing = getContainerPadding(borderPosition, borderWidth);
105
+ const {
106
+ borderPosition,
107
+ borderPaddingTop,
108
+ borderPaddingRight,
109
+ borderPaddingBottom,
110
+ borderPaddingLeft,
111
+ style,
112
+ children,
113
+ } = props;
112
114
 
113
115
  const borderStyleForMissingBorder =
114
116
  style?.borderWidth === 0 || !style?.borderColor ? { borderRadius: 0 } : {};
115
117
 
116
- const updatedMargins = MARGINS.reduce((acc, margin) => {
117
- const marginValue = calculateContainerSpacing({
118
- spacingKey: margin,
119
- style,
120
- additionalSpacing: spacing,
121
- });
122
-
123
- return { ...acc, ...marginValue };
124
- }, {});
125
-
126
- const updatedPaddings = PADDINGS.reduce((acc, padding) => {
127
- const paddingValue = calculateContainerSpacing({
128
- spacingKey: padding,
129
- style,
130
- additionalSpacing: spacing,
131
- });
132
-
133
- return { ...acc, ...paddingValue };
134
- }, {});
135
-
136
- const updatedWidth = style.width
137
- ? isPercentageValue(style.width)
138
- ? style.width
139
- : toNumberWithDefaultZero(style.width) - spacing * 2
140
- : undefined;
141
-
142
- const updatedHeight = style.height
143
- ? isPercentageValue(style.height)
144
- ? style.height
145
- : toNumberWithDefaultZero(style.height) - spacing * 2
146
- : undefined;
147
-
148
- const containerStyle = {
149
- borderWidth: getContainerBorderWidth(borderPosition, borderWidth),
150
- padding: spacing,
151
- ...updatedMargins,
152
- ...updatedPaddings,
153
- width: updatedWidth,
154
- height: updatedHeight,
155
- ...borderStyleForMissingBorder,
156
- };
118
+ const padding =
119
+ borderPosition === "outside"
120
+ ? {
121
+ paddingTop: borderPaddingTop,
122
+ paddingRight: borderPaddingRight,
123
+ paddingBottom: borderPaddingBottom,
124
+ paddingLeft: borderPaddingLeft,
125
+ }
126
+ : {};
157
127
 
158
128
  return (
159
- <View {...props} style={[style, containerStyle]}>
129
+ <View
130
+ {...props}
131
+ style={[style, styles.container, borderStyleForMissingBorder]}
132
+ >
160
133
  {children}
161
134
  <InsideBorder
162
- style={{ ...style, ...borderStyleForMissingBorder }}
135
+ style={style}
136
+ padding={padding}
163
137
  borderPosition={borderPosition}
164
138
  />
165
139
  </View>
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  compact,
3
3
  ifElseFocusedSelected,
4
+ ifElseSelected,
4
5
  } from "@applicaster/zapp-react-native-utils/cellUtils";
5
6
 
6
7
  import { getPositionStyles } from "./utils";
@@ -12,9 +13,15 @@ type Props = {
12
13
  value: Function;
13
14
  state: string;
14
15
  visible?: Boolean;
16
+ isMobile?: Boolean;
15
17
  };
16
18
 
17
- export const CellBadge = ({ value, state, visible = true }: Props) => {
19
+ export const CellBadge = ({
20
+ value,
21
+ state,
22
+ visible = true,
23
+ isMobile = false,
24
+ }: Props) => {
18
25
  if (!value("cell_badge_switch") || !visible) {
19
26
  return null;
20
27
  }
@@ -45,13 +52,19 @@ export const CellBadge = ({ value, state, visible = true }: Props) => {
45
52
  data: [
46
53
  {
47
54
  func: () => {
48
- return ifElseFocusedSelected(
49
- state,
50
- value("cell_badge_focused"),
51
- value("cell_badge_selected"),
52
- value("cell_badge_focused_selected"),
53
- value("cell_badge_default")
54
- );
55
+ return isMobile
56
+ ? ifElseSelected(
57
+ state,
58
+ value("cell_badge_selected"),
59
+ value("cell_badge_default")
60
+ )
61
+ : ifElseFocusedSelected(
62
+ state,
63
+ value("cell_badge_focused"),
64
+ value("cell_badge_selected"),
65
+ value("cell_badge_focused_selected"),
66
+ value("cell_badge_default")
67
+ );
55
68
  },
56
69
  args: [],
57
70
  propName: "uri",
@@ -1,11 +1,11 @@
1
1
  import * as React from "react";
2
2
  import { ImageStyle, View } from "react-native";
3
3
  import * as R from "ramda";
4
- import ImageComponent from "@applicaster/zapp-react-native-ui-components/Components/FastImage";
4
+ import { QBImage } from "@applicaster/zapp-react-native-ui-components/Components/Image";
5
5
  import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
6
- import { toFiniteNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
7
6
 
8
7
  import { useImageSourceWithDefault, useCheckFailedUri } from "./hooks";
8
+ import useImageSize from "./hooks/useImageSize";
9
9
 
10
10
  type Source = {
11
11
  uri: string;
@@ -19,32 +19,13 @@ type Props = Record<string, any> & {
19
19
  withDimensions: (source: Source) => Source;
20
20
  };
21
21
 
22
- const MemoizedImage = React.memo(ImageComponent, R.equals);
23
-
24
- const normalize = Math.ceil;
22
+ const MemoizedImage = React.memo(QBImage, R.equals);
25
23
 
26
24
  const isValidExternalSource = (source): boolean =>
27
25
  source?.uri?.startsWith("http") ||
28
26
  source?.uri?.startsWith("data:image") ||
29
27
  source?.uri?.startsWith("file://");
30
28
 
31
- const isValidSize = (size: unknown): boolean =>
32
- toFiniteNumberWithDefault(0, size) > 0;
33
-
34
- const getSourceWithMaxSizes = (applyMaxSize, style, source) => {
35
- const { width, height } = style;
36
-
37
- if (!applyMaxSize || !isValidSize(width) || !isValidSize(height)) {
38
- return source;
39
- }
40
-
41
- return {
42
- ...source,
43
- maxWidth: normalize(width),
44
- maxHeight: normalize(height),
45
- };
46
- };
47
-
48
29
  export default function Image({
49
30
  style,
50
31
  uri,
@@ -82,10 +63,12 @@ export default function Image({
82
63
 
83
64
  const applyMaxSize = toBooleanWithDefaultFalse(otherProps?.applyMaxSize);
84
65
 
85
- const sourceWithMaxSizes = React.useMemo(
86
- () => withDimensions(getSourceWithMaxSizes(applyMaxSize, style, source)),
87
- [applyMaxSize, style?.height, style?.width, source?.uri, withDimensions]
88
- );
66
+ const sourceWithMaxSizes = useImageSize({
67
+ style,
68
+ applyMaxSize,
69
+ source,
70
+ withDimensions,
71
+ });
89
72
 
90
73
  if (isFailedToLoadUri(uri) || !isValidExternalSource(source)) {
91
74
  return <View style={style} />;
@@ -0,0 +1,65 @@
1
+ import * as React from "react";
2
+ import { ImageStyle } from "react-native";
3
+ import { toFiniteNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
4
+
5
+ type Source = {
6
+ uri: string;
7
+ };
8
+
9
+ type Props = Record<string, any> & {
10
+ style: ImageStyle;
11
+ uri?: string;
12
+ placeholderImage?: string;
13
+ entry?: ZappEntry | ZappFeed;
14
+ withDimensions: (source: Source) => Source;
15
+ };
16
+
17
+ const normalize = Math.ceil;
18
+
19
+ const isValidSize = (size: unknown): boolean =>
20
+ toFiniteNumberWithDefault(0, size) > 0;
21
+
22
+ type WidthDimensions = { aspectRatio?: number; width: number; height?: number };
23
+ type HeightDimensions = {
24
+ aspectRatio?: number;
25
+ width?: number;
26
+ height: number;
27
+ };
28
+ type FullDimensions = { aspectRatio?: number; width: number; height: number };
29
+ type Dimensions = WidthDimensions | HeightDimensions | FullDimensions;
30
+
31
+ const calculateDimensions = ({ aspectRatio, width, height }: Dimensions) => {
32
+ if (aspectRatio) {
33
+ return {
34
+ width: width || height * aspectRatio,
35
+ height: height || width / aspectRatio,
36
+ };
37
+ }
38
+
39
+ return { width, height };
40
+ };
41
+
42
+ const getSourceWithMaxSizes = (applyMaxSize, style, source) => {
43
+ const { width, height } = calculateDimensions(style);
44
+
45
+ if (!applyMaxSize || !isValidSize(width) || !isValidSize(height)) {
46
+ return source;
47
+ }
48
+
49
+ return {
50
+ ...source,
51
+ maxWidth: normalize(width),
52
+ maxHeight: normalize(height),
53
+ };
54
+ };
55
+
56
+ export default function useImageSize(props: Props) {
57
+ const { style, applyMaxSize, source, withDimensions } = props;
58
+
59
+ const sourceWithMaxSizes = React.useMemo(
60
+ () => withDimensions(getSourceWithMaxSizes(applyMaxSize, style, source)),
61
+ [applyMaxSize, style?.height, style?.width, source?.uri, withDimensions]
62
+ );
63
+
64
+ return sourceWithMaxSizes;
65
+ }
@@ -18,6 +18,7 @@ type LiveImageProps = {
18
18
  imageKey?: string;
19
19
  state: "focused" | "unfocused" | "selected";
20
20
  audio_muted_by_default?: boolean;
21
+ uri: string; // cover image url
21
22
  };
22
23
 
23
24
  const DEFAULT_PLAYER_IDENTIFIER = "QuickBrickPlayerPlugin";
@@ -176,10 +177,23 @@ const getPlayerConfig = (player_screen_id, actionIdentifier) => {
176
177
  const positionAbsolute: ViewStyle = { position: "absolute" };
177
178
 
178
179
  const LiveImageComponent = (props: LiveImageProps) => {
179
- 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;
180
188
 
181
189
  const component = useUIComponentContext();
182
190
 
191
+ // Fix for blinking on state change
192
+ const style = React.useMemo(
193
+ () => ({ ...componentStyle, backgroundColor: "transparent" }),
194
+ [componentStyle]
195
+ );
196
+
183
197
  const componentType = React.useMemo(
184
198
  () => getPropComponentType(component),
185
199
  [component?.id]
@@ -209,10 +223,11 @@ const LiveImageComponent = (props: LiveImageProps) => {
209
223
 
210
224
  return (
211
225
  <>
212
- <Image {...props} />
226
+ <Image {...props} applyMaxSize />
213
227
  {isShouldRender && (
214
228
  <View style={[style, positionAbsolute]}>
215
229
  <PlayerLiveImage
230
+ uri={props.uri}
216
231
  playerId={cellUUID}
217
232
  playerPluginId={playerPluginId}
218
233
  screenConfig={screenConfig}
@@ -1,11 +1,12 @@
1
1
  import * as React from "react";
2
2
  import { isNil } from "ramda";
3
- import { Image as RNImage, ImageStyle, View } from "react-native";
3
+ import { ImageStyle, View } from "react-native";
4
4
  import {
5
5
  FIT_POSITION,
6
6
  IMAGE_SIZING_FIT,
7
7
  IMAGE_SIZING_FILL,
8
8
  } from "@applicaster/zapp-react-native-utils/manifestUtils/secondaryImage";
9
+ import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
9
10
 
10
11
  import { useGetImageDimensions } from "./hooks";
11
12
  import {
@@ -59,7 +60,7 @@ const SecondaryImageComponent = (props: Props) => {
59
60
 
60
61
  return (
61
62
  <View style={style} onLayout={onAsyncRender}>
62
- <RNImage
63
+ <Image
63
64
  {...props}
64
65
  source={{ uri }}
65
66
  style={{
@@ -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(
@@ -1,6 +1,7 @@
1
- import { path, pathOr } from "ramda";
1
+ import { path } from "ramda";
2
2
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
3
3
  import {
4
+ getColorFromData,
4
5
  getKeyForLabel,
5
6
  mapSelfAlignment,
6
7
  } from "@applicaster/zapp-react-native-utils/cellUtils";
@@ -48,15 +49,12 @@ export const CollapsibleTextContainer = ({
48
49
  ]),
49
50
  {
50
51
  func: (data) => {
51
- const value = getValue("background_color");
52
-
53
- // check if value is a reference to an extension
54
- if (value.startsWith("extensions")) {
55
- return pathOr(value, value.split("."), data);
56
- } else {
57
- // return default value
58
- return additionalStyles.backgroundColor;
59
- }
52
+ return getColorFromData({
53
+ data,
54
+ getValue,
55
+ key: "background_color",
56
+ defaultColor: additionalStyles.backgroundColor,
57
+ });
60
58
  },
61
59
  propName: "backgroundColor",
62
60
  },