@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5895113034 → 12.0.0-alpha.5933014273

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 (83) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +30 -9
  3. package/Components/Cell/CellWithFocusable.tsx +23 -9
  4. package/Components/Cell/TvOSCellComponent.tsx +3 -7
  5. package/Components/Focusable/Focusable.tsx +6 -1
  6. package/Components/FocusableGroup/index.tsx +11 -2
  7. package/Components/FreezeWithCallback/Freeze.tsx +31 -0
  8. package/Components/FreezeWithCallback/LICENSE +21 -0
  9. package/Components/FreezeWithCallback/Suspender.tsx +17 -0
  10. package/Components/FreezeWithCallback/__tests__/index.test.tsx +170 -0
  11. package/Components/FreezeWithCallback/index.tsx +3 -0
  12. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  13. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  14. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  15. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  16. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  17. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  18. package/Components/Layout/TV/__tests__/index.test.tsx +20 -75
  19. package/Components/Layout/TV/index.tsx +11 -67
  20. package/Components/Layout/TV/index.web.tsx +33 -0
  21. package/Components/Layout/index.tsx +15 -11
  22. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -1
  23. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +8 -9
  24. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +17 -1
  25. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +71 -0
  26. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +20 -0
  27. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  28. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  29. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  30. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +7 -1
  31. package/Components/MasterCell/dataAdapter.ts +21 -7
  32. package/Components/MasterCell/index.tsx +5 -3
  33. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  34. package/Components/MasterCell/utils/index.ts +17 -30
  35. package/Components/Navigator/StackNavigator.tsx +57 -0
  36. package/Components/Navigator/index.ts +1 -0
  37. package/Components/PlayerContainer/PlayerContainer.tsx +5 -1
  38. package/Components/River/TV/River.tsx +0 -23
  39. package/Components/Screen/TV/hooks/index.ts +1 -0
  40. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  41. package/Components/Screen/TV/index.web.tsx +178 -0
  42. package/Components/Screen/__tests__/Screen.test.tsx +3 -49
  43. package/Components/Screen/index.tsx +55 -76
  44. package/Components/Screen/navigationHandler.ts +6 -3
  45. package/Components/Screen/orientationHandler.ts +18 -7
  46. package/Components/Screen/utils.ts +16 -0
  47. package/Components/ScreenResolver/index.tsx +9 -12
  48. package/Components/Transitioner/Scene.tsx +20 -12
  49. package/Components/Transitioner/Transitioner.tsx +40 -49
  50. package/Components/Transitioner/__tests__/Scene.test.js +7 -0
  51. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +8 -4
  52. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  53. package/Components/VideoLive/LiveImageManager.ts +25 -1
  54. package/Components/VideoLive/PlayerLiveImageComponent.tsx +52 -24
  55. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +26 -24
  56. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +2 -14
  57. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +79 -45
  58. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +47 -33
  59. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +121 -86
  60. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +32 -4
  61. package/Components/VideoModal/ModalAnimation/utils.ts +7 -4
  62. package/Components/VideoModal/PlayerWrapper.tsx +10 -10
  63. package/Components/VideoModal/VideoModal.tsx +4 -4
  64. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +80 -14
  65. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  66. package/Components/VideoModal/utils.ts +5 -2
  67. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +4 -3
  68. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  69. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  70. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  71. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  72. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  73. package/Contexts/FocusableGroupContext/index.ts +7 -0
  74. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  75. package/Contexts/ScreenDataContext/index.tsx +20 -0
  76. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  77. package/const.ts +6 -0
  78. package/index.js +3 -0
  79. package/package.json +5 -5
  80. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  81. package/Contexts/ScreenDataContext/index.ts +0 -3
  82. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  83. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
@@ -60,7 +60,7 @@ export function ScreenLayoutContextProvider(props: {
60
60
  initialState
61
61
  );
62
62
 
63
- const screeenLayoutValue = React.useMemo(
63
+ const screenLayoutValue = React.useMemo(
64
64
  () => ({
65
65
  ...screenLayout,
66
66
  setScreenLayout,
@@ -69,7 +69,7 @@ export function ScreenLayoutContextProvider(props: {
69
69
  );
70
70
 
71
71
  return (
72
- <ScreenLayoutContext.Provider value={screeenLayoutValue}>
72
+ <ScreenLayoutContext.Provider value={screenLayoutValue}>
73
73
  {props.children}
74
74
  </ScreenLayoutContext.Provider>
75
75
  );
@@ -1,14 +1,27 @@
1
1
  import * as React from "react";
2
2
  import { View } from "react-native";
3
- import { render } from "@testing-library/react-native";
3
+ import { render, waitFor } from "@testing-library/react-native";
4
+ import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
4
5
 
5
6
  import { ScreenLayoutContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenLayoutContext";
6
7
 
7
8
  const navigator_mock = {
8
9
  currentRoute: "/river/A1234",
9
10
  screenData: {},
11
+ activeRiver: {
12
+ navigations: {},
13
+ id: "A1234",
14
+ home: false,
15
+ },
10
16
  };
11
17
 
18
+ jest.mock("@applicaster/zapp-react-native-utils/navigationUtils", () => ({
19
+ ...jest.requireActual<any>(
20
+ "@applicaster/zapp-react-native-utils/navigationUtils"
21
+ ),
22
+ findMenuPlugin: () => ({}),
23
+ }));
24
+
12
25
  jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
13
26
  useRiverInitialState: () => ({}),
14
27
  }));
@@ -17,13 +30,13 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
17
30
  useNavigation: () => navigator_mock,
18
31
  }));
19
32
 
20
- jest.mock("../../../River/useScreenConfiguration.ts", () => ({
33
+ jest.mock("../../../River/useScreenConfiguration", () => ({
21
34
  useScreenConfiguration: () => ({}),
22
35
  }));
23
36
 
24
- jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
25
- useCurrentScreenData: jest.fn(() => ({})),
26
- }));
37
+ jest.mock(
38
+ "@applicaster/zapp-react-native-utils/reactHooks/screen/useCurrentScreenData"
39
+ );
27
40
 
28
41
  jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
29
42
  useTheme: () => ({}),
@@ -44,42 +57,39 @@ describe("ScreenContainer", () => {
44
57
  const setScreenLayout = () => {};
45
58
 
46
59
  const wrapper = ({ children }) => (
47
- <ScreenLayoutContext.Provider value={{ ...screenLayout, setScreenLayout }}>
48
- {children}
49
- </ScreenLayoutContext.Provider>
60
+ <WrappedWithProviders>
61
+ <ScreenLayoutContext.Provider
62
+ value={{ ...screenLayout, setScreenLayout }}
63
+ >
64
+ <ScreenContainer NavBar={View}>{children}</ScreenContainer>
65
+ </ScreenLayoutContext.Provider>
66
+ </WrappedWithProviders>
50
67
  );
51
68
 
52
69
  const TestComp = () => <View testID="test-component" />;
53
70
 
54
71
  it("renders", () => {
55
- const { toJSON } = render(
56
- <ScreenContainer>
57
- <TestComp />
58
- </ScreenContainer>,
59
- { wrapper }
60
- );
72
+ const { toJSON } = render(<TestComp />, { wrapper });
61
73
 
62
74
  expect(toJSON()).toMatchSnapshot();
63
75
  });
64
76
 
65
- it("renders passed component", () => {
66
- const { getByTestId } = render(
67
- <ScreenContainer>
68
- <TestComp />
69
- </ScreenContainer>,
70
- { wrapper }
71
- );
77
+ it("renders passed component", async () => {
78
+ const { getByTestId } = render(<TestComp />, { wrapper });
79
+
80
+ await waitFor(() => {
81
+ return expect(getByTestId("screen-container")).toBeDefined();
82
+ });
72
83
 
73
84
  expect(getByTestId("test-component")).toBeDefined();
74
85
  });
75
86
 
76
- it("sets margins provided by the theme", () => {
77
- const { getByTestId } = render(
78
- <ScreenContainer>
79
- <TestComp />
80
- </ScreenContainer>,
81
- { wrapper }
82
- );
87
+ it("sets margins provided by the theme", async () => {
88
+ const { getByTestId } = render(<TestComp />, { wrapper });
89
+
90
+ await waitFor(() => {
91
+ return expect(getByTestId("screen-container")).toBeDefined();
92
+ });
83
93
 
84
94
  const screen = getByTestId("screen-container");
85
95
  expect(screen.props.style).toMatchSnapshot();
@@ -1,28 +1,40 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`ScreenContainer renders 1`] = `
4
- <View
4
+ <RNCSafeAreaProvider
5
+ onInsetsChange={[Function]}
5
6
  style={
6
7
  Array [
7
8
  Object {
8
9
  "flex": 1,
9
- "height": "100%",
10
- "width": "100%",
11
- },
12
- Object {
13
- "backgroundColor": "transparent",
14
- "marginBottom": 10,
15
- "paddingBottom": 0,
16
- "paddingTop": 0,
17
10
  },
11
+ undefined,
18
12
  ]
19
13
  }
20
- testID="screen-container"
21
14
  >
22
15
  <View
23
- testID="test-component"
24
- />
25
- </View>
16
+ style={
17
+ Array [
18
+ Object {
19
+ "position": "absolute",
20
+ "top": 0,
21
+ "width": 1920,
22
+ "zIndex": 10,
23
+ },
24
+ Object {
25
+ "maxHeight": 10,
26
+ },
27
+ ]
28
+ }
29
+ testID="nav-bar-container"
30
+ >
31
+ <View
32
+ isVisible={true}
33
+ menuPlugin={Object {}}
34
+ navigationProps={null}
35
+ />
36
+ </View>
37
+ </RNCSafeAreaProvider>
26
38
  `;
27
39
 
28
40
  exports[`ScreenContainer sets margins provided by the theme 1`] = `
@@ -2,58 +2,18 @@
2
2
 
3
3
  exports[`Layout TV renders 1`] = `
4
4
  <View
5
- style={
6
- Object {
7
- "flex": 1,
8
- "height": 1080,
9
- "width": 1920,
10
- }
11
- }
12
- testID="layout-container"
5
+ excludeFromFocusSearching={true}
6
+ id="/river/A1234"
7
+ preferredFocus={true}
13
8
  >
14
9
  <View
15
- backgroundImageUrl="http://applicaster.com"
16
- testID="background-component"
17
- >
18
- <View
19
- style={
20
- Array [
21
- Object {
22
- "position": "absolute",
23
- "top": 0,
24
- "width": 1920,
25
- "zIndex": 10,
26
- },
27
- Object {
28
- "maxHeight": 10,
29
- },
30
- ]
10
+ Components={
11
+ Object {
12
+ "Background": [Function],
13
+ "NavBar": [Function],
31
14
  }
32
- testID="nav-bar-container"
33
- >
34
- <View
35
- isVisible={true}
36
- menuPlugin={
37
- Object {
38
- "module": [Function],
39
- "name": "resolved-menu-plug",
40
- "type": "menu",
41
- }
42
- }
43
- navigationProps={
44
- Object {
45
- "assets": undefined,
46
- "home": undefined,
47
- "nav_items": undefined,
48
- "rules": undefined,
49
- "selected": undefined,
50
- "styles": undefined,
51
- "title": "",
52
- }
53
- }
54
- testID="nav-bar-component"
55
- />
56
- </View>
57
- </View>
15
+ }
16
+ route="/river/A1234"
17
+ />
58
18
  </View>
59
19
  `;
@@ -1,79 +1,49 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
2
+ import { omit } from "ramda";
3
3
  import { View } from "react-native";
4
4
  import { render } from "@testing-library/react-native";
5
5
  import { Provider } from "react-redux";
6
- import { createStore } from "redux";
7
6
  import { NavigationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NavigationContext";
7
+ import configureStore from "redux-mock-store";
8
8
 
9
- import { ThemeContext } from "@applicaster/zapp-react-native-utils/theme";
9
+ const withoutChildren = omit(["children"]);
10
10
 
11
- jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
12
- useRiverInitialState: () => ({}),
11
+ jest.mock("../../../Screen/TV/index.web", () => ({
12
+ Screen: View,
13
13
  }));
14
14
 
15
- jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen");
15
+ const Layout = require("../index.web").default;
16
16
 
17
- const Layout = require("..").default;
18
-
19
- const ResolvedMenuPlug = () => <View testID="resolved-menu-plug" />;
20
-
21
- const plugins = [
22
- {
23
- module: ResolvedMenuPlug,
24
- type: "menu",
25
- name: "resolved-menu-plug",
26
- },
27
- ];
28
-
29
- const bgImage = "http://applicaster.com";
30
- const isNavBarVisible = true;
31
-
32
- const store = createStore(() => ({
17
+ const mockStore = configureStore()({
33
18
  appState: { appReady: true },
34
- remoteConfigurations: { sdkAssets: { bgImage } },
35
- plugins,
36
- }));
19
+ });
37
20
 
38
21
  describe("Layout TV", () => {
39
- const themes = {
40
- myTheme: { screen_margin_top: 10, screen_margin_bottom: 10 },
41
- };
42
-
43
- const selectedThemeId = "myTheme";
44
-
45
22
  const navigator = {
46
- activeRiver: {
47
- navigations: [{ category: "menu", navigation_type: "menu" }],
48
- },
49
- canGoBack: () => false,
50
23
  currentRoute: "/river/A1234",
51
- screenData: {
52
- navigations: [{ category: "menu", navigation_type: "menu" }],
53
- },
54
- isNavBarVisible: isNavBarVisible,
24
+ canGoBack: jest.fn(),
25
+ mainStack: [
26
+ {
27
+ route: "/river/A1234",
28
+ state: { screen: {} },
29
+ },
30
+ ],
55
31
  };
56
32
 
57
33
  const wrapper = ({ children }) => (
58
- <Provider store={store}>
34
+ <Provider store={mockStore}>
59
35
  <NavigationContext.Provider value={navigator}>
60
- <ThemeContext.Provider value={{ themes, selectedThemeId }}>
61
- {children}
62
- </ThemeContext.Provider>
36
+ {children}
63
37
  </NavigationContext.Provider>
64
38
  </Provider>
65
39
  );
66
40
 
67
- const Screen = (props) => (
68
- <View testID="screen-component" {...R.omit(["children"])(props)} />
69
- );
70
-
71
41
  const NavBar = (props) => (
72
- <View testID="nav-bar-component" {...R.omit(["children"])(props)} />
42
+ <View testID="nav-bar-component" {...withoutChildren(props)} />
73
43
  );
74
44
 
75
45
  const Background = (props: Record<any, any>) => (
76
- <View testID="background-component" {...R.omit(["children"])(props)}>
46
+ <View testID="background-component" {...withoutChildren(props)}>
77
47
  {props.children}
78
48
  </View>
79
49
  );
@@ -84,33 +54,8 @@ describe("Layout TV", () => {
84
54
  };
85
55
 
86
56
  it("renders", () => {
87
- const { toJSON } = render(
88
- <Layout Components={Components}>
89
- <Screen />
90
- </Layout>,
91
- { wrapper }
92
- );
57
+ const { toJSON } = render(<Layout Components={Components} />, { wrapper });
93
58
 
94
59
  expect(toJSON()).toMatchSnapshot();
95
60
  });
96
-
97
- describe("props passed to background component", () => {
98
- it("passes correct props", () => {
99
- const { getByTestId } = render(
100
- <Layout
101
- Components={Components}
102
- ComponentsExtraProps={{
103
- Background: { testProp: true },
104
- }}
105
- >
106
- <Screen />
107
- </Layout>,
108
- { wrapper }
109
- );
110
-
111
- const bgComponent = getByTestId("background-component");
112
- expect(bgComponent.props.backgroundImageUrl).toBe(bgImage);
113
- expect(bgComponent.props.testProp).toBe(true);
114
- });
115
- });
116
61
  });
@@ -4,23 +4,14 @@ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
4
4
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
5
5
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
6
6
 
7
- import {
8
- getNavigationProps,
9
- resolveNavigationPlugin,
10
- routeIsPlayerScreen,
11
- } from "@applicaster/zapp-react-native-utils/navigationUtils";
12
-
13
7
  import { getBackgroundImageUrl } from "../utils";
14
8
  import { LayoutContainer } from "./LayoutContainer";
15
- import { NavBarContainer } from "./NavBarContainer";
16
9
  import { ScreenContainer } from "./ScreenContainer";
17
- import { TransitionOverlay } from "./TransitionOverlay";
18
10
 
19
11
  import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
20
- import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
21
12
  import { PathnameContext } from "../../../Contexts/PathnameContext";
22
- import { useNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
23
13
  import { ScreenDataContext } from "../../../Contexts/ScreenDataContext";
14
+ import { ScreenContextProvider } from "../../../Contexts/ScreenContext";
24
15
 
25
16
  type Components = {
26
17
  NavBar: React.ComponentType<any>;
@@ -41,20 +32,9 @@ type Props = {
41
32
  const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
42
33
  const navigator = useNavigation();
43
34
  const theme = useTheme();
44
- const { title, visible } = useNavbarState();
45
- const { activeRiver, currentRoute } = navigator;
46
-
47
- const {
48
- appState: { appReady = false } = {},
49
- remoteConfigurations,
50
- plugins = [],
51
- } = usePickFromState(["appState", "remoteConfigurations", "plugins"]);
52
35
 
53
- const navigationProps = getNavigationProps({
54
- navigator,
55
- title,
56
- category: "menu",
57
- });
36
+ const { appState: { appReady = false } = {}, remoteConfigurations } =
37
+ usePickFromState(["appState", "remoteConfigurations", "plugins"]);
58
38
 
59
39
  const backgroundColor = React.useMemo(() => theme.app_background_color, []);
60
40
 
@@ -63,37 +43,6 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
63
43
  [remoteConfigurations]
64
44
  );
65
45
 
66
- const menuPlugin = React.useMemo(
67
- () =>
68
- resolveNavigationPlugin({
69
- category: "menu",
70
- navigations: activeRiver?.navigations,
71
- plugins,
72
- }) as QuickBrickPlugin,
73
- []
74
- );
75
-
76
- const NavBar = React.useMemo(
77
- () => Components.NavBar || (menuPlugin.module as React.ComponentType<any>),
78
- []
79
- );
80
-
81
- const [navBarReady, setNavBarReady] = React.useState(false);
82
-
83
- const navBarContainer = (
84
- <NavBarContainer isVisible={visible} onReady={setNavBarReady}>
85
- <NavBar
86
- ref={ComponentsExtraProps?.NavBar?.ref}
87
- isVisible={visible && !routeIsPlayerScreen(currentRoute)}
88
- menuPlugin={menuPlugin}
89
- navigationProps={navigationProps}
90
- {...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
91
- />
92
- </NavBarContainer>
93
- );
94
-
95
- const isTVOS = isApplePlatform();
96
-
97
46
  if (!appReady) {
98
47
  return null;
99
48
  }
@@ -108,21 +57,16 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
108
57
  >
109
58
  <ScreenDataContext.Provider value={navigator.data}>
110
59
  <PathnameContext.Provider value={navigator.currentRoute}>
111
- {!isTVOS ? navBarContainer : null}
112
- {navBarReady ? (
113
- /**
114
- Need to re-mount this component on each navigation event
115
- It's a hack to work around the fact that we don't support
116
- correct screen rendering in our navigator (shared currentRoute state)
117
- **/
118
- <ScreenContainer>{children}</ScreenContainer>
119
- ) : null}
120
- {/** Render NavBarContainer over ScreenContainer to let tvOS native
121
- focus manager works correctly to focus nav bar items first **/}
122
- {isTVOS ? navBarContainer : null}
60
+ <ScreenContextProvider pathname={navigator.currentRoute}>
61
+ <ScreenContainer
62
+ ComponentsExtraProps={ComponentsExtraProps}
63
+ NavBar={Components.NavBar}
64
+ >
65
+ {children}
66
+ </ScreenContainer>
67
+ </ScreenContextProvider>
123
68
  </PathnameContext.Provider>
124
69
  </ScreenDataContext.Provider>
125
- <TransitionOverlay />
126
70
  </Components.Background>
127
71
  </ScreenLayoutContextProvider>
128
72
  </LayoutContainer>
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import { pathOr } from "ramda";
3
+
4
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
+
6
+ import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
7
+ import { StackNavigator } from "../../Navigator";
8
+
9
+ type Components = {
10
+ NavBar: React.ComponentType<any>;
11
+ Background: React.ComponentType<any>;
12
+ };
13
+
14
+ type Props = {
15
+ Components: Components;
16
+ };
17
+
18
+ const Layout = ({ Components }: Props) => {
19
+ const { appState } = usePickFromState(["appState"]);
20
+ const appReady = pathOr(false, ["appReady"], appState);
21
+
22
+ if (!appReady) {
23
+ return null;
24
+ }
25
+
26
+ return (
27
+ <ScreenLayoutContextProvider>
28
+ <StackNavigator Components={Components} />
29
+ </ScreenLayoutContextProvider>
30
+ );
31
+ };
32
+
33
+ export default Layout;
@@ -1,20 +1,24 @@
1
1
  import * as React from "react";
2
- import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
3
- import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
4
- import { ScreenContextProvider } from "../../Contexts/ScreenContext";
2
+ import {
3
+ isTV,
4
+ platformSelect,
5
+ } from "@applicaster/zapp-react-native-utils/reactUtils";
5
6
 
6
7
  import Mobile from "./Mobile";
7
- import TV from "./TV";
8
8
 
9
- export default function Layout(props) {
10
- const { currentRoute: pathname } = useNavigation();
9
+ import TVWeb from "./TV/index.web";
10
+ import TVDefault from "./TV/index";
11
+
12
+ const TV = platformSelect({
13
+ samsung_tv: TVWeb,
14
+ lg_tv: TVWeb,
15
+ web: TVWeb,
16
+ default: TVDefault,
17
+ });
11
18
 
19
+ export default function Layout(props) {
12
20
  if (isTV()) {
13
- return (
14
- <ScreenContextProvider pathname={pathname}>
15
- <TV {...props} />
16
- </ScreenContextProvider>
17
- );
21
+ return <TV {...props} />;
18
22
  } else {
19
23
  return <Mobile {...props} />;
20
24
  }
@@ -53,6 +53,7 @@ export function ActionButton(props: Props) {
53
53
  const { item, action, asset, flavour = "flavour_1", cellUUID } = props;
54
54
  const actionContext = useActions(action?.identifier);
55
55
 
56
+ // TODO: add subscription API for action availability
56
57
  const actionDisabled =
57
58
  typeof actionContext?.isActionAvailable === "function" &&
58
59
  !actionContext.isActionAvailable(item);
@@ -68,7 +69,7 @@ export function ActionButton(props: Props) {
68
69
  if (actionDisabled || !actionContext) return;
69
70
 
70
71
  setActionState(actionContext.initialEntryState(item));
71
- }, [actionDisabled, item?.id]);
72
+ }, [actionDisabled, item?.id, actionContext]);
72
73
 
73
74
  const onPress = useCallback(() => {
74
75
  actionContext.invokeAction(item, {
@@ -22,6 +22,10 @@ const styles = StyleSheet.create({
22
22
  borderWidth: 0,
23
23
  borderColor: COLOR_TRANSPARENT,
24
24
  },
25
+ border: {
26
+ ...StyleSheet.absoluteFillObject,
27
+ zIndex: 1,
28
+ },
25
29
  });
26
30
 
27
31
  const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
@@ -87,8 +91,9 @@ const Border = ({
87
91
 
88
92
  return (
89
93
  <View
94
+ pointerEvents="none"
90
95
  style={{
91
- ...StyleSheet.absoluteFillObject,
96
+ ...styles.border,
92
97
  right: -containerPadding - paddingRight,
93
98
  bottom: -containerPadding - paddingBottom,
94
99
  left: -containerPadding - paddingLeft,
@@ -112,9 +117,6 @@ export const BorderContainerView = (props: Props) => {
112
117
  children,
113
118
  } = props;
114
119
 
115
- const borderStyleForMissingBorder =
116
- style?.borderWidth === 0 || !style?.borderColor ? { borderRadius: 0 } : {};
117
-
118
120
  const padding =
119
121
  borderPosition === "outside"
120
122
  ? {
@@ -126,12 +128,9 @@ export const BorderContainerView = (props: Props) => {
126
128
  : {};
127
129
 
128
130
  return (
129
- <View
130
- {...props}
131
- style={[style, styles.container, borderStyleForMissingBorder]}
132
- >
133
- {children}
131
+ <View pointerEvents="box-none" {...props} style={[style, styles.container]}>
134
132
  <Border style={style} padding={padding} borderPosition={borderPosition} />
133
+ {children}
135
134
  </View>
136
135
  );
137
136
  };
@@ -4,6 +4,8 @@ import { Focusable } from "@applicaster/zapp-react-native-ui-components/Componen
4
4
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
5
  import * as R from "ramda";
6
6
  import { getXray } from "@applicaster/zapp-react-native-utils/logger";
7
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
8
+ import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
7
9
 
8
10
  const { Logger } = getXray();
9
11
 
@@ -35,12 +37,20 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
35
37
 
36
38
  const focusableId = getFocusableId(prefixId, suffixId);
37
39
 
38
- const focused = focusableId === focusedButtonId;
40
+ const focused =
41
+ focusableId === focusedButtonId ||
42
+ toBooleanWithDefaultFalse(otherProps.focused);
39
43
 
40
44
  const additionalStyles = focused ? focusedStyles : normalStyles;
41
45
 
42
46
  const actionContext = useActions(pluginIdentifier);
43
47
 
48
+ const accessibilityManager = useAccessibilityManager({});
49
+
50
+ const { ttsLabel } = actionContext
51
+ ?.initialEntryState(item)
52
+ ?.getAccessibility(item);
53
+
44
54
  const onPress = (event) => {
45
55
  event?.preventDefault?.();
46
56
 
@@ -85,6 +95,12 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
85
95
  mouse: focusable.mouse,
86
96
  });
87
97
  });
98
+
99
+ if (ttsLabel) {
100
+ accessibilityManager.readText({
101
+ text: ttsLabel,
102
+ });
103
+ }
88
104
  };
89
105
 
90
106
  const handleBlur = (_focusable, _direction) => {