@applicaster/zapp-react-native-ui-components 13.0.11-rc.0 → 13.0.11

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.
@@ -28,17 +28,10 @@ interface Props {
28
28
  onAsyncRender: () => void;
29
29
  }
30
30
 
31
- const SecondaryImageComponent = (props: Props) => {
32
- const {
33
- uri,
34
- style,
35
- displayMode,
36
- imageSizing,
37
- fitPosition,
38
- fixedHeight,
39
- fixedWidth,
40
- onAsyncRender,
41
- } = props;
31
+ /** Secondary Image Dynamic does not render until the image is loaded */
32
+ const SecondaryImageDynamic = (props: Props) => {
33
+ const { uri, style, displayMode, imageSizing, fitPosition, onAsyncRender } =
34
+ props;
42
35
 
43
36
  const imageDimension = useGetImageDimensions(
44
37
  uri,
@@ -46,13 +39,9 @@ const SecondaryImageComponent = (props: Props) => {
46
39
  isImageSizingFit(imageSizing) ? undefined : (style.height as number)
47
40
  );
48
41
 
49
- const containerHeight = isDisplayModeFixed(displayMode)
50
- ? fixedHeight
51
- : imageDimension?.height;
42
+ const containerHeight = imageDimension?.height;
52
43
 
53
- const containerWidth = isDisplayModeFixed(displayMode)
54
- ? fixedWidth
55
- : style?.width;
44
+ const containerWidth = style?.width;
56
45
 
57
46
  if (isNil(imageDimension?.aspectRatio)) {
58
47
  return null;
@@ -80,4 +69,63 @@ const SecondaryImageComponent = (props: Props) => {
80
69
  );
81
70
  };
82
71
 
72
+ /** Secondary Image Fixed does not render the image until the image is loaded, but keep container rendered */
73
+ const SecondaryImageFixed = (props: Props) => {
74
+ const {
75
+ uri,
76
+ style,
77
+ displayMode,
78
+ imageSizing,
79
+ fitPosition,
80
+ fixedHeight,
81
+ fixedWidth,
82
+ onAsyncRender,
83
+ } = props;
84
+
85
+ const imageDimension = useGetImageDimensions(
86
+ uri,
87
+ style.width as number,
88
+ isImageSizingFit(imageSizing) ? undefined : (style.height as number)
89
+ );
90
+
91
+ return (
92
+ <View style={style} onLayout={onAsyncRender}>
93
+ {isNil(imageDimension?.aspectRatio) ? null : (
94
+ <Image
95
+ {...props}
96
+ source={{ uri }}
97
+ style={{
98
+ ...getStyle({
99
+ imageSizing,
100
+ fitPosition,
101
+ displayMode,
102
+ imageDimension,
103
+ containerHeight: fixedHeight,
104
+ containerWidth: fixedWidth,
105
+ }),
106
+ borderRadius: style.borderRadius,
107
+ aspectRatio: imageDimension.aspectRatio,
108
+ }}
109
+ />
110
+ )}
111
+ </View>
112
+ );
113
+ };
114
+
115
+ const SecondaryImageComponent = (props: Props) => {
116
+ const { uri, displayMode } = props;
117
+
118
+ if (!uri) {
119
+ return null;
120
+ }
121
+
122
+ const isFixed = isDisplayModeFixed(displayMode);
123
+
124
+ return isFixed ? (
125
+ <SecondaryImageFixed {...props} />
126
+ ) : (
127
+ <SecondaryImageDynamic {...props} />
128
+ );
129
+ };
130
+
83
131
  export const SecondaryImage = withAsyncRenderHOC(SecondaryImageComponent);
@@ -4,9 +4,26 @@ import { render } from "@testing-library/react-native";
4
4
  import { SecondaryImage } from "../Image";
5
5
 
6
6
  describe("SecondaryImage - Image", () => {
7
- it("SecondaryImage should not render if no aspect ratio", async () => {
7
+ it("SecondaryImage should not render if no uri", async () => {
8
8
  const wrapper = await render(
9
- <SecondaryImage uri="" style={{ width: 100 }} />
9
+ <SecondaryImage
10
+ displayMode="dynamic"
11
+ uri={undefined}
12
+ style={{ width: 100 }}
13
+ />
14
+ );
15
+
16
+ expect(wrapper.toJSON()).toEqual(null);
17
+ expect(wrapper.toJSON()).toMatchSnapshot();
18
+ });
19
+
20
+ it("SecondaryImage should not render if no aspect ratio (dynamic)", async () => {
21
+ const wrapper = await render(
22
+ <SecondaryImage
23
+ displayMode="dynamic"
24
+ uri="someurl"
25
+ style={{ width: 100 }}
26
+ />
10
27
  );
11
28
 
12
29
  expect(wrapper.toJSON()).toEqual(null);
@@ -16,7 +33,8 @@ describe("SecondaryImage - Image", () => {
16
33
  it("SecondaryImage should render if known dimensions", async () => {
17
34
  const wrapper = await render(
18
35
  <SecondaryImage
19
- uri=""
36
+ uri="someUrl"
37
+ displayMode="dynamic"
20
38
  style={{ width: 100, height: 100, borderRadius: 10 }}
21
39
  />
22
40
  );
@@ -1,6 +1,8 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`SecondaryImage - Image SecondaryImage should not render if no aspect ratio 1`] = `null`;
3
+ exports[`SecondaryImage - Image SecondaryImage should not render if no aspect ratio (dynamic) 1`] = `null`;
4
+
5
+ exports[`SecondaryImage - Image SecondaryImage should not render if no uri 1`] = `null`;
4
6
 
5
7
  exports[`SecondaryImage - Image SecondaryImage should render if known dimensions 1`] = `
6
8
  <View
@@ -14,10 +16,11 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
14
16
  }
15
17
  >
16
18
  <Image
19
+ displayMode="dynamic"
17
20
  onAsyncRender={[Function]}
18
21
  source={
19
22
  {
20
- "uri": "",
23
+ "uri": "someUrl",
21
24
  }
22
25
  }
23
26
  style={
@@ -28,7 +31,7 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
28
31
  "width": 100,
29
32
  }
30
33
  }
31
- uri=""
34
+ uri="someUrl"
32
35
  />
33
36
  </View>
34
37
  `;
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
- import { View, StyleSheet, FlatList } from "react-native";
3
+ import { FlatList, StyleSheet, View } from "react-native";
4
4
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
5
5
  import { RiverItem } from "../RiverItem";
6
6
  import { RiverFooter } from "../RiverFooter";
@@ -9,8 +9,8 @@ import { useScreenConfiguration } from "../useScreenConfiguration";
9
9
  import { RefreshControl } from "../RefreshControl";
10
10
  import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
11
11
  import {
12
- useProfilerLogging,
13
12
  usePipesCacheReset,
13
+ useProfilerLogging,
14
14
  } from "@applicaster/zapp-react-native-utils/reactHooks";
15
15
  import { useLoadingState } from "./hooks/useLoadingState";
16
16
  import { ViewportTracker } from "../../Viewport";
@@ -25,6 +25,8 @@ import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHo
25
25
  import { useShallow } from "zustand/react/shallow";
26
26
 
27
27
  import { isAndroidPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
28
+ import { ComponentsMapHeightContext } from "./ContextProviders/ComponentsMapHeightContext";
29
+ import { ComponentsMapRefContext } from "./ContextProviders/ComponentsMapRefContext";
28
30
 
29
31
  const isAndroid = isAndroidPlatform();
30
32
 
@@ -70,6 +72,7 @@ function ComponentsMapComponent(props: Props) {
70
72
  } = props;
71
73
 
72
74
  const flatListRef = React.useRef<FlatList | null>(null);
75
+ const flatListWrapperRef = React.useRef<View | null>(null);
73
76
  const screenConfig = useScreenConfiguration(riverId);
74
77
  const screenData = useScreenData(riverId);
75
78
  const pullToRefreshEnabled = screenData?.rules?.pull_to_refresh_enabled;
@@ -265,48 +268,52 @@ function ComponentsMapComponent(props: Props) {
265
268
  // The Screen Picker in Mobile is completly different than the TV
266
269
  // so the various offsets / margins in TV do not apply here.
267
270
  return (
268
- <View style={styles.container}>
269
- <ScreenLoadingMeasurements
270
- riverId={riverId}
271
- numberOfComponents={riverComponents.length}
272
- >
273
- <ViewportTracker>
274
- <FlatList
275
- ref={(ref) => {
276
- flatListRef.current = ref;
277
- }}
278
- // Fix for WebView rerender crashes on Android API 28+
279
- // https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
280
- overScrollMode={isAndroid ? "never" : "auto"}
281
- scrollIndicatorInsets={scrollIndicatorInsets}
282
- extraData={feed}
283
- stickyHeaderIndices={stickyHeaderIndices}
284
- removeClippedSubviews={isAndroid}
285
- onLayout={handleOnLayout}
286
- initialNumToRender={3}
287
- maxToRenderPerBatch={10}
288
- windowSize={12}
289
- listKey={riverId}
290
- keyExtractor={keyExtractor}
291
- renderItem={renderRiverItem}
292
- data={riverComponents}
293
- contentContainerStyle={contentContainerStyle}
294
- ListFooterComponent={
295
- <RiverFooter
296
- flatListHeight={flatListHeight}
297
- loadingState={loadingState}
271
+ <View style={styles.container} ref={flatListWrapperRef}>
272
+ <ComponentsMapHeightContext.Provider value={flatListHeight}>
273
+ <ComponentsMapRefContext.Provider value={flatListWrapperRef}>
274
+ <ScreenLoadingMeasurements
275
+ riverId={riverId}
276
+ numberOfComponents={riverComponents.length}
277
+ >
278
+ <ViewportTracker>
279
+ <FlatList
280
+ ref={(ref) => {
281
+ flatListRef.current = ref;
282
+ }}
283
+ // Fix for WebView rerender crashes on Android API 28+
284
+ // https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
285
+ overScrollMode={isAndroid ? "never" : "auto"}
286
+ scrollIndicatorInsets={scrollIndicatorInsets}
287
+ extraData={feed}
288
+ stickyHeaderIndices={stickyHeaderIndices}
289
+ removeClippedSubviews={isAndroid}
290
+ onLayout={handleOnLayout}
291
+ initialNumToRender={3}
292
+ maxToRenderPerBatch={10}
293
+ windowSize={12}
294
+ listKey={riverId}
295
+ keyExtractor={keyExtractor}
296
+ renderItem={renderRiverItem}
297
+ data={riverComponents}
298
+ contentContainerStyle={contentContainerStyle}
299
+ ListFooterComponent={
300
+ <RiverFooter
301
+ flatListHeight={flatListHeight}
302
+ loadingState={loadingState}
303
+ />
304
+ }
305
+ refreshControl={refreshControl}
306
+ onScrollBeginDrag={onScrollBeginDrag}
307
+ onScroll={onScroll}
308
+ onMomentumScrollEnd={_onMomentumScrollEnd}
309
+ onScrollEndDrag={_onScrollEndDrag}
310
+ scrollEventThrottle={16}
311
+ {...scrollViewExtraProps}
298
312
  />
299
- }
300
- refreshControl={refreshControl}
301
- onScrollBeginDrag={onScrollBeginDrag}
302
- onScroll={onScroll}
303
- onMomentumScrollEnd={_onMomentumScrollEnd}
304
- onScrollEndDrag={_onScrollEndDrag}
305
- scrollEventThrottle={16}
306
- {...scrollViewExtraProps}
307
- />
308
- </ViewportTracker>
309
- </ScreenLoadingMeasurements>
313
+ </ViewportTracker>
314
+ </ScreenLoadingMeasurements>
315
+ </ComponentsMapRefContext.Provider>
316
+ </ComponentsMapHeightContext.Provider>
310
317
  </View>
311
318
  );
312
319
  }
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+
3
+ export const ComponentsMapHeightContext = React.createContext<number | null>(
4
+ null
5
+ );
6
+
7
+ export const useComponentsMapHeight = () =>
8
+ React.useContext(ComponentsMapHeightContext);
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ import { View } from "react-native";
3
+
4
+ export const ComponentsMapRefContext =
5
+ React.createContext<React.RefObject<View | null> | null>(null);
6
+
7
+ export const useComponentsMapRef = () =>
8
+ React.useContext(ComponentsMapRefContext);
@@ -43,6 +43,8 @@ type Props = {
43
43
  children: React.ReactNode;
44
44
  };
45
45
 
46
+ const activeOffsetY = [-5, 5];
47
+
46
48
  export const AnimatedScrollModalComponent = ({ children }: Props) => {
47
49
  const {
48
50
  isActiveGesture,
@@ -378,9 +380,9 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
378
380
  ref={panHandlerRef}
379
381
  simultaneousHandlers={[scrollRef, tapHandlerRef]}
380
382
  shouldCancelWhenOutside={isMaximizedModal}
383
+ activeOffsetY={activeOffsetY}
381
384
  onGestureEvent={onGestureEvent}
382
385
  onHandlerStateChange={onHandlerStateChange}
383
- activeOffsetY={[-5, 5]}
384
386
  >
385
387
  <Animated.View>
386
388
  <NativeViewGestureHandler
@@ -4,8 +4,8 @@ import {
4
4
  Dimensions,
5
5
  Easing,
6
6
  StyleProp,
7
- ViewStyle,
8
7
  StyleSheet,
8
+ ViewStyle,
9
9
  } from "react-native";
10
10
  import { useTargetScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
11
11
  import { ComponentsMap } from "@applicaster/zapp-react-native-ui-components/Components/River/ComponentsMap";
@@ -47,11 +47,10 @@ export const PlayerDetails = ({
47
47
  const screenData = useTargetScreenData(entry);
48
48
  const insets = useSafeAreaInsets();
49
49
 
50
- const extraTabletStyles = !isAudioPlayer
51
- ? isTabletLandscape
50
+ const extraTabletStyles =
51
+ !isAudioPlayer && isTabletLandscape
52
52
  ? { marginTop: -insets.top, paddingTop: insets.top + 20 }
53
- : { marginTop: -8, paddingTop: -8 }
54
- : {};
53
+ : null;
55
54
 
56
55
  // Animation setup
57
56
  const translateY = useRef(new Animated.Value(50)).current;
@@ -107,6 +106,7 @@ export const PlayerDetails = ({
107
106
  riverId={screenData.id}
108
107
  feed={screenData?.data?.source}
109
108
  riverComponents={screenData.ui_components}
109
+ isScreenWrappedInContainer
110
110
  />
111
111
  ) : null}
112
112
  </Animated.View>
@@ -1,4 +1,5 @@
1
1
  import * as React from "react";
2
+ import { useContext } from "react";
2
3
  import {
3
4
  Dimensions,
4
5
  Platform,
@@ -131,6 +132,11 @@ const getTabletWidth = (
131
132
  return widthValue - sidebarWidth;
132
133
  };
133
134
 
135
+ const PlayerDetailsWrapperHeightContext = React.createContext(null);
136
+
137
+ export const usePlayerDetailsWrapperHeight = () =>
138
+ useContext(PlayerDetailsWrapperHeightContext);
139
+
134
140
  const PlayerWrapperComponent = (props: Props) => {
135
141
  const {
136
142
  entry,
@@ -146,6 +152,20 @@ const PlayerWrapperComponent = (props: Props) => {
146
152
  pip,
147
153
  } = props;
148
154
 
155
+ const [playerDetailsWrapperHeight, setPlayerDetailsWrapperHeight] =
156
+ React.useState(0);
157
+
158
+ const onLayout = React.useCallback(
159
+ ({
160
+ nativeEvent: {
161
+ layout: { height },
162
+ },
163
+ }) => {
164
+ setPlayerDetailsWrapperHeight(height);
165
+ },
166
+ []
167
+ );
168
+
149
169
  const isTablet = useIsTablet();
150
170
 
151
171
  const isInlineModal = inline && isModal;
@@ -238,23 +258,28 @@ const PlayerWrapperComponent = (props: Props) => {
238
258
  </AnimatedVideoPlayerComponent>
239
259
  </AnimationComponent>
240
260
  </View>
241
-
242
- <AnimatedScrollModal>
243
- {isShowPlayerDetails ? (
244
- <AnimationComponent
245
- animationType={ComponentAnimationType.componentFade}
246
- style={defaultStyles.flex}
247
- >
248
- <PlayerDetails
249
- configuration={configuration}
250
- style={defaultStyles.playerDetails}
251
- entry={entry}
252
- isTabletLandscape={isTabletLandscape}
253
- isTablet={isTablet}
254
- />
255
- </AnimationComponent>
256
- ) : null}
257
- </AnimatedScrollModal>
261
+ <View style={defaultStyles.flex} onLayout={onLayout}>
262
+ <PlayerDetailsWrapperHeightContext.Provider
263
+ value={playerDetailsWrapperHeight}
264
+ >
265
+ <AnimatedScrollModal>
266
+ {isShowPlayerDetails ? (
267
+ <AnimationComponent
268
+ animationType={ComponentAnimationType.componentFade}
269
+ style={defaultStyles.flex}
270
+ >
271
+ <PlayerDetails
272
+ configuration={configuration}
273
+ style={defaultStyles.playerDetails}
274
+ entry={entry}
275
+ isTabletLandscape={isTabletLandscape}
276
+ isTablet={isTablet}
277
+ />
278
+ </AnimationComponent>
279
+ ) : null}
280
+ </AnimatedScrollModal>
281
+ </PlayerDetailsWrapperHeightContext.Provider>
282
+ </View>
258
283
  </AnimationComponent>
259
284
  </WrapperView>
260
285
  );
@@ -72,7 +72,7 @@ exports[`PlayerWrapper renders inline 1`] = `
72
72
  testID="test-player-container"
73
73
  />
74
74
  <View
75
- animationType="componentFade"
75
+ onLayout={[Function]}
76
76
  style={
77
77
  {
78
78
  "flex": 1,
@@ -80,26 +80,35 @@ exports[`PlayerWrapper renders inline 1`] = `
80
80
  }
81
81
  >
82
82
  <View
83
- configuration={
84
- {
85
- "tablet_landscape_player_container_background_color": "red",
86
- "tablet_landscape_sidebar_width": "35%",
87
- }
88
- }
89
- entry={
90
- {
91
- "id": "test",
92
- }
93
- }
94
- isTablet={false}
95
- isTabletLandscape={false}
83
+ animationType="componentFade"
96
84
  style={
97
85
  {
98
86
  "flex": 1,
99
- "paddingTop": 20,
100
87
  }
101
88
  }
102
- />
89
+ >
90
+ <View
91
+ configuration={
92
+ {
93
+ "tablet_landscape_player_container_background_color": "red",
94
+ "tablet_landscape_sidebar_width": "35%",
95
+ }
96
+ }
97
+ entry={
98
+ {
99
+ "id": "test",
100
+ }
101
+ }
102
+ isTablet={false}
103
+ isTabletLandscape={false}
104
+ style={
105
+ {
106
+ "flex": 1,
107
+ "paddingTop": 20,
108
+ }
109
+ }
110
+ />
111
+ </View>
103
112
  </View>
104
113
  </View>
105
114
  </RNCSafeAreaView>
@@ -177,6 +186,14 @@ exports[`PlayerWrapper renders inline and docked 1`] = `
177
186
  }
178
187
  testID="test-player-container"
179
188
  />
189
+ <View
190
+ onLayout={[Function]}
191
+ style={
192
+ {
193
+ "flex": 1,
194
+ }
195
+ }
196
+ />
180
197
  </View>
181
198
  </RNCSafeAreaView>
182
199
  </RNCSafeAreaProvider>
@@ -270,7 +287,7 @@ exports[`PlayerWrapper renders inline on tablet in landscape orientation 1`] = `
270
287
  />
271
288
  </View>
272
289
  <View
273
- animationType="componentFade"
290
+ onLayout={[Function]}
274
291
  style={
275
292
  {
276
293
  "flex": 1,
@@ -278,26 +295,35 @@ exports[`PlayerWrapper renders inline on tablet in landscape orientation 1`] = `
278
295
  }
279
296
  >
280
297
  <View
281
- configuration={
282
- {
283
- "tablet_landscape_player_container_background_color": "red",
284
- "tablet_landscape_sidebar_width": "35%",
285
- }
286
- }
287
- entry={
288
- {
289
- "id": "test",
290
- }
291
- }
292
- isTablet={true}
293
- isTabletLandscape={true}
298
+ animationType="componentFade"
294
299
  style={
295
300
  {
296
301
  "flex": 1,
297
- "paddingTop": 20,
298
302
  }
299
303
  }
300
- />
304
+ >
305
+ <View
306
+ configuration={
307
+ {
308
+ "tablet_landscape_player_container_background_color": "red",
309
+ "tablet_landscape_sidebar_width": "35%",
310
+ }
311
+ }
312
+ entry={
313
+ {
314
+ "id": "test",
315
+ }
316
+ }
317
+ isTablet={true}
318
+ isTabletLandscape={true}
319
+ style={
320
+ {
321
+ "flex": 1,
322
+ "paddingTop": 20,
323
+ }
324
+ }
325
+ />
326
+ </View>
301
327
  </View>
302
328
  </View>
303
329
  </RNCSafeAreaView>
@@ -375,6 +401,14 @@ exports[`PlayerWrapper renders properly 1`] = `
375
401
  }
376
402
  testID="test-player-container"
377
403
  />
404
+ <View
405
+ onLayout={[Function]}
406
+ style={
407
+ {
408
+ "flex": 1,
409
+ }
410
+ }
411
+ />
378
412
  </View>
379
413
  </RNCSafeAreaView>
380
414
  </RNCSafeAreaProvider>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-ui-components",
3
- "version": "13.0.11-rc.0",
3
+ "version": "13.0.11",
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",
@@ -31,10 +31,10 @@
31
31
  "redux-mock-store": "^1.5.3"
32
32
  },
33
33
  "dependencies": {
34
- "@applicaster/applicaster-types": "13.0.11-rc.0",
35
- "@applicaster/zapp-react-native-bridge": "13.0.11-rc.0",
36
- "@applicaster/zapp-react-native-redux": "13.0.11-rc.0",
37
- "@applicaster/zapp-react-native-utils": "13.0.11-rc.0",
34
+ "@applicaster/applicaster-types": "13.0.11",
35
+ "@applicaster/zapp-react-native-bridge": "13.0.11",
36
+ "@applicaster/zapp-react-native-redux": "13.0.11",
37
+ "@applicaster/zapp-react-native-utils": "13.0.11",
38
38
  "promise": "^8.3.0",
39
39
  "react-router-native": "^5.1.2",
40
40
  "url": "^0.11.0",