@applicaster/zapp-react-native-ui-components 15.0.0-alpha.2239032089 → 15.0.0-alpha.2413435535

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 (53) hide show
  1. package/Components/AnimatedInOut/index.tsx +69 -26
  2. package/Components/Cell/Cell.tsx +8 -3
  3. package/Components/Cell/FocusableWrapper.tsx +44 -0
  4. package/Components/Cell/TvOSCellComponent.tsx +92 -17
  5. package/Components/Focusable/Focusable.tsx +2 -4
  6. package/Components/Focusable/FocusableTvOS.tsx +1 -5
  7. package/Components/FocusableGroup/FocusableTvOS.tsx +0 -5
  8. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +9 -11
  9. package/Components/HandlePlayable/HandlePlayable.tsx +14 -65
  10. package/Components/HandlePlayable/const.ts +3 -0
  11. package/Components/HandlePlayable/utils.ts +74 -0
  12. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +16 -1
  13. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +30 -2
  14. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +12 -6
  15. package/Components/MasterCell/DefaultComponents/Text/index.tsx +8 -8
  16. package/Components/MasterCell/index.tsx +2 -0
  17. package/Components/PlayerContainer/PlayerContainer.tsx +1 -16
  18. package/Components/PlayerImageBackground/index.tsx +3 -22
  19. package/Components/River/TV/River.tsx +3 -9
  20. package/Components/River/TV/index.tsx +3 -3
  21. package/Components/River/TV/withTVEventHandler.tsx +27 -0
  22. package/Components/Screen/TV/hooks/useInitialFocus.ts +14 -4
  23. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  24. package/Components/Screen/index.tsx +22 -5
  25. package/Components/ScreenResolver/index.tsx +8 -2
  26. package/Components/ScreenRevealManager/utils/index.ts +23 -0
  27. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +54 -24
  28. package/Components/Tabs/TV/Tabs.tsx +20 -3
  29. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
  30. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +114 -171
  31. package/Components/VideoModal/ModalAnimation/index.ts +2 -13
  32. package/Components/VideoModal/ModalAnimation/utils.ts +1 -327
  33. package/Components/VideoModal/PlayerWrapper.tsx +14 -88
  34. package/Components/VideoModal/VideoModal.tsx +1 -5
  35. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
  36. package/Components/VideoModal/hooks/useModalSize.ts +10 -5
  37. package/Components/VideoModal/playerWrapperStyle.ts +70 -0
  38. package/Components/VideoModal/playerWrapperUtils.ts +91 -0
  39. package/Components/VideoModal/utils.ts +7 -0
  40. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +4 -6
  41. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  42. package/Decorators/RiverFeedLoader/utils/getDatasourceUrl.ts +6 -10
  43. package/events/index.ts +0 -2
  44. package/index.d.ts +7 -0
  45. package/package.json +5 -5
  46. package/Components/River/TV/withFocusableGroupForContent.tsx +0 -60
  47. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +0 -60
  48. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +0 -417
  49. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +0 -294
  50. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +0 -176
  51. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +0 -93
  52. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +0 -500
  53. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +0 -108
@@ -1,8 +1,8 @@
1
+ import * as React from "react";
1
2
  import {
2
3
  BorderContainerView,
3
4
  getBorderPadding, // Export for testing (using a double underscore prefix is a common convention)
4
5
  } from "../index";
5
- import * as React from "react";
6
6
  import { render } from "@testing-library/react-native";
7
7
  import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
8
8
  import { View } from "react-native";
@@ -11,6 +11,15 @@ jest.mock("@applicaster/zapp-react-native-utils/numberUtils", () => ({
11
11
  toNumberWithDefaultZero: jest.fn((value) => Number(value) || 0),
12
12
  }));
13
13
 
14
+ jest.mock(
15
+ "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks",
16
+ () => ({
17
+ useAccessibilityManager: jest.fn(() => ({
18
+ addHeading: jest.fn(),
19
+ })),
20
+ })
21
+ );
22
+
14
23
  describe("BorderContainerView", () => {
15
24
  describe("getBorderPadding", () => {
16
25
  it("returns 0 for inside", () => {
@@ -42,6 +51,8 @@ describe("BorderContainerView", () => {
42
51
  };
43
52
 
44
53
  const borderPosition = null;
54
+ const mockEntry = { id: "test-entry" } as ZappEntry;
55
+ const mockHasFocusableInside = jest.fn(() => false);
45
56
 
46
57
  const { queryByTestId } = render(
47
58
  <BorderContainerView
@@ -52,6 +63,10 @@ describe("BorderContainerView", () => {
52
63
  borderPaddingRight={toNumberWithDefaultZero(padding.paddingRight)}
53
64
  borderPaddingBottom={toNumberWithDefaultZero(padding.paddingBottom)}
54
65
  borderPaddingLeft={toNumberWithDefaultZero(padding.paddingLeft)}
66
+ hasFocusableInside={mockHasFocusableInside}
67
+ entry={mockEntry}
68
+ state="focused"
69
+ hasTextLabels={false}
55
70
  >
56
71
  <View testID="child" />
57
72
  </BorderContainerView>
@@ -1,10 +1,16 @@
1
- import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
2
- import * as React from "react";
1
+ import React, { useMemo, useContext, useEffect } from "react";
3
2
  import { ImageStyle, StyleSheet, View, ViewStyle } from "react-native";
3
+ import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
4
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
5
+ import { MeasurementPortalContext } from "../../../MeasurmentsPortal/MeasurementsPortal";
4
6
 
5
7
  type BorderPosition = "inside" | "outside" | "center";
6
8
 
7
9
  interface Props {
10
+ hasFocusableInside: (entry: ZappEntry) => boolean;
11
+ entry: ZappEntry;
12
+ state: CellState;
13
+ hasTextLabels: boolean;
8
14
  style: ImageStyle | ViewStyle;
9
15
  borderPosition: BorderPosition;
10
16
  borderPaddingTop: number;
@@ -118,8 +124,30 @@ export const BorderContainerView = (props: Props) => {
118
124
  borderPaddingLeft,
119
125
  style,
120
126
  children,
127
+ hasFocusableInside,
128
+ entry,
129
+ state,
130
+ hasTextLabels,
121
131
  } = props;
122
132
 
133
+ const accessibilityManager = useAccessibilityManager();
134
+ const isMeasurement = useContext(MeasurementPortalContext);
135
+
136
+ const isImageOnlyCell = useMemo(
137
+ () =>
138
+ !hasFocusableInside(entry) &&
139
+ !hasTextLabels &&
140
+ state === "focused" &&
141
+ !isMeasurement,
142
+ [hasFocusableInside, entry, hasTextLabels, state, isMeasurement]
143
+ );
144
+
145
+ useEffect(() => {
146
+ if (isImageOnlyCell && entry?.title) {
147
+ accessibilityManager.addHeading(String(entry.title));
148
+ }
149
+ }, [isImageOnlyCell, entry?.title]);
150
+
123
151
  const padding =
124
152
  borderPosition === "outside"
125
153
  ? {
@@ -9,6 +9,7 @@ import { useTrackCurrentAutoScrollingElement } from "@applicaster/zapp-react-nat
9
9
  import { useUIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
10
10
  import { getPropComponentType } from "@applicaster/zapp-react-native-utils/cellUtils";
11
11
  import { findPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
12
+ import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
12
13
 
13
14
  type LiveImageProps = {
14
15
  item: ZappEntry;
@@ -108,8 +109,7 @@ const prepareEntry = (entry) => {
108
109
  };
109
110
  }
110
111
 
111
- const previewPlayback =
112
- entry.extensions?.["brightcove"]?.["preview_playback"];
112
+ const previewPlayback = entry.extensions?.brightcove?.preview_playback;
113
113
 
114
114
  if (previewPlayback) {
115
115
  return {
@@ -117,14 +117,14 @@ const prepareEntry = (entry) => {
117
117
  extensions: {
118
118
  ...entry.extensions,
119
119
  brightcove: {
120
- ...entry?.extensions?.["brightcove"],
120
+ ...entry?.extensions?.brightcove,
121
121
  video_id: previewPlayback,
122
122
  },
123
123
  },
124
124
  };
125
125
  }
126
126
 
127
- if (entry.extensions?.["brightcove"]?.["video_id"]) {
127
+ if (entry.extensions?.brightcove?.video_id) {
128
128
  return entry;
129
129
  }
130
130
 
@@ -174,7 +174,7 @@ const getPlayerConfig = (player_screen_id, actionIdentifier) => {
174
174
  // TODO: Add more dict if needed from the screen component, styles, data etc
175
175
  return {
176
176
  playerPluginId: playerScreen?.type ?? DEFAULT_PLAYER_IDENTIFIER,
177
- screenConfig: playerScreen?.["general"],
177
+ screenConfig: playerScreen?.general,
178
178
  };
179
179
  }
180
180
 
@@ -206,6 +206,11 @@ const LiveImageComponent = (props: LiveImageProps) => {
206
206
  state,
207
207
  } = props;
208
208
 
209
+ const accessibilityManager = useAccessibilityManager();
210
+
211
+ const isScreenReaderEnabled =
212
+ accessibilityManager.accessibilityManagerState.screenReaderEnabled;
213
+
209
214
  const component = useUIComponentContext();
210
215
 
211
216
  // Fix for blinking on state change
@@ -239,7 +244,8 @@ const LiveImageComponent = (props: LiveImageProps) => {
239
244
  getFocusedState(state, componentType, isCurrentlyFocused) &&
240
245
  playableEntry &&
241
246
  cellUUID &&
242
- isSupportedTVForLiveImage();
247
+ isSupportedTVForLiveImage() &&
248
+ !isScreenReaderEnabled;
243
249
 
244
250
  return (
245
251
  <>
@@ -52,14 +52,14 @@ const _Text = ({
52
52
  : textTransform(transformText, _label);
53
53
 
54
54
  React.useLayoutEffect(() => {
55
- // For FocusableCells with action buttons
56
- if (otherProps.state) {
57
- if (otherProps.state === "focused" && cellFocused === true) {
58
- accessibilityManager.addHeading(textLabel);
59
- }
60
- } else {
61
- if (cellFocused === true) {
62
- accessibilityManager.addHeading(textLabel);
55
+ if (cellFocused) {
56
+ switch (otherProps.state) {
57
+ case "focused":
58
+ accessibilityManager.addHeading(textLabel);
59
+ break;
60
+ case "focused_selected":
61
+ accessibilityManager.addHeading(`${textLabel}, Selected`);
62
+ break;
63
63
  }
64
64
  }
65
65
  }, [cellFocused, otherProps.state, textLabel]);
@@ -103,6 +103,8 @@ export function masterCellBuilder({
103
103
  wrapperRef,
104
104
  cellUUID,
105
105
  skipButtons,
106
+ hasFocusableInside,
107
+ entry: item,
106
108
  })
107
109
  );
108
110
 
@@ -56,11 +56,6 @@ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
56
56
  import { usePlayNextOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayNextOverlay";
57
57
  import { PlayNextState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/OverlayObserver/OverlaysObserver";
58
58
 
59
- import {
60
- PlayerAnimationStateEnum,
61
- useModalAnimationContext,
62
- } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
63
-
64
59
  import {
65
60
  PlayerNativeCommandTypes,
66
61
  PlayerNativeSendCommand,
@@ -248,9 +243,6 @@ const PlayerContainerComponent = (props: Props) => {
248
243
  const screenData = useTargetScreenData(item);
249
244
  const { setVisible: showNavBar } = useSetNavbarState();
250
245
 
251
- const { isActiveGesture, startComponentsAnimation, setPlayerAnimationState } =
252
- useModalAnimationContext();
253
-
254
246
  const playerEvent = (event, ...args) => {
255
247
  playerManager.invokeHandler(event, ...args);
256
248
  };
@@ -482,8 +474,6 @@ const PlayerContainerComponent = (props: Props) => {
482
474
  if (isModal && mode === VideoModalMode.MAXIMIZED) {
483
475
  if (disableMiniPlayer) {
484
476
  navigator.closeVideoModal();
485
- } else {
486
- setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
487
477
  }
488
478
  }
489
479
 
@@ -680,11 +670,7 @@ const PlayerContainerComponent = (props: Props) => {
680
670
  autoplay={true}
681
671
  controls={false}
682
672
  disableCastAction={disableCastAction}
683
- docked={
684
- navigator.isVideoModalDocked() &&
685
- !startComponentsAnimation &&
686
- !isActiveGesture
687
- }
673
+ docked={navigator.isVideoModalDocked()}
688
674
  entry={item}
689
675
  fullscreen={mode === VideoModalMode.FULLSCREEN}
690
676
  inline={inline}
@@ -702,7 +688,6 @@ const PlayerContainerComponent = (props: Props) => {
702
688
  setNextVideoPreloadThresholdPercentage={
703
689
  setNextVideoPreloadThresholdPercentage
704
690
  }
705
- startComponentsAnimation={startComponentsAnimation}
706
691
  >
707
692
  {renderApplePlayer(applePlayerProps)}
708
693
  </Player>
@@ -2,12 +2,6 @@ import React, { PropsWithChildren } from "react";
2
2
  import { ImageBackground, View } from "react-native";
3
3
 
4
4
  import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
5
- import {
6
- AnimationComponent,
7
- ComponentAnimationType,
8
- useModalAnimationContext,
9
- PlayerAnimationStateEnum,
10
- } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
11
5
 
12
6
  type Props = PropsWithChildren<{
13
7
  entry: ZappEntry;
@@ -25,30 +19,17 @@ const PlayerImageBackgroundComponent = ({
25
19
  style,
26
20
  imageStyle,
27
21
  imageKey,
28
- defaultImageDimensions,
29
22
  }: Props) => {
30
23
  const source = React.useMemo(
31
24
  () => ({ uri: imageSrcFromMediaItem(entry, [imageKey]) }),
32
25
  [imageKey, entry]
33
26
  );
34
27
 
35
- const { playerAnimationState } = useModalAnimationContext();
36
-
37
28
  if (!source) return <>{children}</>;
38
29
 
39
30
  return (
40
- <View
41
- style={
42
- playerAnimationState === PlayerAnimationStateEnum.maximize
43
- ? defaultImageDimensions
44
- : style
45
- }
46
- >
47
- <AnimationComponent
48
- style={style}
49
- animationType={ComponentAnimationType.player}
50
- additionalData={defaultImageDimensions}
51
- >
31
+ <View style={style}>
32
+ <View style={style}>
52
33
  <ImageBackground
53
34
  resizeMode="cover"
54
35
  style={imageSize}
@@ -57,7 +38,7 @@ const PlayerImageBackgroundComponent = ({
57
38
  >
58
39
  {children}
59
40
  </ImageBackground>
60
- </AnimationComponent>
41
+ </View>
61
42
  </View>
62
43
  );
63
44
  };
@@ -4,7 +4,6 @@ import * as React from "react";
4
4
  import { Text } from "react-native";
5
5
  import * as R from "ramda";
6
6
 
7
- import { isNil } from "@applicaster/zapp-react-native-utils/utils";
8
7
  import { GeneralContentScreen } from "../../GeneralContentScreen";
9
8
  import { ScreenResolver } from "@applicaster/zapp-react-native-ui-components/Components/ScreenResolver";
10
9
  import { utilsLogger } from "@applicaster/zapp-react-native-utils/logger";
@@ -25,7 +24,6 @@ type Props = {
25
24
  isInsideContainer?: boolean;
26
25
  extraAnchorPointYOffset: number;
27
26
  river?: ZappRiver | ZappEntry;
28
- groupId: string;
29
27
  };
30
28
 
31
29
  export const River = (props: Props) => {
@@ -37,7 +35,6 @@ export const River = (props: Props) => {
37
35
  componentsMapExtraProps,
38
36
  isInsideContainer,
39
37
  extraAnchorPointYOffset,
40
- groupId,
41
38
  } = props;
42
39
 
43
40
  const { title: screenTitle, summary: screenSummary } = useNavbarState();
@@ -55,7 +52,7 @@ export const River = (props: Props) => {
55
52
  );
56
53
 
57
54
  const stringOrEmpty = (value: string | number | undefined): string =>
58
- isNil(value) ? "" : String(value);
55
+ R.isNil(value) ? "" : String(value);
59
56
 
60
57
  React.useEffect(() => {
61
58
  if (!isInsideContainer) {
@@ -95,10 +92,8 @@ export const River = (props: Props) => {
95
92
  <ScreenResolver
96
93
  screenType={river.type}
97
94
  screenId={screenId}
98
- screenData={Object.assign(river || {}, { groupId: extraData?.groupId })}
99
- componentsMapExtraProps={Object.assign(componentsMapExtraProps || {}, {
100
- groupId,
101
- })}
95
+ screenData={R.merge(river, { groupId: extraData?.groupId })}
96
+ componentsMapExtraProps={componentsMapExtraProps}
102
97
  {...extraData}
103
98
  />
104
99
  );
@@ -111,7 +106,6 @@ export const River = (props: Props) => {
111
106
  isScreenWrappedInContainer={isInsideContainer}
112
107
  extraAnchorPointYOffset={extraAnchorPointYOffset}
113
108
  componentsMapExtraProps={componentsMapExtraProps}
114
- groupId={groupId}
115
109
  />
116
110
  );
117
111
  };
@@ -1,11 +1,11 @@
1
1
  import { compose } from "ramda";
2
2
  import { River as RiverComponent } from "./River";
3
+ import { withTvEventHandler } from "./withTVEventHandler";
3
4
  import { withComponentsMapOffsetContext } from "../../../Contexts/ComponentsMapOffsetContext";
4
5
  import { withRiverDataLoader } from "./withRiverDataLoader";
5
- import { withFocusableGroupForContent } from "./withFocusableGroupForContent";
6
6
 
7
7
  export const River = compose(
8
+ withTvEventHandler,
8
9
  withComponentsMapOffsetContext,
9
- withRiverDataLoader,
10
- withFocusableGroupForContent
10
+ withRiverDataLoader
11
11
  )(RiverComponent);
@@ -0,0 +1,27 @@
1
+ /* eslint max-len: off */
2
+
3
+ import React from "react";
4
+ import { TVEventHandlerComponent } from "@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent";
5
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
6
+
7
+ export const withTvEventHandler = (Component) => {
8
+ return function WithTVEventHandler(props) {
9
+ const navigator = useNavigation();
10
+
11
+ const remoteHandler = (event) => {
12
+ const { eventType } = event;
13
+
14
+ const canGoBack = navigator.canGoBack();
15
+
16
+ if (eventType === "menu" && canGoBack) {
17
+ navigator.goBack();
18
+ }
19
+ };
20
+
21
+ return (
22
+ <TVEventHandlerComponent tvEventHandler={remoteHandler}>
23
+ <Component {...props} />
24
+ </TVEventHandlerComponent>
25
+ );
26
+ };
27
+ };
@@ -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
+ };