@applicaster/zapp-react-native-ui-components 12.0.0-rc.13 → 12.0.0-rc.130

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 (171) hide show
  1. package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
  2. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
  3. package/Components/AudioPlayer/helpers.tsx +1 -0
  4. package/Components/BaseFocusable/index.tsx +3 -3
  5. package/Components/Cell/Cell.tsx +32 -11
  6. package/Components/Cell/CellWithFocusable.tsx +40 -19
  7. package/Components/Cell/TvOSCellComponent.tsx +5 -9
  8. package/Components/ErrorScreen/index.tsx +1 -1
  9. package/Components/Focusable/Focusable.tsx +6 -1
  10. package/Components/Focusable/__tests__/index.android.test.tsx +72 -0
  11. package/Components/Focusable/index.android.tsx +15 -9
  12. package/Components/FocusableCell/index.tsx +7 -7
  13. package/Components/FocusableGroup/index.tsx +11 -2
  14. package/Components/FocusableList/FocusableChild.ts +26 -18
  15. package/Components/FocusableList/FocusableItem.tsx +1 -1
  16. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  17. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  18. package/Components/FocusableList/index.tsx +37 -37
  19. package/Components/FocusableScrollView/index.tsx +1 -1
  20. package/Components/FreezeWithCallback/Freeze.tsx +31 -0
  21. package/Components/FreezeWithCallback/LICENSE +21 -0
  22. package/Components/FreezeWithCallback/Suspender.tsx +17 -0
  23. package/Components/FreezeWithCallback/__tests__/index.test.tsx +170 -0
  24. package/Components/FreezeWithCallback/index.tsx +3 -0
  25. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
  26. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  27. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  28. package/Components/HandlePlayable/HandlePlayable.tsx +4 -1
  29. package/Components/HookRenderer/index.tsx +1 -6
  30. package/Components/Image/Image.android.tsx +3 -25
  31. package/Components/Image/Image.tsx +18 -2
  32. package/Components/Image/utils/index.ts +39 -0
  33. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  34. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  35. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  36. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  37. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  38. package/Components/Layout/TV/__tests__/index.test.tsx +20 -108
  39. package/Components/Layout/TV/index.tsx +15 -84
  40. package/Components/Layout/TV/index.web.tsx +33 -0
  41. package/Components/Layout/index.tsx +15 -11
  42. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
  43. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +86 -117
  44. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
  45. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  46. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +16 -1
  47. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  48. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +3 -0
  49. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +2 -2
  50. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  51. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +71 -0
  52. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  53. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +37 -2
  54. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  55. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  56. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  57. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  58. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  59. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  60. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  61. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +6 -10
  62. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +7 -1
  63. package/Components/MasterCell/dataAdapter.ts +21 -7
  64. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  65. package/Components/MasterCell/index.tsx +5 -3
  66. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  67. package/Components/MasterCell/utils/index.ts +33 -35
  68. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  69. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  70. package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
  71. package/Components/ModalComponent/Button/Item.tsx +82 -0
  72. package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
  73. package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
  74. package/Components/ModalComponent/Button/assets.ts +4 -0
  75. package/Components/ModalComponent/Button/index.tsx +111 -0
  76. package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
  77. package/Components/ModalComponent/Header/Title.tsx +70 -0
  78. package/Components/ModalComponent/Header/assets.ts +4 -0
  79. package/Components/ModalComponent/Header/index.tsx +71 -0
  80. package/Components/ModalComponent/Header/utils.ts +79 -0
  81. package/Components/ModalComponent/index.tsx +38 -0
  82. package/Components/ModalComponent/utils.ts +126 -0
  83. package/Components/Navigator/StackNavigator.tsx +57 -0
  84. package/Components/Navigator/index.ts +1 -0
  85. package/Components/PlayerContainer/PlayerContainer.tsx +73 -34
  86. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  87. package/Components/PlayerImageBackground/index.tsx +34 -8
  88. package/Components/River/ComponentsMap/ComponentsMap.tsx +97 -2
  89. package/Components/River/RefreshControl.tsx +55 -1
  90. package/Components/River/TV/River.tsx +2 -25
  91. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  92. package/Components/River/__tests__/componentsMap.test.js +7 -0
  93. package/Components/River/useScreenConfiguration.ts +2 -2
  94. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
  95. package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
  96. package/Components/RouteManager/index.tsx +4 -2
  97. package/Components/Screen/TV/hooks/index.ts +1 -0
  98. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  99. package/Components/Screen/TV/index.web.tsx +178 -0
  100. package/Components/Screen/__tests__/Screen.test.tsx +43 -75
  101. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +24 -6
  102. package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
  103. package/Components/Screen/index.tsx +104 -65
  104. package/Components/Screen/navigationHandler.ts +6 -85
  105. package/Components/Screen/orientationHandler.ts +18 -7
  106. package/Components/Screen/utils.ts +16 -0
  107. package/Components/ScreenResolver/index.tsx +10 -13
  108. package/Components/Tabs/TV/styles.ts +1 -1
  109. package/Components/TrackedView/index.tsx +6 -3
  110. package/Components/Transitioner/Scene.tsx +39 -26
  111. package/Components/Transitioner/Transitioner.tsx +56 -48
  112. package/Components/Transitioner/__tests__/Scene.test.js +7 -0
  113. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +12 -4
  114. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +2 -0
  115. package/Components/VideoLive/LiveImageManager.ts +70 -14
  116. package/Components/VideoLive/PlayerLiveImageComponent.tsx +132 -95
  117. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  118. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +24 -27
  119. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +66 -0
  120. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +377 -0
  121. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +165 -0
  122. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +481 -0
  123. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +182 -0
  124. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  125. package/Components/VideoModal/ModalAnimation/utils.ts +302 -0
  126. package/Components/VideoModal/PlayerDetails.tsx +21 -4
  127. package/Components/VideoModal/PlayerWrapper.tsx +115 -55
  128. package/Components/VideoModal/VideoModal.tsx +32 -43
  129. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +6 -1
  130. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +12 -1
  131. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -0
  132. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +358 -166
  133. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  134. package/Components/VideoModal/utils.ts +8 -5
  135. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
  136. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  137. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
  138. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  139. package/Components/Viewport/utils.js +1 -1
  140. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +7 -3
  141. package/Components/ZappFrameworkComponents/BarView/Buttons/BackButton.tsx +7 -2
  142. package/Components/ZappFrameworkComponents/BarView/Buttons/CloseButton.tsx +7 -2
  143. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +87 -0
  144. package/Components/ZappFrameworkComponents/Buttons/ActionButton.tsx +9 -4
  145. package/Components/ZappUIComponent/index.tsx +22 -12
  146. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  147. package/Components/index.js +2 -0
  148. package/Contexts/CachedDimensionsContext/index.ts +13 -9
  149. package/Contexts/ConfirmDialogState/index.ts +1 -1
  150. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  151. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  152. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  153. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  154. package/Contexts/FocusableGroupContext/index.ts +7 -0
  155. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  156. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  157. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  158. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  159. package/Contexts/ScreenContext/index.tsx +101 -30
  160. package/Contexts/ScreenDataContext/index.tsx +20 -0
  161. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  162. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  163. package/const.ts +6 -0
  164. package/events/index.ts +1 -0
  165. package/index.js +3 -0
  166. package/package.json +5 -5
  167. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  168. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  169. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  170. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  171. /package/Contexts/{index.js → index.ts} +0 -0
@@ -31,10 +31,10 @@ function Image({
31
31
 
32
32
  return (
33
33
  <RnImage
34
- defaultSource={placeholderImage || ""}
34
+ defaultSource={placeholderImage || null}
35
35
  style={style}
36
36
  source={updatedSource}
37
- {...R.omit(["source"], otherProps)}
37
+ {...R.omit(["source", "placeholderImage"], otherProps)}
38
38
  />
39
39
  );
40
40
  }
@@ -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
+ }
@@ -0,0 +1,71 @@
1
+ import { ImageStyle } from "react-native";
2
+
3
+ import {
4
+ getImageContainerMarginStyles,
5
+ ifElseFocusedSelected,
6
+ } from "@applicaster/zapp-react-native-utils/cellUtils";
7
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
8
+
9
+ export const ImageBorderContainer = ({
10
+ value,
11
+ state,
12
+ imageStyles,
13
+ }: {
14
+ value: (key: string) => string;
15
+ state: CellState;
16
+ imageStyles: ImageStyle;
17
+ }) => {
18
+ const imageMargins = getImageContainerMarginStyles({ value });
19
+
20
+ return {
21
+ type: "BorderContainerView",
22
+ style: {
23
+ position: "absolute",
24
+ // Image border now separated from the image, so cell padding affects the border position
25
+ marginTop:
26
+ toNumberWithDefaultZero(imageMargins.marginTop) +
27
+ toNumberWithDefaultZero(value("cell_padding_top")),
28
+ marginLeft:
29
+ toNumberWithDefaultZero(imageMargins.marginLeft) +
30
+ toNumberWithDefaultZero(value("cell_padding_left")),
31
+ marginRight:
32
+ toNumberWithDefaultZero(imageMargins.marginRight) +
33
+ toNumberWithDefaultZero(value("cell_padding_right")),
34
+ marginBottom:
35
+ toNumberWithDefaultZero(imageMargins.marginBottom) +
36
+ toNumberWithDefaultZero(value("cell_padding_bottom")),
37
+ zIndex: 1,
38
+ borderWidth: value("image_border_size"),
39
+ borderColor: ifElseFocusedSelected(
40
+ state,
41
+ value("image_focused_border_color"),
42
+ value("image_selected_border_color"),
43
+ value("image_focused_selected_border_color"),
44
+ value("image_border_color")
45
+ ),
46
+ borderRadius: toNumberWithDefaultZero(value("image_corner_radius")),
47
+ },
48
+ additionalProps: {
49
+ pointerEvents: "none",
50
+ borderPosition: value("image_border_position"),
51
+ borderPaddingTop: toNumberWithDefaultZero(
52
+ value("image_border_padding_top")
53
+ ),
54
+ borderPaddingRight: toNumberWithDefaultZero(
55
+ value("image_border_padding_right")
56
+ ),
57
+ borderPaddingBottom: toNumberWithDefaultZero(
58
+ value("image_border_padding_bottom")
59
+ ),
60
+ borderPaddingLeft: toNumberWithDefaultZero(
61
+ value("image_border_padding_left")
62
+ ),
63
+ },
64
+ elements: [
65
+ {
66
+ type: "View",
67
+ style: imageStyles,
68
+ },
69
+ ],
70
+ };
71
+ };
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { isVideoPreviewEnabled } from "@applicaster/zapp-react-native-ui-components/Components/MasterCell/utils";
3
3
  import { LiveImage } from "../LiveImage";
4
4
  import PureImage from "../Image";
5
+ import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks";
5
6
 
6
7
  type Props = {
7
8
  enable_video_preview: boolean;
@@ -9,7 +10,10 @@ type Props = {
9
10
  };
10
11
 
11
12
  export const ImageContainer = (props: Props) => {
12
- const Component = isVideoPreviewEnabled(props) ? LiveImage : PureImage;
13
+ const isActive = useIsScreenActive();
14
+
15
+ const Component =
16
+ isVideoPreviewEnabled(props) && isActive ? LiveImage : PureImage;
13
17
 
14
18
  return <Component {...props} />;
15
19
  };
@@ -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";
@@ -107,6 +108,26 @@ const prepareEntry = (entry) => {
107
108
  };
108
109
  }
109
110
 
111
+ const previewPlayback =
112
+ entry.extensions?.["brightcove"]?.["preview_playback"];
113
+
114
+ if (previewPlayback) {
115
+ return {
116
+ ...entry,
117
+ extensions: {
118
+ ...entry.extensions,
119
+ brightcove: {
120
+ ...entry?.extensions?.["brightcove"],
121
+ video_id: previewPlayback,
122
+ },
123
+ },
124
+ };
125
+ }
126
+
127
+ if (entry.extensions?.["brightcove"]?.["video_id"]) {
128
+ return entry;
129
+ }
130
+
110
131
  const src = entry.content?.src;
111
132
 
112
133
  if (!src) {
@@ -176,10 +197,23 @@ const getPlayerConfig = (player_screen_id, actionIdentifier) => {
176
197
  const positionAbsolute: ViewStyle = { position: "absolute" };
177
198
 
178
199
  const LiveImageComponent = (props: LiveImageProps) => {
179
- const { item, style, cellUUID, imageKey, actionIdentifier, state } = props;
200
+ const {
201
+ item,
202
+ style: componentStyle,
203
+ cellUUID,
204
+ imageKey,
205
+ actionIdentifier,
206
+ state,
207
+ } = props;
180
208
 
181
209
  const component = useUIComponentContext();
182
210
 
211
+ // Fix for blinking on state change
212
+ const style = React.useMemo(
213
+ () => ({ ...componentStyle, backgroundColor: "transparent" }),
214
+ [componentStyle]
215
+ );
216
+
183
217
  const componentType = React.useMemo(
184
218
  () => getPropComponentType(component),
185
219
  [component?.id]
@@ -209,10 +243,11 @@ const LiveImageComponent = (props: LiveImageProps) => {
209
243
 
210
244
  return (
211
245
  <>
212
- <Image {...props} />
246
+ <Image {...props} applyMaxSize />
213
247
  {isShouldRender && (
214
248
  <View style={[style, positionAbsolute]}>
215
249
  <PlayerLiveImage
250
+ uri={props.uri}
216
251
  playerId={cellUUID}
217
252
  playerPluginId={playerPluginId}
218
253
  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={{
@@ -1,7 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { Text } from "react-native";
3
3
  import { omit } from "ramda";
4
- import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
5
4
  import {
6
5
  textTransform,
7
6
  dateFormat,
@@ -9,6 +8,9 @@ import {
9
8
 
10
9
  import { useTextLabel, withFocusedStyles } from "./hooks";
11
10
  import { withScaledLineHeight } from "./utils";
11
+ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
12
+ import { MeasurementPortalContext } from "../../../MeasurmentsPortal";
13
+ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
12
14
 
13
15
  type Props = {
14
16
  style: any;
@@ -32,14 +34,24 @@ const _Text = ({
32
34
  ...otherProps
33
35
  }: Props) => {
34
36
  const _label = useTextLabel({ label, entry });
37
+ const isMeasurement = React.useContext(MeasurementPortalContext);
35
38
 
36
39
  if (isNilOrEmpty(_label)) {
37
40
  return null;
38
41
  }
39
42
 
43
+ // set maximum possible height for the text in case of measurement
44
+ const height =
45
+ isMeasurement && otherProps.numberOfLines && style.lineHeight
46
+ ? toNumber(otherProps.numberOfLines) * toNumber(style.lineHeight)
47
+ : undefined;
48
+
40
49
  return (
41
50
  <Text
42
- style={withScaledLineHeight(withFocusedStyles({ style, otherProps }))}
51
+ style={[
52
+ withScaledLineHeight(withFocusedStyles({ style, otherProps })),
53
+ { height },
54
+ ]}
43
55
  {...withoutLabel(otherProps)}
44
56
  >
45
57
  {dateTransformEnabled
@@ -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,11 +1,23 @@
1
1
  import React from "react";
2
- import { Text } from "react-native";
2
+ import RN from "react-native";
3
3
  import { Provider } from "react-redux";
4
4
  import TestRenderer from "react-test-renderer";
5
- import configureStore from "redux-mock-store";
6
- import CustomText from "../Text";
5
+ import configureStoreFn from "redux-mock-store";
7
6
 
8
- const mockStore = configureStore();
7
+ const mockUseIsRTL = jest.fn(() => true);
8
+ const mockGetIsRTL = jest.fn(() => true);
9
+ const mockUseLocalizedStrings = jest.fn(() => ({}));
10
+ const mockGetUILanguage = jest.fn(() => "he-HE");
11
+
12
+ jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
13
+ useIsRTL: mockUseIsRTL,
14
+ getIsRTL: mockGetIsRTL,
15
+ useLocalizedStrings: mockUseLocalizedStrings,
16
+ getUILanguage: mockGetUILanguage,
17
+ }));
18
+
19
+ const CustomText = require("../Text").default;
20
+ const mockStore = configureStoreFn();
9
21
 
10
22
  const defaultProps = {
11
23
  entry: {},
@@ -22,7 +34,7 @@ const getRenderedText = (label: string, store: any) => {
22
34
  </Provider>
23
35
  );
24
36
 
25
- return testRenderer.root.findByType(Text).props.children;
37
+ return testRenderer.root.findByType(RN.Text).props.children;
26
38
  };
27
39
 
28
40
  describe("RTL app: Hebrew text contains english word", () => {
@@ -34,7 +46,7 @@ describe("RTL app: Hebrew text contains english word", () => {
34
46
  remoteConfigurations: { localizations: { he: {} } },
35
47
  appData: {
36
48
  languageCode: "he",
37
- countryCode: "HE",
49
+ countryCode: "IL",
38
50
  },
39
51
  });
40
52
 
@@ -47,18 +59,22 @@ describe("RTL app: Hebrew text contains english word", () => {
47
59
 
48
60
  it("Hebrew text contains not first english word", () => {
49
61
  const renderedText = getRenderedText(textWithNotFirstEnglishWord, store);
50
-
51
62
  expect(renderedText).toEqual(textWithNotFirstEnglishWord);
52
63
  });
53
64
 
54
65
  it("Hebrew text doesn't contain english word", () => {
55
66
  const renderedText = getRenderedText(textWithoutEnglishWord, store);
56
-
57
67
  expect(renderedText).toEqual(textWithoutEnglishWord);
58
68
  });
59
69
  });
60
70
 
61
71
  describe("LTR app: English text", () => {
72
+ beforeAll(() => {
73
+ mockUseIsRTL.mockReturnValue(false);
74
+ mockGetIsRTL.mockReturnValue(false);
75
+ mockGetUILanguage.mockReturnValue("en-US");
76
+ });
77
+
62
78
  const englishText = "Test sentence";
63
79
 
64
80
  const store = mockStore({
@@ -71,7 +87,6 @@ describe("LTR app: English text", () => {
71
87
 
72
88
  it("English text", () => {
73
89
  const renderedText = getRenderedText(englishText, store);
74
-
75
90
  expect(renderedText).toEqual(englishText);
76
91
  });
77
92
  });
@@ -15,6 +15,7 @@ import { BorderContainerView } from "./BorderContainerView";
15
15
  import { CellBadge } from "./CellBadge";
16
16
  import { TvActionButtons } from "./tv/TvActionButtons";
17
17
  import { ButtonContainerView } from "./tv/ButtonContainerView";
18
+ import { ImageBorderContainer } from "./ImageBorderContainer";
18
19
 
19
20
  export const defaultComponents = {
20
21
  View,
@@ -28,6 +29,7 @@ export const defaultComponents = {
28
29
  ProgressBar,
29
30
  Toggle,
30
31
  LiveImage,
32
+ ImageBorderContainer,
31
33
  BorderContainerView,
32
34
  DynamicBadge,
33
35
  SecondaryImage,
@@ -61,7 +61,10 @@ export function ButtonContainerView({
61
61
  <View style={applyRTLStylesIfNeeded(style, isRTL)}>
62
62
  {insertBetween(
63
63
  (index) => (
64
- <HorizontalSeparator key={index} width={horizontalGutter} />
64
+ <HorizontalSeparator
65
+ key={`separator_${index}`}
66
+ width={horizontalGutter}
67
+ />
65
68
  ),
66
69
  cloneElementsWithIds(buttonIds, filteredChildren)
67
70
  )}
@@ -7,6 +7,10 @@ import type {
7
7
  ContainerProps,
8
8
  ContainerChildren,
9
9
  } from "./types";
10
+ import {
11
+ useIsRTL,
12
+ applyRTLStylesIfNeeded,
13
+ } from "@applicaster/zapp-react-native-utils/localizationUtils";
10
14
 
11
15
  const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
12
16
  <View style={{ width }} />
@@ -17,6 +21,7 @@ export function ButtonContainerView({
17
21
  children,
18
22
  ...otherProps
19
23
  }: ContainerProps) {
24
+ const isRTL = useIsRTL();
20
25
  const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
21
26
 
22
27
  const filteredChildren = useFilterChildren<ContainerChildren>(children);
@@ -26,10 +31,13 @@ export function ButtonContainerView({
26
31
  }
27
32
 
28
33
  return (
29
- <View style={style}>
34
+ <View style={applyRTLStylesIfNeeded(style, isRTL)}>
30
35
  {insertBetween(
31
36
  (index) => (
32
- <HorizontalSeparator key={index} width={horizontalGutter} />
37
+ <HorizontalSeparator
38
+ key={`separator_${index}`}
39
+ width={horizontalGutter}
40
+ />
33
41
  ),
34
42
  filteredChildren
35
43
  )}
@@ -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,10 @@ 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
+ valueFromLayout: additionalStyles.backgroundColor,
55
+ });
60
56
  },
61
57
  propName: "backgroundColor",
62
58
  },
@@ -5,6 +5,7 @@ import * as R from "ramda";
5
5
 
6
6
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
7
7
  import { normalizeProgressValue } from "./utils";
8
+ import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks";
8
9
 
9
10
  const ACTION_NAME = "continue-watching";
10
11
  const DEFAULT_FILL = 0;
@@ -27,6 +28,7 @@ const getProgressValue = (
27
28
 
28
29
  export const useFillInPercent = (entry: ZappEntry): number => {
29
30
  const action = useActions(ACTION_NAME);
31
+ const isActive = useIsScreenActive();
30
32
 
31
33
  const [fillInPercent, setFillInPercent] =
32
34
  React.useState<number>(DEFAULT_FILL);
@@ -39,6 +41,10 @@ export const useFillInPercent = (entry: ZappEntry): number => {
39
41
  }, []);
40
42
 
41
43
  React.useEffect(() => {
44
+ if (!isActive) {
45
+ return;
46
+ }
47
+
42
48
  // refresh progress in UI when it was changed in CW
43
49
  const unsubscribe = action?.addDataSourceListener?.(() => {
44
50
  getProgressValue(action, entry, (progress) => {
@@ -49,7 +55,7 @@ export const useFillInPercent = (entry: ZappEntry): number => {
49
55
  return () => {
50
56
  unsubscribe?.();
51
57
  };
52
- }, []);
58
+ }, [isActive]);
53
59
 
54
60
  return fillInPercent;
55
61
  };
@@ -37,14 +37,28 @@ function retrieveData(entry, func, args) {
37
37
  */
38
38
 
39
39
  export function configInflater(
40
- entry,
41
- { type, style, additionalProps = {}, data = [], elements }
40
+ entry: any,
41
+ {
42
+ type,
43
+ style,
44
+ additionalProps = {},
45
+ data = [],
46
+ elements,
47
+ }: {
48
+ type: any;
49
+ style: any;
50
+ additionalProps?: Record<string, any>;
51
+ data?: Array<{ propName: string; func: Function; args: any[] }>;
52
+ elements?: any[];
53
+ }
42
54
  ) {
43
- const props = R.reduce(
44
- (acc, curr) =>
45
- R.assoc(curr.propName, retrieveData(entry, curr.func, curr.args), acc),
46
- additionalProps,
47
- data
55
+ const props = data.reduce(
56
+ (acc, curr) => {
57
+ acc[curr.propName] = retrieveData(entry, curr.func, curr.args);
58
+
59
+ return acc;
60
+ },
61
+ { ...additionalProps }
48
62
  );
49
63
 
50
64
  let adjustedElements;
@@ -53,7 +53,7 @@ export const useAsyncRendering = (viewRef: React.RefObject<View>): Return => {
53
53
  measure(viewRef?.current, ({ width, height }) => {
54
54
  setDimensions({
55
55
  width,
56
- height,
56
+ height: Math.ceil(height),
57
57
  });
58
58
  });
59
59
  })
@@ -52,6 +52,8 @@ export function masterCellBuilder({
52
52
  const { View } = componentsMap;
53
53
  const elementsBuilder = dataAdapter(cellConfiguration);
54
54
 
55
+ // This is very expensive operation
56
+ // Keep calls to minimum
55
57
  const hasFocusableInside = hasFocusableInsideBuilder(elementsBuilder);
56
58
 
57
59
  /**
@@ -74,8 +76,8 @@ export function masterCellBuilder({
74
76
  const selectedState = screen?.selectedEntry;
75
77
 
76
78
  const entryIsSelected = React.useMemo(
77
- () => selectedState?.id === item?.id,
78
- [selectedState?.id]
79
+ () => !!(selectedState?.id && selectedState?.id === item?.id),
80
+ [selectedState?.id, item?.id]
79
81
  );
80
82
 
81
83
  const elements = React.useMemo(
@@ -84,7 +86,7 @@ export function masterCellBuilder({
84
86
  entry: item,
85
87
  state: getEntryState(state, entryIsSelected),
86
88
  }),
87
- [state, item, entryIsSelected]
89
+ [state, item?.id, entryIsSelected] // Assuming that item won't mutate
88
90
  );
89
91
 
90
92
  const wrapperRef = React.useRef(null);
@@ -9,4 +9,13 @@ describe("insertBetween", () => {
9
9
 
10
10
  expect(result).toEqual([1, "|", 2, "|", 3]);
11
11
  });
12
+
13
+ it("insertBetween", () => {
14
+ const separator = (index) => `separator:${index}`;
15
+ const items = [1, 2, 3];
16
+
17
+ const result = insertBetween(separator, items);
18
+
19
+ expect(result).toEqual([1, "separator:0", 2, "separator:1", 3]);
20
+ });
12
21
  });