@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4342066143 → 12.0.0-alpha.4354608902

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 (66) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +14 -13
  3. package/Components/Cell/CellWithFocusable.tsx +24 -10
  4. package/Components/Cell/TvOSCellComponent.tsx +6 -11
  5. package/Components/Focusable/Focusable.tsx +6 -1
  6. package/Components/FocusableGroup/index.tsx +11 -2
  7. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  8. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  9. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  10. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  11. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  12. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  13. package/Components/Layout/TV/__tests__/index.test.tsx +20 -75
  14. package/Components/Layout/TV/index.tsx +11 -67
  15. package/Components/Layout/TV/index.web.tsx +33 -0
  16. package/Components/Layout/index.tsx +15 -11
  17. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +7 -2
  18. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  19. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +20 -0
  20. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  21. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  22. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  23. package/Components/MasterCell/utils/index.ts +17 -30
  24. package/Components/NativeFocusables/{index.js → index.ts} +4 -2
  25. package/Components/Navigator/StackNavigator.tsx +57 -0
  26. package/Components/Navigator/index.ts +1 -0
  27. package/Components/PlayerContainer/PlayerContainer.tsx +5 -1
  28. package/Components/River/TV/River.tsx +0 -23
  29. package/Components/Screen/TV/hooks/index.ts +3 -0
  30. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  31. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  32. package/Components/Screen/TV/index.web.tsx +165 -0
  33. package/Components/Screen/__tests__/Screen.test.tsx +3 -0
  34. package/Components/Screen/index.tsx +55 -58
  35. package/Components/Screen/navigationHandler.ts +5 -0
  36. package/Components/ScreenResolver/index.tsx +3 -1
  37. package/Components/Transitioner/Scene.tsx +12 -11
  38. package/Components/Transitioner/Transitioner.tsx +15 -29
  39. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +8 -4
  40. package/Components/VideoLive/LiveImageManager.ts +25 -1
  41. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +2 -14
  42. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +56 -43
  43. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +42 -32
  44. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +104 -86
  45. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +32 -4
  46. package/Components/VideoModal/ModalAnimation/utils.ts +7 -4
  47. package/Components/VideoModal/PlayerWrapper.tsx +10 -10
  48. package/Components/VideoModal/VideoModal.tsx +4 -4
  49. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +32 -12
  50. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  51. package/Components/VideoModal/utils.ts +5 -2
  52. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  53. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  54. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  55. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  56. package/Contexts/FocusableGroupContext/index.ts +7 -0
  57. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  58. package/Contexts/ScreenDataContext/index.tsx +20 -0
  59. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  60. package/Decorators/Analytics/{index.js → index.tsx} +11 -11
  61. package/README.md +1 -13
  62. package/package.json +5 -5
  63. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  64. package/Contexts/ScreenDataContext/index.ts +0 -3
  65. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  66. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
@@ -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
  }
@@ -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,
@@ -130,8 +135,8 @@ export const BorderContainerView = (props: Props) => {
130
135
  {...props}
131
136
  style={[style, styles.container, borderStyleForMissingBorder]}
132
137
  >
133
- {children}
134
138
  <Border style={style} padding={padding} borderPosition={borderPosition} />
139
+ {children}
135
140
  </View>
136
141
  );
137
142
  };
@@ -4,6 +4,7 @@ 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";
7
8
 
8
9
  const { Logger } = getXray();
9
10
 
@@ -35,7 +36,9 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
35
36
 
36
37
  const focusableId = getFocusableId(prefixId, suffixId);
37
38
 
38
- const focused = focusableId === focusedButtonId;
39
+ const focused =
40
+ focusableId === focusedButtonId ||
41
+ toBooleanWithDefaultFalse(otherProps.focused);
39
42
 
40
43
  const additionalStyles = focused ? focusedStyles : normalStyles;
41
44
 
@@ -108,6 +108,26 @@ const prepareEntry = (entry) => {
108
108
  };
109
109
  }
110
110
 
111
+ const previewPlayback =
112
+ entry.extensions?.["brightcove"]?.["preview_playback"];
113
+
114
+ if (previewPlayback) {
115
+ return {
116
+ ...entry,
117
+ extensions: {
118
+ ...entry.extensions,
119
+ brightcove: {
120
+ ...entry?.extensions?.["brightcove"],
121
+ video_id: previewPlayback,
122
+ },
123
+ },
124
+ };
125
+ }
126
+
127
+ if (entry.extensions?.["brightcove"]?.["video_id"]) {
128
+ return entry;
129
+ }
130
+
111
131
  const src = entry.content?.src;
112
132
 
113
133
  if (!src) {
@@ -61,7 +61,10 @@ export function ButtonContainerView({
61
61
  <View style={applyRTLStylesIfNeeded(style, isRTL)}>
62
62
  {insertBetween(
63
63
  (index) => (
64
- <HorizontalSeparator key={index} width={horizontalGutter} />
64
+ <HorizontalSeparator
65
+ key={`separator_${index}`}
66
+ width={horizontalGutter}
67
+ />
65
68
  ),
66
69
  cloneElementsWithIds(buttonIds, filteredChildren)
67
70
  )}
@@ -7,6 +7,10 @@ import type {
7
7
  ContainerProps,
8
8
  ContainerChildren,
9
9
  } from "./types";
10
+ import {
11
+ useIsRTL,
12
+ applyRTLStylesIfNeeded,
13
+ } from "@applicaster/zapp-react-native-utils/localizationUtils";
10
14
 
11
15
  const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
12
16
  <View style={{ width }} />
@@ -17,6 +21,7 @@ export function ButtonContainerView({
17
21
  children,
18
22
  ...otherProps
19
23
  }: ContainerProps) {
24
+ const isRTL = useIsRTL();
20
25
  const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
21
26
 
22
27
  const filteredChildren = useFilterChildren<ContainerChildren>(children);
@@ -26,10 +31,13 @@ export function ButtonContainerView({
26
31
  }
27
32
 
28
33
  return (
29
- <View style={style}>
34
+ <View style={applyRTLStylesIfNeeded(style, isRTL)}>
30
35
  {insertBetween(
31
36
  (index) => (
32
- <HorizontalSeparator key={index} width={horizontalGutter} />
37
+ <HorizontalSeparator
38
+ key={`separator_${index}`}
39
+ width={horizontalGutter}
40
+ />
33
41
  ),
34
42
  filteredChildren
35
43
  )}
@@ -9,4 +9,13 @@ describe("insertBetween", () => {
9
9
 
10
10
  expect(result).toEqual([1, "|", 2, "|", 3]);
11
11
  });
12
+
13
+ it("insertBetween", () => {
14
+ const separator = (index) => `separator:${index}`;
15
+ const items = [1, 2, 3];
16
+
17
+ const result = insertBetween(separator, items);
18
+
19
+ expect(result).toEqual([1, "separator:0", 2, "separator:1", 3]);
20
+ });
12
21
  });
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect, useMemo } from "react";
2
2
  import * as R from "ramda";
3
+ import validateColor from "validate-color";
3
4
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
5
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
6
 
@@ -9,6 +10,7 @@ import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerM
9
10
  import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
10
11
  import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
11
12
  import { getCellState } from "../../Cell/utils";
13
+ import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
12
14
 
13
15
  const hasElementSpecificViewType = (viewType) => (element) => {
14
16
  if (R.isNil(element)) {
@@ -47,44 +49,29 @@ export const hasElementsSpecificViewType = (viewType) => (elements) => {
47
49
  return R.any(hasElementSpecificViewType(viewType))(elements);
48
50
  };
49
51
 
50
- const isColorOrEmpty = (value) =>
51
- value === null ||
52
- value === undefined ||
53
- value === "" ||
54
- value.startsWith("#") ||
55
- value.startsWith("rgba") ||
56
- value === "transparent";
57
-
58
52
  function resolveColorForProp(entry, style, colorProp) {
59
- const colorValue = style[colorProp];
60
-
61
- if (isColorOrEmpty(colorValue)) {
62
- return style;
53
+ const colorFromProp = style[colorProp];
54
+ const nestedEntryValue = R.path(colorFromProp.split("."), entry);
55
+
56
+ if (
57
+ nestedEntryValue === undefined &&
58
+ !validateColor(colorFromProp.replace(".00", "")) // https://github.com/dreamyguy/validate-color/issues/44
59
+ ) {
60
+ logWarning(colorFromProp, style, entry, nestedEntryValue);
63
61
  }
64
62
 
65
- const pathParts = colorValue.split(".");
66
- let nestedEntryValue = entry;
67
-
68
- for (const part of pathParts) {
69
- if (nestedEntryValue === null || typeof nestedEntryValue !== "object") {
70
- logWarning(colorValue, style, entry, nestedEntryValue);
71
-
72
- return style;
73
- }
74
-
75
- nestedEntryValue = nestedEntryValue[part];
76
- }
63
+ const colorValue = getColorFromData({
64
+ data: entry,
65
+ valueFromLayout: colorFromProp,
66
+ });
77
67
 
78
- if (nestedEntryValue === undefined) {
79
- logWarning(colorValue, style, entry, nestedEntryValue);
68
+ if (!colorValue) {
69
+ logWarning(colorProp, style, entry, colorValue);
80
70
 
81
71
  return style;
82
72
  }
83
73
 
84
- // After successful traversal, set the value.
85
- style[colorProp] = nestedEntryValue;
86
-
87
- return style;
74
+ return { ...style, [colorProp]: colorValue };
88
75
  }
89
76
 
90
77
  export function resolveColor(entry, style) {
@@ -1,10 +1,12 @@
1
1
  import { requireNativeComponent, View } from "react-native";
2
2
  import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
3
3
 
4
+ // @TODO define the types
4
5
  export const FocusableGroupNative = isAppleTV()
5
- ? requireNativeComponent("FocusableGroupViewModule", null)
6
+ ? requireNativeComponent<any>("FocusableGroupViewModule")
6
7
  : View;
7
8
 
9
+ // @TODO define the types
8
10
  export const FocusableItemNative = isAppleTV()
9
- ? requireNativeComponent("FocusableViewModule", null)
11
+ ? requireNativeComponent<any>("FocusableViewModule")
10
12
  : View;
@@ -0,0 +1,57 @@
1
+ import * as React from "react";
2
+ import { isEmpty } from "ramda";
3
+
4
+ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
5
+ import { getPathAttributes } from "@applicaster/zapp-react-native-utils/navigationUtils";
6
+ import { FocusableGroup } from "@applicaster/zapp-react-native-ui-components/Components/FocusableGroup";
7
+ import { ScreenDataContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenDataContext";
8
+ import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
9
+
10
+ import { Screen } from "../Screen/TV/index.web";
11
+ import { isLast } from "@applicaster/zapp-react-native-utils/arrayUtils";
12
+ import { ScreenContextProvider } from "../../Contexts/ScreenContext";
13
+
14
+ type Components = {
15
+ NavBar: React.ComponentType<any>;
16
+ Background: React.ComponentType<any>;
17
+ };
18
+
19
+ type Props = {
20
+ Components: Components;
21
+ };
22
+
23
+ export const StackNavigator = ({ Components }: Props) => {
24
+ const { mainStack } = useNavigation();
25
+
26
+ const renderScreen = ({ route, state }, index) => {
27
+ const pathAttributes = getPathAttributes({ pathname: route });
28
+
29
+ if (isEmpty(pathAttributes)) {
30
+ return null;
31
+ }
32
+
33
+ // HACK: Adding index to route to avoid conflicts in the navigator.
34
+ // Startup hooks are all using PUSH navigation action to navigate, but their routes are always like root routes.
35
+ // This means some hooks can have the same routes, e.g., ["hooks/context-setter", "/hooks/login-plug", "hooks/context-setter"].
36
+ const routeId = `${route}--${index}`;
37
+
38
+ return (
39
+ <FocusableGroup
40
+ id={route}
41
+ preferredFocus={isLast(index, mainStack.length)}
42
+ excludeFromFocusSearching
43
+ key={routeId}
44
+ >
45
+ <ScreenDataContext.Provider value={state}>
46
+ <PathnameContext.Provider value={route}>
47
+ <ScreenContextProvider pathname={route}>
48
+ <Screen route={route} Components={Components} />
49
+ </ScreenContextProvider>
50
+ </PathnameContext.Provider>
51
+ </ScreenDataContext.Provider>
52
+ </FocusableGroup>
53
+ );
54
+ };
55
+
56
+ return <>{mainStack.map(renderScreen)}</>;
57
+ };
@@ -0,0 +1 @@
1
+ export { StackNavigator } from "./StackNavigator";
@@ -68,6 +68,7 @@ type Props = {
68
68
  mode?: VideoModalMode;
69
69
  loadPipesData: (url: string, options: {}) => void;
70
70
  disableCastAction?: boolean;
71
+ groupId?: string;
71
72
  };
72
73
 
73
74
  type ScreenStyles = ZappRiver["styles"] & {
@@ -123,6 +124,7 @@ const nativeStyles = {
123
124
  playerScreen: {
124
125
  flex: 1,
125
126
  backgroundColor: "black",
127
+ overflow: "hidden",
126
128
  },
127
129
  playerWrapper: {
128
130
  flex: 1,
@@ -200,6 +202,7 @@ const PlayerContainerComponent = (props: Props) => {
200
202
  style: videoStyle,
201
203
  mode = VideoModalMode.MAXIMIZED,
202
204
  disableCastAction = false,
205
+ groupId,
203
206
  } = props;
204
207
 
205
208
  // Hooks
@@ -249,8 +252,8 @@ const PlayerContainerComponent = (props: Props) => {
249
252
  return;
250
253
  }
251
254
 
252
- navigator.goBack();
253
255
  showNavBar(true);
256
+ navigator.goBack();
254
257
  }, [isModal, navigator.goBack, showNavBar]);
255
258
 
256
259
  const playEntry = (entry) => navigator.replaceTop(entry, { mode });
@@ -629,6 +632,7 @@ const PlayerContainerComponent = (props: Props) => {
629
632
  style={styles.focusableGroup}
630
633
  preferredFocus
631
634
  shouldUsePreferredFocus
635
+ groupId={groupId}
632
636
  >
633
637
  {/* Video player and components */}
634
638
  <View