@applicaster/zapp-react-native-ui-components 15.0.0-rc.2 → 15.0.0-rc.20

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.
@@ -0,0 +1,44 @@
1
+ import * as React from "react";
2
+ import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/FocusableTvOS";
3
+ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
4
+
5
+ type Props = {
6
+ id: string;
7
+ groupId: string;
8
+ isParallaxDisabled: boolean;
9
+ applyWrapper: boolean;
10
+ children: (focused: boolean) => React.ReactNode;
11
+ onFocus: (arg1: any, index?: number) => void;
12
+ onBlur: Callback;
13
+ };
14
+
15
+ export const FocusableWrapper = ({
16
+ id,
17
+ groupId,
18
+ isParallaxDisabled,
19
+ children,
20
+ applyWrapper,
21
+ onFocus,
22
+ onBlur,
23
+ }: Props) => {
24
+ if (applyWrapper) {
25
+ return (
26
+ <Focusable
27
+ id={id}
28
+ groupId={groupId}
29
+ isParallaxDisabled={isParallaxDisabled}
30
+ onFocus={onFocus}
31
+ onBlur={onBlur}
32
+ willReceiveFocus={noop}
33
+ hasReceivedFocus={noop}
34
+ // @ts-ignore
35
+ offsetUpdater={noop}
36
+ isFocusable
37
+ >
38
+ {(focused) => children(focused)}
39
+ </Focusable>
40
+ );
41
+ }
42
+
43
+ return <>{children(false)}</>;
44
+ };
@@ -1,6 +1,9 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
3
  import { View, StyleSheet } from "react-native";
4
+ import { first, filter } from "rxjs/operators";
5
+
6
+ import { compose } from "@applicaster/zapp-react-native-utils/utils";
4
7
 
5
8
  import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/FocusableTvOS";
6
9
  import { FocusableCell } from "@applicaster/zapp-react-native-ui-components/Components/FocusableCell";
@@ -9,7 +12,11 @@ import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUti
9
12
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
10
13
  import { sendSelectCellEvent } from "@applicaster/zapp-react-native-utils/analyticsUtils";
11
14
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
15
+ import { toBooleanWithDefaultTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
12
16
  import { CellWithFocusable } from "./CellWithFocusable";
17
+ import { FocusableWrapper } from "./FocusableWrapper";
18
+
19
+ import { focusableButtonsRegistration$ } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
13
20
 
14
21
  type Props = {
15
22
  item: ZappEntry;
@@ -66,6 +73,8 @@ type Props = {
66
73
  shouldUpdate: boolean;
67
74
  behavior: Behavior;
68
75
  componentsMapOffset: number;
76
+ applyFocusableWrapper: boolean;
77
+ hasFocusableInside: boolean;
69
78
  };
70
79
 
71
80
  type State = {
@@ -82,7 +91,7 @@ const baseCellStyles = {
82
91
  flex: 1,
83
92
  } as const;
84
93
 
85
- export class TvOSCellComponent extends React.Component<Props, State> {
94
+ class TvOSCell extends React.Component<Props, State> {
86
95
  cell: any;
87
96
  target: any;
88
97
  layout: any;
@@ -239,6 +248,8 @@ export class TvOSCellComponent extends React.Component<Props, State> {
239
248
  groupId,
240
249
  isFocusable,
241
250
  behavior,
251
+ applyFocusableWrapper,
252
+ hasFocusableInside,
242
253
  } = this.props;
243
254
 
244
255
  const { id } = item;
@@ -254,24 +265,33 @@ export class TvOSCellComponent extends React.Component<Props, State> {
254
265
  this.onFocus(arg1, index);
255
266
  };
256
267
 
257
- const hasFocusableInside = CellRenderer.hasFocusableInside?.(item);
258
-
259
268
  if (hasFocusableInside) {
260
269
  return (
261
270
  <View onLayout={this.onLayout}>
262
- <CellWithFocusable
263
- CellRenderer={CellRenderer}
264
- item={item}
271
+ <FocusableWrapper
265
272
  id={focusableId}
266
- groupId={(groupId || component?.id).toString()}
273
+ groupId={String(groupId || component?.id)}
274
+ isParallaxDisabled={this.layout?.width > 1740}
267
275
  onFocus={handleFocus}
268
- index={index}
269
- scrollTo={this.scrollTo}
270
- preferredFocus={preferredFocus}
271
- focused={this.props.focused}
272
- behavior={behavior}
273
- isFocusable={isFocusable}
274
- />
276
+ onBlur={onBlur || this.onBlur}
277
+ applyWrapper={applyFocusableWrapper}
278
+ >
279
+ {(focused) => (
280
+ <CellWithFocusable
281
+ CellRenderer={CellRenderer}
282
+ item={item}
283
+ id={focusableId}
284
+ groupId={(groupId || component?.id).toString()}
285
+ onFocus={handleFocus}
286
+ index={index}
287
+ scrollTo={this.scrollTo}
288
+ preferredFocus={preferredFocus}
289
+ focused={focused || this.props.focused}
290
+ behavior={behavior}
291
+ isFocusable={isFocusable}
292
+ />
293
+ )}
294
+ </FocusableWrapper>
275
295
  </View>
276
296
  );
277
297
  }
@@ -309,3 +329,49 @@ export class TvOSCellComponent extends React.Component<Props, State> {
309
329
  );
310
330
  }
311
331
  }
332
+
333
+ export function withFocusableWrapperHOC(Component) {
334
+ return function WrappedComponent(props) {
335
+ const [focusableViewIsRendered, setFocusableViewIsRendered] =
336
+ React.useState(false);
337
+
338
+ const { CellRenderer, item, groupId, component } = props;
339
+
340
+ const isFocusable = toBooleanWithDefaultTrue(props?.isFocusable);
341
+
342
+ const focusableGroupId = String(groupId || component?.id);
343
+
344
+ const hasFocusableInside = CellRenderer.hasFocusableInside?.(item);
345
+
346
+ React.useEffect(() => {
347
+ // start waiting any first registration of FocusableButton inside this focusableGroup
348
+ // after it we could get rid of applying focusable-wrapper
349
+ const subscription = focusableButtonsRegistration$(focusableGroupId)
350
+ .pipe(
351
+ filter(() => isFocusable),
352
+ first()
353
+ )
354
+ .subscribe(() => {
355
+ setFocusableViewIsRendered(true);
356
+ });
357
+
358
+ return () => {
359
+ subscription.unsubscribe();
360
+ };
361
+ }, [isFocusable, focusableGroupId]);
362
+
363
+ const applyFocusableWrapper = React.useMemo(() => {
364
+ return isFocusable && hasFocusableInside && !focusableViewIsRendered;
365
+ }, [isFocusable, hasFocusableInside, focusableViewIsRendered]);
366
+
367
+ return (
368
+ <Component
369
+ {...props}
370
+ applyFocusableWrapper={applyFocusableWrapper}
371
+ hasFocusableInside={hasFocusableInside}
372
+ />
373
+ );
374
+ };
375
+ }
376
+
377
+ export const TvOSCellComponent = compose(withFocusableWrapperHOC)(TvOSCell);
@@ -142,17 +142,15 @@ export const useCurationAPI = (
142
142
  const url = path(SOURCE_PATH, component);
143
143
  const mapping = path(MAPPING_PATH, component);
144
144
 
145
- map[component.id] = mapping
146
- ? getInflatedDataSourceUrl({
147
- source: url,
148
- contexts: {
149
- entry: entryContext,
150
- screen: screenContext,
151
- search: getSearchContext(searchContext, mapping),
152
- },
153
- mapping,
154
- })
155
- : url;
145
+ map[component.id] = getInflatedDataSourceUrl({
146
+ source: url,
147
+ contexts: {
148
+ entry: entryContext,
149
+ screen: screenContext,
150
+ search: getSearchContext(searchContext, mapping),
151
+ },
152
+ mapping,
153
+ });
156
154
  });
157
155
 
158
156
  return map;
@@ -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,47 +268,51 @@ 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
- keyExtractor={keyExtractor}
290
- renderItem={renderRiverItem}
291
- data={riverComponents}
292
- contentContainerStyle={contentContainerStyle}
293
- ListFooterComponent={
294
- <RiverFooter
295
- flatListHeight={flatListHeight}
296
- 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
+ keyExtractor={keyExtractor}
295
+ renderItem={renderRiverItem}
296
+ data={riverComponents}
297
+ contentContainerStyle={contentContainerStyle}
298
+ ListFooterComponent={
299
+ <RiverFooter
300
+ flatListHeight={flatListHeight}
301
+ loadingState={loadingState}
302
+ />
303
+ }
304
+ refreshControl={refreshControl}
305
+ onScrollBeginDrag={onScrollBeginDrag}
306
+ onScroll={onScroll}
307
+ onMomentumScrollEnd={_onMomentumScrollEnd}
308
+ onScrollEndDrag={_onScrollEndDrag}
309
+ scrollEventThrottle={16}
310
+ {...scrollViewExtraProps}
297
311
  />
298
- }
299
- refreshControl={refreshControl}
300
- onScrollBeginDrag={onScrollBeginDrag}
301
- onScroll={onScroll}
302
- onMomentumScrollEnd={_onMomentumScrollEnd}
303
- onScrollEndDrag={_onScrollEndDrag}
304
- scrollEventThrottle={16}
305
- {...scrollViewExtraProps}
306
- />
307
- </ViewportTracker>
308
- </ScreenLoadingMeasurements>
312
+ </ViewportTracker>
313
+ </ScreenLoadingMeasurements>
314
+ </ComponentsMapRefContext.Provider>
315
+ </ComponentsMapHeightContext.Provider>
309
316
  </View>
310
317
  );
311
318
  }
@@ -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);
@@ -10,6 +10,8 @@ import {
10
10
  setFocusOnMenu,
11
11
  } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux";
12
12
 
13
+ import { waitUntilScreenRevealManagerIsReady } from "@applicaster/zapp-react-native-ui-components/Components/ScreenRevealManager/utils";
14
+
13
15
  type Return =
14
16
  | {
15
17
  onContent: true;
@@ -57,14 +59,22 @@ export const useInitialFocus = (): void => {
57
59
  React.useEffect(() => {
58
60
  const initialFocus = getInitialFocus(focusOnContent, isNavBarVisible);
59
61
 
60
- if (initialFocus.onContent) {
61
- setFocusOnContent(currentRoute);
62
+ if (initialFocus.onMenu) {
63
+ setFocusOnMenu(currentRoute);
62
64
 
63
65
  return;
64
66
  }
65
67
 
66
- if (initialFocus.onMenu) {
67
- setFocusOnMenu(currentRoute);
68
+ if (initialFocus.onContent) {
69
+ const subscription = waitUntilScreenRevealManagerIsReady().subscribe(
70
+ () => {
71
+ setFocusOnContent(currentRoute);
72
+ }
73
+ );
74
+
75
+ return () => {
76
+ subscription.unsubscribe();
77
+ };
68
78
  }
69
79
  }, []);
70
80
  };
@@ -2,6 +2,7 @@
2
2
 
3
3
  exports[`<Screen Component /> when the navbar should be hidden renders correctly 1`] = `
4
4
  <View
5
+ importantForAccessibility="yes"
5
6
  style={
6
7
  {
7
8
  "backgroundColor": "blue",
@@ -34,6 +35,7 @@ exports[`<Screen Component /> when the navbar should be hidden renders correctly
34
35
 
35
36
  exports[`<Screen Component /> when the navbar should show renders correctly 1`] = `
36
37
  <View
38
+ importantForAccessibility="yes"
37
39
  style={
38
40
  {
39
41
  "backgroundColor": "blue",
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@applicaster/applicaster-types" />
2
2
  import React from "react";
3
- import { View } from "react-native";
3
+ import { AccessibilityInfo, findNodeHandle, View } from "react-native";
4
4
  import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
5
 
6
6
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
@@ -12,6 +12,7 @@ import {
12
12
  } from "@applicaster/zapp-react-native-utils/navigationUtils";
13
13
  import {
14
14
  useCurrentScreenData,
15
+ useIsScreenActive,
15
16
  useNavbarState,
16
17
  useNavigation,
17
18
  useRoute,
@@ -57,8 +58,8 @@ export function Screen(_props: Props) {
57
58
  const hasMenu = shouldNavBarDisplayMenu(currentRiver, plugins);
58
59
 
59
60
  const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(
60
- getNavBarProps(currentRiver, pathname, title),
61
- [currentRiver, pathname]
61
+ () => getNavBarProps(currentRiver, pathname, title),
62
+ [currentRiver, pathname, title]
62
63
  );
63
64
 
64
65
  const NavBar = React.useMemo(
@@ -89,13 +90,29 @@ export function Screen(_props: Props) {
89
90
  [theme.app_background_color, backgroundColor]
90
91
  );
91
92
 
92
- // Set ready state when screen is rotated to desired orientation
93
+ const isActive = useIsScreenActive();
94
+
95
+ const ref = React.useRef(null);
93
96
  const isReady = useWaitForValidOrientation();
94
97
 
98
+ React.useEffect(() => {
99
+ if (ref.current && isActive && isReady) {
100
+ const nodeHandle = findNodeHandle(ref.current);
101
+
102
+ if (nodeHandle != null) {
103
+ AccessibilityInfo.setAccessibilityFocus(nodeHandle);
104
+ }
105
+ }
106
+ }, [isActive, isReady]);
107
+
95
108
  // We prevent rendering of the screen until UI is actually rotated to screen desired orientation.
96
109
  // This saves unnecessary re-renders and user will not see distorted aspect screen.
97
110
  return (
98
- <View style={style}>
111
+ <View
112
+ ref={ref}
113
+ style={style}
114
+ importantForAccessibility={!isActive ? "no-hide-descendants" : "yes"}
115
+ >
99
116
  {isReady ? (
100
117
  <>
101
118
  {navBarProps ? <NavBar {...navBarProps} hasMenu={hasMenu} /> : null}
@@ -16,6 +16,7 @@ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
16
16
  import { useScreenAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks";
17
17
 
18
18
  import { useCallbackActions } from "@applicaster/zapp-react-native-utils/zappFrameworkUtils/HookCallback/useCallbackActions";
19
+ import { ScreenResultCallback } from "@applicaster/zapp-react-native-utils/zappFrameworkUtils/HookCallback/callbackNavigationAction";
19
20
 
20
21
  const logger = componentsLogger.addSubsystem("ScreenResolver");
21
22
 
@@ -26,6 +27,7 @@ type Props = {
26
27
  feedId?: string;
27
28
  feedTitle?: string;
28
29
  focused?: boolean;
30
+ resultCallback?: ScreenResultCallback;
29
31
  parentFocus?: {
30
32
  nextFocusDown?: React.Ref<any>;
31
33
  nextFocusRight?: React.Ref<any>;
@@ -61,13 +63,17 @@ export function ScreenResolverComponent(props: Props) {
61
63
 
62
64
  React.useEffect(() => {
63
65
  setScreenContext(rivers[screenId]);
64
- }, [screenId]);
66
+ }, [rivers, screenId, setScreenContext]);
65
67
 
66
- const callbackAction = useCallbackActions(
68
+ const parentCallback = props.resultCallback;
69
+
70
+ const screenAction = useCallbackActions(
67
71
  hookPlugin || screenData,
68
72
  screenData.callback
69
73
  );
70
74
 
75
+ const callbackAction = parentCallback || screenAction;
76
+
71
77
  const ScreenPlugin =
72
78
  findPluginByType(screenType, plugins, { skipWarning: true }) ||
73
79
  findPluginByIdentifier(screenType, plugins) ||
@@ -0,0 +1,23 @@
1
+ import { ReplaySubject } from "rxjs";
2
+ import { pairwise, filter, first } from "rxjs/operators";
3
+
4
+ // we are interested in last 2 events, because we wait transition from <false> to <true>
5
+ const screenRevealManagerSubject$ = new ReplaySubject<boolean>(2);
6
+
7
+ export const emitScreenRevealManagerIsReadyToShow = () => {
8
+ screenRevealManagerSubject$.next(true);
9
+ };
10
+
11
+ export const emitScreenRevealManagerIsNotReadyToShow = () => {
12
+ screenRevealManagerSubject$.next(false);
13
+ };
14
+
15
+ export const waitUntilScreenRevealManagerIsReady = () => {
16
+ return screenRevealManagerSubject$.pipe(
17
+ pairwise(), // emit consecutive pairs: [prev, curr]
18
+ filter(
19
+ ([previousIsReady, currentIsReady]) => !previousIsReady && currentIsReady
20
+ ), // detect transition from not_ready to ready
21
+ first()
22
+ );
23
+ };
@@ -1,21 +1,23 @@
1
1
  import * as React from "react";
2
- import { Animated } from "react-native";
2
+ import { Animated, StyleSheet } from "react-native";
3
3
  import { isFirstComponentScreenPicker } from "@applicaster/zapp-react-native-utils/componentsUtils";
4
- import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
4
  import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
5
+ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
6
6
 
7
7
  import { ScreenRevealManager } from "./ScreenRevealManager";
8
+ import {
9
+ emitScreenRevealManagerIsReadyToShow,
10
+ emitScreenRevealManagerIsNotReadyToShow,
11
+ } from "./utils";
8
12
 
9
- const flex = platformSelect({
10
- tvos: 1,
11
- android_tv: 1,
12
- web: undefined,
13
- samsung_tv: undefined,
14
- lg_tv: undefined,
15
- default: undefined,
13
+ const styles = StyleSheet.create({
14
+ container: {
15
+ ...StyleSheet.absoluteFillObject,
16
+ position: "absolute",
17
+ },
16
18
  });
17
19
 
18
- export const TIMEOUT = 500; // 500 ms
20
+ export const TIMEOUT = 300; // 300 ms
19
21
 
20
22
  const HIDDEN = 0; // opacity = 0
21
23
 
@@ -29,29 +31,48 @@ export const withScreenRevealManager = (Component) => {
29
31
  return function WithScreenRevealManager(props: Props) {
30
32
  const { componentsToRender } = props;
31
33
 
32
- const [isReadyToShow, setIsReadyToShow] = React.useState(false);
34
+ const [isContentReadyToBeShown, setIsContentReadyToBeShown] =
35
+ React.useState(false);
33
36
 
34
- const handleSetIsReadyToShow = React.useCallback(() => {
35
- setIsReadyToShow(true);
37
+ const [isShowOverlay, setIsShowOverlay] = React.useState(true);
38
+
39
+ const theme = useTheme<BaseThemePropertiesTV>();
40
+
41
+ const handleSetIsContentReadyToBeShown = React.useCallback(() => {
42
+ setIsContentReadyToBeShown(true);
36
43
  }, []);
37
44
 
38
45
  const managerRef = useRefWithInitialValue<ScreenRevealManager>(
39
- () => new ScreenRevealManager(componentsToRender, handleSetIsReadyToShow)
46
+ () =>
47
+ new ScreenRevealManager(
48
+ componentsToRender,
49
+ handleSetIsContentReadyToBeShown
50
+ )
40
51
  );
41
52
 
42
53
  const opacityRef = useRefWithInitialValue<Animated.Value>(
43
- () => new Animated.Value(HIDDEN)
54
+ () => new Animated.Value(SHOWN)
44
55
  );
45
56
 
46
57
  React.useEffect(() => {
47
- if (isReadyToShow) {
58
+ if (!isContentReadyToBeShown) {
59
+ emitScreenRevealManagerIsNotReadyToShow();
60
+ } else {
61
+ emitScreenRevealManagerIsReadyToShow();
62
+ }
63
+ }, [isContentReadyToBeShown]);
64
+
65
+ React.useEffect(() => {
66
+ if (isContentReadyToBeShown) {
48
67
  Animated.timing(opacityRef.current, {
49
- toValue: SHOWN,
68
+ toValue: HIDDEN,
50
69
  duration: TIMEOUT,
51
70
  useNativeDriver: true,
52
- }).start();
71
+ }).start(() => {
72
+ setIsShowOverlay(false);
73
+ });
53
74
  }
54
- }, [isReadyToShow]);
75
+ }, [isContentReadyToBeShown]);
55
76
 
56
77
  if (isFirstComponentScreenPicker(componentsToRender)) {
57
78
  // for screen-picker with have additional internal ComponentsMap, no need to add this wrapper
@@ -59,10 +80,7 @@ export const withScreenRevealManager = (Component) => {
59
80
  }
60
81
 
61
82
  return (
62
- <Animated.View
63
- style={{ opacity: opacityRef.current, flex }}
64
- testID="animated-component"
65
- >
83
+ <>
66
84
  <Component
67
85
  {...props}
68
86
  initialNumberToLoad={
@@ -73,7 +91,19 @@ export const withScreenRevealManager = (Component) => {
73
91
  }
74
92
  onLoadFailedFromScreenRevealManager={managerRef.current.onLoadFailed}
75
93
  />
76
- </Animated.View>
94
+ {isShowOverlay ? (
95
+ <Animated.View
96
+ style={[
97
+ styles.container,
98
+ {
99
+ opacity: opacityRef.current,
100
+ backgroundColor: theme.app_background_color,
101
+ },
102
+ ]}
103
+ testID="animated-component"
104
+ />
105
+ ) : null}
106
+ </>
77
107
  );
78
108
  };
79
109
  };
@@ -42,6 +42,8 @@ type Props = {
42
42
  children: React.ReactNode;
43
43
  };
44
44
 
45
+ const activeOffsetY = [-5, 5] as [number, number];
46
+
45
47
  export const AnimatedScrollModalComponent = ({ children }: Props) => {
46
48
  const {
47
49
  isActiveGesture,
@@ -372,9 +374,9 @@ export const AnimatedScrollModalComponent = ({ children }: Props) => {
372
374
  ref={panHandlerRef}
373
375
  simultaneousHandlers={[scrollRef, tapHandlerRef]}
374
376
  shouldCancelWhenOutside={isMaximizedModal}
377
+ activeOffsetY={activeOffsetY}
375
378
  onGestureEvent={onGestureEvent}
376
379
  onHandlerStateChange={onHandlerStateChange}
377
- activeOffsetY={[-5, 5]}
378
380
  >
379
381
  <Animated.View>
380
382
  <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";
@@ -69,11 +69,10 @@ export const PlayerDetails = ({
69
69
  const screenData = useTargetScreenData(entry);
70
70
  const insets = useSafeAreaInsets();
71
71
 
72
- const extraTabletStyles = !isAudioPlayer
73
- ? isTabletLandscape
72
+ const extraTabletStyles =
73
+ !isAudioPlayer && isTabletLandscape
74
74
  ? { marginTop: -insets.top, paddingTop: insets.top + 20 }
75
- : { marginTop: -8, paddingTop: -8 }
76
- : {};
75
+ : null;
77
76
 
78
77
  // Animation setup
79
78
  const translateY = useRef(new Animated.Value(50)).current;
@@ -129,6 +128,7 @@ export const PlayerDetails = ({
129
128
  riverId={screenData.id}
130
129
  feed={screenData?.data?.source}
131
130
  riverComponents={screenData.ui_components}
131
+ isScreenWrappedInContainer
132
132
  />
133
133
  ) : null}
134
134
  </Animated.View>
@@ -1,6 +1,5 @@
1
1
  import React from "react";
2
- import { StyleSheet } from "react-native";
3
- import { SafeAreaView } from "react-native-safe-area-context";
2
+ import { StyleSheet, View } from "react-native";
4
3
 
5
4
  import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
6
5
  import CloseButton from "./Buttons/CloseButton";
@@ -45,15 +44,14 @@ const BarView = ({ screenStyles, barState = defaultState }: Props) => {
45
44
  );
46
45
 
47
46
  return (
48
- <SafeAreaView
49
- edges={["top", "left", "right"]}
47
+ <View
50
48
  style={[style.view, isRTL ? style.rtlStyle : {}]}
51
- testID="BarView-safeAreaView"
49
+ testID="BarView-Container"
52
50
  >
53
51
  {backButton}
54
52
  <BarTitle title={barState.title} screenStyles={screenStyles} />
55
53
  {closeButton}
56
- </SafeAreaView>
54
+ </View>
57
55
  );
58
56
  };
59
57
 
@@ -46,7 +46,7 @@ describe("BarView", () => {
46
46
  <BarView screenStyles={screenStyles} barState={barState} />
47
47
  );
48
48
 
49
- expect(getByTestId("BarView-safeAreaView").props.style).toContainEqual({
49
+ expect(getByTestId("BarView-Container").props.style).toContainEqual({
50
50
  transform: [{ scaleX: -1 }],
51
51
  });
52
52
  });
@@ -58,7 +58,7 @@ describe("BarView", () => {
58
58
  <BarView screenStyles={screenStyles} barState={barState} />
59
59
  );
60
60
 
61
- expect(getByTestId("BarView-safeAreaView").props.style).not.toContainEqual({
61
+ expect(getByTestId("BarView-Container").props.style).not.toContainEqual({
62
62
  transform: [{ scaleX: -1 }],
63
63
  });
64
64
  });
@@ -24,16 +24,12 @@ export const getDatasourceUrl: (
24
24
  ) => {
25
25
  const { source, mapping } = R.propOr({}, ["data"], component);
26
26
 
27
- if (mapping) {
28
- const contexts = {
29
- entry: entryContext,
30
- screen: screenContext || screenData,
31
- search: getSearchContext(searchContext, mapping),
32
- };
27
+ const contexts = {
28
+ entry: entryContext,
29
+ screen: screenContext || screenData,
30
+ search: getSearchContext(searchContext, mapping),
31
+ };
33
32
 
34
- return getInflatedDataSourceUrl({ source, mapping, contexts });
35
- }
36
-
37
- return source;
33
+ return getInflatedDataSourceUrl({ source, mapping, contexts });
38
34
  }
39
35
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-ui-components",
3
- "version": "15.0.0-rc.2",
3
+ "version": "15.0.0-rc.20",
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",
@@ -28,10 +28,10 @@
28
28
  },
29
29
  "homepage": "https://github.com/applicaster/quickbrick#readme",
30
30
  "dependencies": {
31
- "@applicaster/applicaster-types": "15.0.0-rc.2",
32
- "@applicaster/zapp-react-native-bridge": "15.0.0-rc.2",
33
- "@applicaster/zapp-react-native-redux": "15.0.0-rc.2",
34
- "@applicaster/zapp-react-native-utils": "15.0.0-rc.2",
31
+ "@applicaster/applicaster-types": "15.0.0-rc.20",
32
+ "@applicaster/zapp-react-native-bridge": "15.0.0-rc.20",
33
+ "@applicaster/zapp-react-native-redux": "15.0.0-rc.20",
34
+ "@applicaster/zapp-react-native-utils": "15.0.0-rc.20",
35
35
  "promise": "^8.3.0",
36
36
  "url": "^0.11.0",
37
37
  "uuid": "^3.3.2"