@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4054240460 → 12.0.0-alpha.4155487673

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 (52) hide show
  1. package/Components/BaseFocusable/index.tsx +2 -2
  2. package/Components/Cell/Cell.tsx +25 -13
  3. package/Components/Cell/CellWithFocusable.tsx +6 -4
  4. package/Components/Cell/TvOSCellComponent.tsx +3 -7
  5. package/Components/Focusable/Focusable.tsx +6 -1
  6. package/Components/FocusableGroup/index.tsx +10 -2
  7. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  8. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  9. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  10. package/Components/Layout/TV/__tests__/index.test.tsx +2 -1
  11. package/Components/Layout/TV/index.tsx +11 -65
  12. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -1
  13. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +8 -9
  14. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  15. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +57 -0
  16. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +20 -0
  17. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  18. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +6 -1
  19. package/Components/MasterCell/dataAdapter.ts +21 -7
  20. package/Components/MasterCell/index.tsx +5 -3
  21. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  22. package/Components/Navigator/StackNavigator.tsx +14 -5
  23. package/Components/PlayerContainer/PlayerContainer.tsx +2 -5
  24. package/Components/Screen/TV/index.web.tsx +9 -23
  25. package/Components/Screen/index.tsx +27 -58
  26. package/Components/Screen/navigationHandler.ts +5 -0
  27. package/Components/ScreenResolver/index.tsx +6 -11
  28. package/Components/Transitioner/Scene.tsx +12 -11
  29. package/Components/Transitioner/Transitioner.tsx +17 -15
  30. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +8 -4
  31. package/Components/VideoLive/LiveImageManager.ts +10 -0
  32. package/Components/VideoLive/PlayerLiveImageComponent.tsx +52 -24
  33. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +26 -24
  34. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +2 -14
  35. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +14 -6
  36. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +9 -2
  37. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +10 -15
  38. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +32 -4
  39. package/Components/VideoModal/ModalAnimation/utils.ts +7 -4
  40. package/Components/VideoModal/PlayerWrapper.tsx +2 -2
  41. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +80 -14
  42. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  43. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  44. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  45. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  46. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  47. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  48. package/Contexts/FocusableGroupContext/index.ts +7 -0
  49. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  50. package/package.json +5 -5
  51. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  52. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
@@ -71,9 +71,9 @@ export class BaseFocusable<
71
71
  }
72
72
 
73
73
  getId(props?: { id: string; groupId: string }) {
74
- const { id, groupId = null } = props || this.props;
74
+ const { id } = props || this.props;
75
75
 
76
- return groupId ? `${groupId}-${id}` : id;
76
+ return id;
77
77
  }
78
78
 
79
79
  getRect() {
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { join, path, merge } from "ramda";
2
+ import { join, merge, path } from "ramda";
3
3
  import { View } from "react-native";
4
4
 
5
5
  import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
@@ -11,10 +11,7 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
11
11
 
12
12
  import { CellWithFocusable } from "./CellWithFocusable";
13
13
  import { BaseFocusable } from "../BaseFocusable";
14
- import {
15
- isVizioPlatform,
16
- TTSManager,
17
- } from "@applicaster/zapp-react-native-utils/appUtils/platform/platformUtils";
14
+ import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
18
15
 
19
16
  type Props = {
20
17
  item: {
@@ -70,6 +67,10 @@ type Props = {
70
67
  behavior: Behavior;
71
68
  };
72
69
 
70
+ type State = {
71
+ hasFocusableInside: boolean;
72
+ };
73
+
73
74
  const baseCellStyles = {
74
75
  height: "100%",
75
76
  display: "flex",
@@ -81,7 +82,7 @@ const touchableStyles = {
81
82
  height: "100%",
82
83
  } as const;
83
84
 
84
- export class CellComponent extends React.Component<Props> {
85
+ export class CellComponent extends React.Component<Props, State> {
85
86
  constructor(props) {
86
87
  super(props);
87
88
  this.onPress = this.onPress.bind(this);
@@ -91,6 +92,10 @@ export class CellComponent extends React.Component<Props> {
91
92
  this.hasReceivedFocus = this.hasReceivedFocus.bind(this);
92
93
  this.scrollVertically = this.scrollVertically.bind(this);
93
94
  this.scrollToIndex = this.scrollToIndex.bind(this);
95
+
96
+ this.state = {
97
+ hasFocusableInside: props.CellRenderer.hasFocusableInside?.(props.item),
98
+ };
94
99
  }
95
100
 
96
101
  setScreenLayout(componentAnchorPointY, screenLayout) {
@@ -144,10 +149,9 @@ export class CellComponent extends React.Component<Props> {
144
149
  ) {
145
150
  this.scrollVertically(focusable);
146
151
 
147
- if (isVizioPlatform()) {
148
- const ttsManager = TTSManager.getInstance();
149
- ttsManager.readText(title);
150
- }
152
+ const accessibilityManager = AccessibilityManager.getInstance();
153
+ // Text from DSP should be localized already
154
+ accessibilityManager.readText({ text: title });
151
155
  }
152
156
  }
153
157
  }
@@ -196,6 +200,16 @@ export class CellComponent extends React.Component<Props> {
196
200
  return !isFocusable ? false : focused || focusableFocused;
197
201
  }
198
202
 
203
+ componentDidUpdate(prevProps: Readonly<Props>) {
204
+ if (prevProps.item !== this.props.item) {
205
+ this.setState({
206
+ hasFocusableInside: this.props.CellRenderer.hasFocusableInside?.(
207
+ this.props.item
208
+ ),
209
+ });
210
+ }
211
+ }
212
+
199
213
  render() {
200
214
  const {
201
215
  item,
@@ -224,9 +238,7 @@ export class CellComponent extends React.Component<Props> {
224
238
  this.onFocus(focusable, mouse);
225
239
  };
226
240
 
227
- const hasFocusableInside = CellRenderer.hasFocusableInside?.(item);
228
-
229
- if (hasFocusableInside) {
241
+ if (this.state.hasFocusableInside) {
230
242
  return (
231
243
  <CellWithFocusable
232
244
  CellRenderer={CellRenderer}
@@ -3,6 +3,8 @@ import * as React from "react";
3
3
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
4
4
  import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
5
5
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
6
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
7
+
6
8
  import { isAppleTV } from "../../Helpers/Platform";
7
9
  import { useCellState } from "../MasterCell/utils";
8
10
 
@@ -54,7 +56,7 @@ type Props = {
54
56
  skipFocusManagerRegistration?: boolean;
55
57
  isFocusable?: boolean;
56
58
  behavior: Behavior;
57
- selected?: boolean;
59
+ focused?: boolean;
58
60
  };
59
61
 
60
62
  export function CellWithFocusable(props: Props) {
@@ -70,7 +72,7 @@ export function CellWithFocusable(props: Props) {
70
72
  skipFocusManagerRegistration,
71
73
  isFocusable,
72
74
  behavior,
73
- selected,
75
+ focused,
74
76
  } = props;
75
77
 
76
78
  const isFocused = useCellFocusedState(
@@ -82,8 +84,7 @@ export function CellWithFocusable(props: Props) {
82
84
  const state = useCellState({
83
85
  id: item.id,
84
86
  behavior,
85
- focused: isFocused,
86
- selected,
87
+ focused: isFocused || toBooleanWithDefaultFalse(focused),
87
88
  });
88
89
 
89
90
  const [focusedButtonId, setFocusedButtonId] = React.useState(undefined);
@@ -114,6 +115,7 @@ export function CellWithFocusable(props: Props) {
114
115
  preferredFocus={preferredFocus}
115
116
  skipFocusManagerRegistration={skipFocusManagerRegistration}
116
117
  isFocusable={isFocusable}
118
+ focused={focused}
117
119
  />
118
120
  );
119
121
  }
@@ -226,13 +226,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
226
226
 
227
227
  hasReceivedFocus() {}
228
228
 
229
- scrollTo(index) {
230
- // Workaround for HL autoscroll/autoloop feature, we scroll manually on tvOS.
231
- // Should be removed after refactoring autoscroll/autoloop feature
232
- if (this.props.component?.component_type !== "horizontal_list_qb") {
233
- this.props?.scrollToIndex?.(index);
234
- }
235
- }
229
+ scrollTo() {}
236
230
 
237
231
  render() {
238
232
  const {
@@ -279,7 +273,9 @@ export class TvOSCellComponent extends React.Component<Props, State> {
279
273
  index={index}
280
274
  scrollTo={this.scrollTo}
281
275
  preferredFocus={preferredFocus}
276
+ focused={this.props.focused}
282
277
  behavior={behavior}
278
+ isFocusable={isFocusable}
283
279
  />
284
280
  </View>
285
281
  );
@@ -4,6 +4,7 @@ import * as R from "ramda";
4
4
  import { BaseFocusable } from "../BaseFocusable";
5
5
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
6
6
  import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
7
+ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
7
8
 
8
9
  type Props = {
9
10
  initialFocus?: boolean;
@@ -23,7 +24,7 @@ type Props = {
23
24
  style?: React.CSSProperties;
24
25
  };
25
26
 
26
- export class Focusable extends BaseFocusable<Props> {
27
+ class Focusable extends BaseFocusable<Props> {
27
28
  isGroup: boolean;
28
29
  mouse: boolean;
29
30
  longPressTimeout = null;
@@ -198,3 +199,7 @@ export class Focusable extends BaseFocusable<Props> {
198
199
  );
199
200
  }
200
201
  }
202
+
203
+ const FocusableWithFocusableContext = withFocusableContext(Focusable);
204
+
205
+ export { FocusableWithFocusableContext as Focusable };
@@ -1,6 +1,8 @@
1
1
  import * as React from "react";
2
2
 
3
3
  import { BaseFocusable } from "../BaseFocusable";
4
+ import { FocusableGroupContextProvider } from "../../Contexts/FocusableGroupContext";
5
+ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
4
6
 
5
7
  type Props = {
6
8
  testID?: string;
@@ -29,7 +31,7 @@ type Props = {
29
31
  excludeFromFocusSearching?: boolean;
30
32
  };
31
33
 
32
- export class FocusableGroup extends BaseFocusable<Props> {
34
+ class FocusableGroup extends BaseFocusable<Props> {
33
35
  constructor(props) {
34
36
  super(props);
35
37
  this.isGroup = true;
@@ -82,8 +84,14 @@ export class FocusableGroup extends BaseFocusable<Props> {
82
84
  style={style}
83
85
  ref={this.ref}
84
86
  >
85
- {children}
87
+ <FocusableGroupContextProvider id={id}>
88
+ {children}
89
+ </FocusableGroupContextProvider>
86
90
  </div>
87
91
  );
88
92
  }
89
93
  }
94
+
95
+ const FocusableGroupWithFocusableContext = withFocusableContext(FocusableGroup);
96
+
97
+ export { FocusableGroupWithFocusableContext as FocusableGroup };
@@ -5,13 +5,30 @@ import * as R from "ramda";
5
5
  import { ScreenLayoutContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenLayoutContext";
6
6
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
7
7
  import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
8
- import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
8
+ import {
9
+ useCurrentScreenData,
10
+ useNavbarState,
11
+ } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
9
12
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
10
13
 
11
14
  import { useScreenConfiguration } from "../../River/useScreenConfiguration";
12
- import { routeIsPlayerScreen } from "@applicaster/zapp-react-native-utils/navigationUtils";
15
+ import {
16
+ findMenuPlugin,
17
+ getNavigationProps,
18
+ routeIsPlayerScreen,
19
+ } from "@applicaster/zapp-react-native-utils/navigationUtils";
20
+ import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
21
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
22
+ import { NavBarContainer } from "./NavBarContainer";
23
+
24
+ type ComponentsExtraProps = {
25
+ Background?: Record<string, any>;
26
+ NavBar?: Record<string, any>;
27
+ };
13
28
 
14
29
  type Props = {
30
+ ComponentsExtraProps?: ComponentsExtraProps;
31
+ NavBar: React.ComponentType<any>;
15
32
  children: React.ReactNode;
16
33
  };
17
34
 
@@ -30,9 +47,13 @@ const styles = StyleSheet.create({
30
47
  container: { flex: 1, height: "100%", width: "100%" },
31
48
  });
32
49
 
50
+ const isTVOS = isApplePlatform();
51
+
33
52
  // eslint-disable-next-line react/display-name
34
53
  export const ScreenContainer = React.memo(function ScreenContainer({
35
54
  children,
55
+ ComponentsExtraProps,
56
+ NavBar,
36
57
  }: Props) {
37
58
  const { screenMarginBottom } = React.useContext(ScreenLayoutContext);
38
59
  const { currentRoute, screenData } = useNavigation();
@@ -80,9 +101,62 @@ export const ScreenContainer = React.memo(function ScreenContainer({
80
101
  [themeStyles]
81
102
  );
82
103
 
104
+ const navigator = useNavigation();
105
+ const { activeRiver } = navigator;
106
+ const { title, visible } = useNavbarState();
107
+
108
+ const { plugins = [] } = usePickFromState([
109
+ "appState",
110
+ "remoteConfigurations",
111
+ "plugins",
112
+ ]);
113
+
114
+ const navigationProps = getNavigationProps({
115
+ navigator,
116
+ title,
117
+ category: "menu",
118
+ });
119
+
120
+ const menuPlugin = React.useMemo(
121
+ () => findMenuPlugin(activeRiver?.navigations, plugins),
122
+ [activeRiver?.navigations]
123
+ );
124
+
125
+ const NavBarComponent = React.useMemo(
126
+ () => NavBar || (menuPlugin.module as React.ComponentType<any>),
127
+ []
128
+ );
129
+
130
+ const [navBarReady, setNavBarReady] = React.useState(false);
131
+
132
+ const navBarContainer = (
133
+ <NavBarContainer isVisible={visible} onReady={setNavBarReady}>
134
+ <NavBarComponent
135
+ ref={ComponentsExtraProps?.NavBar?.ref}
136
+ isVisible={visible}
137
+ menuPlugin={menuPlugin}
138
+ navigationProps={navigationProps}
139
+ {...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
140
+ />
141
+ </NavBarContainer>
142
+ );
143
+
83
144
  return (
84
- <View testID="screen-container" style={viewStyles}>
85
- {children}
86
- </View>
145
+ <>
146
+ {!isTVOS ? navBarContainer : null}
147
+ {navBarReady ? (
148
+ /**
149
+ Need to re-mount this component on each navigation event
150
+ It's a hack to work around the fact that we don't support
151
+ correct screen rendering in our navigator (shared currentRoute state)
152
+ **/
153
+ <View testID="screen-container" style={viewStyles}>
154
+ {children}
155
+ </View>
156
+ ) : null}
157
+ {/** Render NavBarContainer over ScreenContainer to let tvOS native
158
+ focus manager works correctly to focus nav bar items first **/}
159
+ {isTVOS ? navBarContainer : null}
160
+ </>
87
161
  );
88
162
  });
@@ -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`] = `
@@ -21,10 +21,11 @@ const mockStore = configureStore()({
21
21
  describe("Layout TV", () => {
22
22
  const navigator = {
23
23
  currentRoute: "/river/A1234",
24
+ canGoBack: jest.fn(),
24
25
  mainStack: [
25
26
  {
26
27
  route: "/river/A1234",
27
- state: "state",
28
+ state: { screen: {} },
28
29
  },
29
30
  ],
30
31
  };
@@ -4,22 +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
10
 
18
11
  import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
19
- import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
20
12
  import { PathnameContext } from "../../../Contexts/PathnameContext";
21
- import { useNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
22
13
  import { ScreenDataContext } from "../../../Contexts/ScreenDataContext";
14
+ import { ScreenContextProvider } from "../../../Contexts/ScreenContext";
23
15
 
24
16
  type Components = {
25
17
  NavBar: React.ComponentType<any>;
@@ -40,20 +32,9 @@ type Props = {
40
32
  const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
41
33
  const navigator = useNavigation();
42
34
  const theme = useTheme();
43
- const { title, visible } = useNavbarState();
44
- const { activeRiver, currentRoute } = navigator;
45
-
46
- const {
47
- appState: { appReady = false } = {},
48
- remoteConfigurations,
49
- plugins = [],
50
- } = usePickFromState(["appState", "remoteConfigurations", "plugins"]);
51
35
 
52
- const navigationProps = getNavigationProps({
53
- navigator,
54
- title,
55
- category: "menu",
56
- });
36
+ const { appState: { appReady = false } = {}, remoteConfigurations } =
37
+ usePickFromState(["appState", "remoteConfigurations", "plugins"]);
57
38
 
58
39
  const backgroundColor = React.useMemo(() => theme.app_background_color, []);
59
40
 
@@ -62,37 +43,6 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
62
43
  [remoteConfigurations]
63
44
  );
64
45
 
65
- const menuPlugin = React.useMemo(
66
- () =>
67
- resolveNavigationPlugin({
68
- category: "menu",
69
- navigations: activeRiver?.navigations,
70
- plugins,
71
- }) as QuickBrickPlugin,
72
- []
73
- );
74
-
75
- const NavBar = React.useMemo(
76
- () => Components.NavBar || (menuPlugin.module as React.ComponentType<any>),
77
- []
78
- );
79
-
80
- const [navBarReady, setNavBarReady] = React.useState(false);
81
-
82
- const navBarContainer = (
83
- <NavBarContainer isVisible={visible} onReady={setNavBarReady}>
84
- <NavBar
85
- ref={ComponentsExtraProps?.NavBar?.ref}
86
- isVisible={visible && !routeIsPlayerScreen(currentRoute)}
87
- menuPlugin={menuPlugin}
88
- navigationProps={navigationProps}
89
- {...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
90
- />
91
- </NavBarContainer>
92
- );
93
-
94
- const isTVOS = isApplePlatform();
95
-
96
46
  if (!appReady) {
97
47
  return null;
98
48
  }
@@ -107,18 +57,14 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
107
57
  >
108
58
  <ScreenDataContext.Provider value={navigator.data}>
109
59
  <PathnameContext.Provider value={navigator.currentRoute}>
110
- {!isTVOS ? navBarContainer : null}
111
- {navBarReady ? (
112
- /**
113
- Need to re-mount this component on each navigation event
114
- It's a hack to work around the fact that we don't support
115
- correct screen rendering in our navigator (shared currentRoute state)
116
- **/
117
- <ScreenContainer>{children}</ScreenContainer>
118
- ) : null}
119
- {/** Render NavBarContainer over ScreenContainer to let tvOS native
120
- focus manager works correctly to focus nav bar items first **/}
121
- {isTVOS ? navBarContainer : null}
60
+ <ScreenContextProvider pathname={navigator.currentRoute}>
61
+ <ScreenContainer
62
+ ComponentsExtraProps={ComponentsExtraProps}
63
+ NavBar={Components.NavBar}
64
+ >
65
+ {children}
66
+ </ScreenContainer>
67
+ </ScreenContextProvider>
122
68
  </PathnameContext.Provider>
123
69
  </ScreenDataContext.Provider>
124
70
  </Components.Background>
@@ -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, {