@applicaster/zapp-react-native-ui-components 15.0.0-alpha.1812021122 → 15.0.0-alpha.1954787293

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.
@@ -8,6 +8,8 @@ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withF
8
8
  import { StyleSheet, ViewStyle } from "react-native";
9
9
  import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
10
10
 
11
+ import { isSearchInputId } from "@applicaster/search-screen/src/tv/utils";
12
+
11
13
  type Props = {
12
14
  initialFocus?: boolean;
13
15
  id: string;
@@ -106,7 +108,7 @@ class Focusable extends BaseFocusable<Props> {
106
108
  onMouseEnter() {
107
109
  const { id } = this.props;
108
110
 
109
- if (id !== "search_input_group_id") {
111
+ if (!isSearchInputId(id)) {
110
112
  this.mouse = true;
111
113
  this.props?.handleFocus?.({ mouse: true });
112
114
 
@@ -120,7 +122,7 @@ class Focusable extends BaseFocusable<Props> {
120
122
  onMouseLeave() {
121
123
  const { id } = this.props;
122
124
 
123
- if (id !== "search_input_group_id") {
125
+ if (!isSearchInputId(id)) {
124
126
  this.mouse = false;
125
127
  this.blur(null);
126
128
  }
@@ -10,9 +10,13 @@ import {
10
10
  forceFocusableFocus,
11
11
  } from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
12
12
  import { findNodeHandle, ViewStyle } from "react-native";
13
- import { emitNativeRegistered } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
14
13
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
15
14
 
15
+ import {
16
+ emitFocused,
17
+ emitNativeRegistered,
18
+ } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
19
+
16
20
  type Props = {
17
21
  id: string;
18
22
  groupId: string;
@@ -86,6 +90,9 @@ export class Focusable extends BaseFocusable<Props> {
86
90
  });
87
91
  }
88
92
 
93
+ const id: string = nativeEvent.itemID;
94
+ emitFocused(id);
95
+
89
96
  onFocus(nativeEvent);
90
97
  }
91
98
 
@@ -2,6 +2,9 @@ import * as React from "react";
2
2
  import { FocusableGroupNative } from "@applicaster/zapp-react-native-ui-components/Components/NativeFocusables";
3
3
  import { BaseFocusable } from "@applicaster/zapp-react-native-ui-components/Components/BaseFocusable";
4
4
  import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
5
+ import { LayoutContext } from "@applicaster/zapp-react-native-tvos-app/Context/LayoutContext";
6
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
7
+ import { isScreenPlayable } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
5
8
  import { emitNativeRegistered } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
6
9
 
7
10
  const { log_verbose } = createLogger({
@@ -34,7 +37,7 @@ type Props = {
34
37
  screenData: { screenId: string; parentScreenId: string };
35
38
  };
36
39
 
37
- export class FocusableGroup extends BaseFocusable<Props> {
40
+ class FocusableGroupComponent extends BaseFocusable<Props> {
38
41
  public readonly isGroup: boolean = true;
39
42
 
40
43
  onRegistered = ({ nativeEvent }) => {
@@ -83,3 +86,20 @@ export class FocusableGroup extends BaseFocusable<Props> {
83
86
  );
84
87
  }
85
88
  }
89
+
90
+ export const withFocusDisabled = (Component) => {
91
+ return function WithFocusDisabled(props) {
92
+ // @ts-ignore
93
+ const { screenFocusBlocked } = React.useContext(LayoutContext.ReactContext);
94
+
95
+ const { pathname } = useRoute();
96
+
97
+ const isPlayerPresented = isScreenPlayable(pathname);
98
+
99
+ const blockScreenFocus = isPlayerPresented === false && screenFocusBlocked;
100
+
101
+ return <Component {...props} isFocusDisabled={blockScreenFocus} />;
102
+ };
103
+ };
104
+
105
+ export const FocusableGroup = withFocusDisabled(FocusableGroupComponent);
@@ -274,7 +274,7 @@ const PlayerContainerComponent = (props: Props) => {
274
274
  );
275
275
  }, [playerManager.isRegistered()]);
276
276
 
277
- useRestrictMobilePlayback({
277
+ const { isRestricted } = useRestrictMobilePlayback({
278
278
  player,
279
279
  entry: item,
280
280
  pluginConfiguration,
@@ -670,36 +670,38 @@ const PlayerContainerComponent = (props: Props) => {
670
670
  <PlayerFocusableWrapperView
671
671
  nextFocusDown={context.bottomFocusableId}
672
672
  >
673
- <Player
674
- source={{
675
- uri,
676
- entry: item,
677
- }}
678
- focused={isInlineTV ? true : undefined}
679
- autoplay={true}
680
- controls={false}
681
- disableCastAction={disableCastAction}
682
- docked={navigator.isVideoModalDocked()}
683
- entry={item}
684
- fullscreen={mode === VideoModalMode.FULLSCREEN}
685
- inline={inline}
686
- isModal={isModal}
687
- isTabletPortrait={isTabletPortrait}
688
- muted={false}
689
- playableItem={item}
690
- playerEvent={playerEvent}
691
- playerId={state.playerId}
692
- pluginConfiguration={pluginConfiguration}
693
- ref={playerRef}
694
- toggleFullscreen={toggleFullscreen}
695
- style={videoStyle}
696
- playNextData={playNextData}
697
- setNextVideoPreloadThresholdPercentage={
698
- setNextVideoPreloadThresholdPercentage
699
- }
700
- >
701
- {renderApplePlayer(applePlayerProps)}
702
- </Player>
673
+ {isRestricted ? null : (
674
+ <Player
675
+ source={{
676
+ uri,
677
+ entry: item,
678
+ }}
679
+ focused={isInlineTV ? true : undefined}
680
+ autoplay={true}
681
+ controls={false}
682
+ disableCastAction={disableCastAction}
683
+ docked={navigator.isVideoModalDocked()}
684
+ entry={item}
685
+ fullscreen={mode === VideoModalMode.FULLSCREEN}
686
+ inline={inline}
687
+ isModal={isModal}
688
+ isTabletPortrait={isTabletPortrait}
689
+ muted={false}
690
+ playableItem={item}
691
+ playerEvent={playerEvent}
692
+ playerId={state.playerId}
693
+ pluginConfiguration={pluginConfiguration}
694
+ ref={playerRef}
695
+ toggleFullscreen={toggleFullscreen}
696
+ style={videoStyle}
697
+ playNextData={playNextData}
698
+ setNextVideoPreloadThresholdPercentage={
699
+ setNextVideoPreloadThresholdPercentage
700
+ }
701
+ >
702
+ {renderApplePlayer(applePlayerProps)}
703
+ </Player>
704
+ )}
703
705
  </PlayerFocusableWrapperView>
704
706
 
705
707
  {state.error ? <ErrorDisplay error={state.error} /> : null}
@@ -1,25 +1,26 @@
1
1
  import { Player } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/player";
2
2
  import NetInfo from "@react-native-community/netinfo";
3
3
 
4
- import { useEffect, useMemo, useRef } from "react";
4
+ import { useEffect, useMemo, useRef, useState } from "react";
5
5
  import { showAlertDialog } from "@applicaster/zapp-react-native-utils/alertUtils";
6
6
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
7
7
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
8
8
  import { log_info } from "./logger";
9
9
 
10
- export const useRestrictMobilePlayback = ({
11
- player,
12
- entry,
13
- pluginConfiguration,
14
- close,
15
- }: {
10
+ type RestrictMobilePlaybackProps = {
16
11
  player?: Player;
17
12
  entry?: ZappEntry;
18
13
  pluginConfiguration?: Record<string, string>;
19
14
  close: () => void;
20
- }) => {
21
- const dialogVisibleRef = useRef(false);
15
+ };
22
16
 
17
+ export const useRestrictMobilePlayback = ({
18
+ player,
19
+ entry,
20
+ pluginConfiguration,
21
+ close,
22
+ }: RestrictMobilePlaybackProps): { isRestricted: boolean } => {
23
+ const dialogVisibleRef = useRef<boolean>(false);
23
24
  const theme = useTheme();
24
25
 
25
26
  useEffect(() => {
@@ -40,24 +41,9 @@ export const useRestrictMobilePlayback = ({
40
41
  return player && entry?.extensions?.connection_restricted;
41
42
  }, [player, entry]);
42
43
 
43
- useEffect(() => {
44
- if (!isConnectionRestricted) {
45
- return;
46
- }
47
-
48
- player.addListener({
49
- id: "restrictMobilePlaybackListener",
50
- listener: {
51
- onPlayerResume: () => {
52
- if (dialogVisibleRef.current) {
53
- player.pause();
54
- }
55
-
56
- dialogVisibleRef.current = false;
57
- },
58
- },
59
- });
60
- }, [isConnectionRestricted, player]);
44
+ const [isRestricted, setIsRestricted] = useState<boolean>(
45
+ isConnectionRestricted
46
+ );
61
47
 
62
48
  useEffect(() => {
63
49
  if (!isConnectionRestricted) {
@@ -69,17 +55,17 @@ export const useRestrictMobilePlayback = ({
69
55
  "Stopping player due to mobile restriction, connection_restricted: true"
70
56
  );
71
57
 
72
- player.pause();
58
+ player?.close();
73
59
 
74
60
  dialogVisibleRef.current = true;
75
61
 
76
62
  showAlertDialog({
77
63
  title:
78
64
  pluginConfiguration?.mobile_connection_restricted_alert_title ||
79
- "N/A",
65
+ "Restricted Connection Type",
80
66
  message:
81
67
  pluginConfiguration?.mobile_connection_restricted_alert_message ||
82
- "N/A",
68
+ "This content can only be viewed over a Wi-Fi or LAN network.",
83
69
  okButtonText: theme.ok_button || "OK",
84
70
  completion: () => {
85
71
  dialogVisibleRef.current = false;
@@ -91,19 +77,25 @@ export const useRestrictMobilePlayback = ({
91
77
 
92
78
  return NetInfo.addEventListener((state) => {
93
79
  if (state.type === "cellular") {
80
+ setIsRestricted(true);
81
+
94
82
  if (dialogVisibleRef.current) {
95
83
  return;
96
84
  }
97
85
 
98
86
  stopPlayer();
87
+ } else {
88
+ setIsRestricted(false);
99
89
  }
100
90
  });
101
91
  }, [
102
92
  close,
103
- entry.extensions.connection_restricted,
93
+ entry?.extensions?.connection_restricted,
104
94
  pluginConfiguration,
105
95
  player,
106
96
  theme.ok_button,
107
97
  isConnectionRestricted,
108
98
  ]);
99
+
100
+ return { isRestricted };
109
101
  };
@@ -2,7 +2,8 @@
2
2
 
3
3
  import * as React from "react";
4
4
  import { Text } from "react-native";
5
- import * as R from "ramda";
5
+
6
+ import { mergeRight } from "@applicaster/zapp-react-native-utils/utils";
6
7
 
7
8
  import { GeneralContentScreen } from "../../GeneralContentScreen";
8
9
  import { ScreenResolver } from "@applicaster/zapp-react-native-ui-components/Components/ScreenResolver";
@@ -13,6 +14,8 @@ import {
13
14
  } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
14
15
  import { useRivers } from "@applicaster/zapp-react-native-utils/reactHooks/state";
15
16
 
17
+ import { toStringOrEmpty } from "./utils";
18
+
16
19
  type Props = {
17
20
  screenId: string;
18
21
  screenData: ZappRiver | ZappEntry;
@@ -24,6 +27,7 @@ type Props = {
24
27
  isInsideContainer?: boolean;
25
28
  extraAnchorPointYOffset: number;
26
29
  river?: ZappRiver | ZappEntry;
30
+ groupId: string;
27
31
  };
28
32
 
29
33
  export const River = (props: Props) => {
@@ -35,6 +39,7 @@ export const River = (props: Props) => {
35
39
  componentsMapExtraProps,
36
40
  isInsideContainer,
37
41
  extraAnchorPointYOffset,
42
+ groupId,
38
43
  } = props;
39
44
 
40
45
  const { title: screenTitle, summary: screenSummary } = useNavbarState();
@@ -51,28 +56,41 @@ export const River = (props: Props) => {
51
56
  [screenId]
52
57
  );
53
58
 
54
- const stringOrEmpty = (value: string | number | undefined): string =>
55
- R.isNil(value) ? "" : String(value);
59
+ const screenResolverData = React.useMemo(() => {
60
+ const extraData = mergeRight(extraProps, screenResolverExtraProps);
61
+
62
+ return {
63
+ extraData,
64
+ screenData: mergeRight(river, { groupId: extraData?.groupId }),
65
+ componentsMapExtraProps: mergeRight(componentsMapExtraProps, { groupId }),
66
+ };
67
+ }, [
68
+ extraProps,
69
+ screenResolverExtraProps,
70
+ river,
71
+ componentsMapExtraProps,
72
+ groupId,
73
+ ]);
56
74
 
57
75
  React.useEffect(() => {
58
76
  if (!isInsideContainer) {
59
- setScreenTitle(stringOrEmpty(screenData?.title));
60
- setScreenSummary(stringOrEmpty(screenData?.summary));
77
+ setScreenTitle(toStringOrEmpty(screenData?.title));
78
+ setScreenSummary(toStringOrEmpty(screenData?.summary));
61
79
  }
62
80
  }, [screenData.id]);
63
81
 
64
82
  React.useEffect(() => {
65
83
  if (feedData && !isInsideContainer) {
66
84
  if (feedData.title && feedData.title !== screenTitle) {
67
- setScreenTitle(stringOrEmpty(feedData.title));
85
+ setScreenTitle(toStringOrEmpty(feedData.title));
68
86
  }
69
87
 
70
88
  if (feedData.summary && feedData.summary !== screenSummary) {
71
- setScreenSummary(stringOrEmpty(feedData.summary));
89
+ setScreenSummary(toStringOrEmpty(feedData.summary));
72
90
  }
73
91
  } else {
74
- setScreenTitle(stringOrEmpty(screenData?.title));
75
- setScreenSummary(stringOrEmpty(screenData?.summary));
92
+ setScreenTitle(toStringOrEmpty(screenData?.title));
93
+ setScreenSummary(toStringOrEmpty(screenData?.summary));
76
94
  }
77
95
  }, [feedData, screenData, screenTitle, screenSummary]);
78
96
 
@@ -86,15 +104,13 @@ export const River = (props: Props) => {
86
104
  }
87
105
 
88
106
  if (river.type !== "general_content") {
89
- const extraData = { ...R.mergeRight(extraProps, screenResolverExtraProps) };
90
-
91
107
  return (
92
108
  <ScreenResolver
93
109
  screenType={river.type}
94
110
  screenId={screenId}
95
- screenData={R.merge(river, { groupId: extraData?.groupId })}
96
- componentsMapExtraProps={componentsMapExtraProps}
97
- {...extraData}
111
+ screenData={screenResolverData.screenData}
112
+ componentsMapExtraProps={screenResolverData.componentsMapExtraProps}
113
+ {...screenResolverData.extraData}
98
114
  />
99
115
  );
100
116
  }
@@ -106,6 +122,7 @@ export const River = (props: Props) => {
106
122
  isScreenWrappedInContainer={isInsideContainer}
107
123
  extraAnchorPointYOffset={extraAnchorPointYOffset}
108
124
  componentsMapExtraProps={componentsMapExtraProps}
125
+ groupId={groupId}
109
126
  />
110
127
  );
111
128
  };
@@ -1,11 +1,12 @@
1
- import { compose } from "ramda";
1
+ import { compose } from "@applicaster/zapp-react-native-utils/utils";
2
+
2
3
  import { River as RiverComponent } from "./River";
3
- import { withTvEventHandler } from "./withTVEventHandler";
4
4
  import { withComponentsMapOffsetContext } from "../../../Contexts/ComponentsMapOffsetContext";
5
5
  import { withRiverDataLoader } from "./withRiverDataLoader";
6
+ import { withFocusableGroupForContent } from "./withFocusableGroupForContent";
6
7
 
7
8
  export const River = compose(
8
- withTvEventHandler,
9
9
  withComponentsMapOffsetContext,
10
- withRiverDataLoader
10
+ withRiverDataLoader,
11
+ withFocusableGroupForContent
11
12
  )(RiverComponent);
@@ -0,0 +1,30 @@
1
+ import { toStringOrEmpty } from "..";
2
+
3
+ describe("toStringOrEmpty", () => {
4
+ test("returns empty string for undefined", () => {
5
+ expect(toStringOrEmpty(undefined)).toBe("");
6
+ });
7
+
8
+ test("returns empty string for null", () => {
9
+ expect(toStringOrEmpty(null)).toBe("");
10
+ });
11
+
12
+ test("converts number to string", () => {
13
+ expect(toStringOrEmpty(0)).toBe("0");
14
+ expect(toStringOrEmpty(123)).toBe("123");
15
+ expect(toStringOrEmpty(-42)).toBe("-42");
16
+ });
17
+
18
+ test("returns string as is", () => {
19
+ expect(toStringOrEmpty("hello")).toBe("hello");
20
+ expect(toStringOrEmpty("")).toBe("");
21
+ });
22
+
23
+ test("works with numeric strings", () => {
24
+ expect(toStringOrEmpty("123")).toBe("123");
25
+ });
26
+
27
+ test("does not throw on falsy values like 0", () => {
28
+ expect(toStringOrEmpty(0)).toBe("0");
29
+ });
30
+ });
@@ -0,0 +1,4 @@
1
+ import { isNil } from "@applicaster/zapp-react-native-utils/utils";
2
+
3
+ export const toStringOrEmpty = (value: unknown): string =>
4
+ isNil(value) ? "" : String(value);
@@ -0,0 +1,71 @@
1
+ import * as React from "react";
2
+ import { View, StyleSheet } from "react-native";
3
+
4
+ import { FocusableGroup } from "@applicaster/zapp-react-native-ui-components/Components/FocusableGroup";
5
+ import { riverFocusManager } from "@applicaster/zapp-react-native-utils/appUtils/RiverFocusManager";
6
+
7
+ import { topMenuLayoutChange$ } from "@applicaster/zapp-react-native-tvos-app/Layout/topMenu";
8
+
9
+ const styles = StyleSheet.create({
10
+ flexOne: {
11
+ flex: 1,
12
+ },
13
+ });
14
+
15
+ export const withFocusableGroupForContent = (Component) => {
16
+ return function WithFocusableGroupForContent(props) {
17
+ const { screenId, isInsideContainer } = props;
18
+
19
+ const [topMenuHeight, setTopMenuHeight] = React.useState(0);
20
+
21
+ React.useEffect(() => {
22
+ const subscription = topMenuLayoutChange$.subscribe((layout) => {
23
+ setTopMenuHeight(layout.height);
24
+ });
25
+
26
+ return () => {
27
+ subscription.unsubscribe();
28
+ };
29
+ }, []);
30
+
31
+ const focusableId = React.useMemo(
32
+ () =>
33
+ riverFocusManager.screenFocusableGroupId({
34
+ screenId,
35
+ isInsideContainer,
36
+ }),
37
+ [screenId, isInsideContainer]
38
+ );
39
+
40
+ if (isInsideContainer) {
41
+ return <Component {...props} />;
42
+ }
43
+
44
+ return (
45
+ <FocusableGroup
46
+ key={focusableId}
47
+ id={focusableId}
48
+ // The top menu is rendered in its own FocusableGroup, anchored at the top of the screen.
49
+ // When the "content" FocusableGroup starts at y = 0 as well, the two groups visually overlap.
50
+ // On TvOS platform this overlap can confuse the focus engine, because the focusable bounds of
51
+ // the top-menu group and the content group intersect, leading to erratic navigation between
52
+ // the menu and the content (e.g. unexpected jumps or focus getting "stuck").
53
+ //
54
+ // To avoid this, we shift the entire content FocusableGroup down by the dynamic top menu
55
+ // height (marginTop: topMenuHeight). This separates the focus regions of the two groups in
56
+ // focus space, so they no longer intersect.
57
+ //
58
+ // The inner <View> below then applies the inverse margin (marginTop: -topMenuHeight) so that
59
+ // the actual visual position of the content on screen does not change; only the focusable
60
+ // bounds of the outer group are offset.
61
+ style={[styles.flexOne, { marginTop: topMenuHeight }]}
62
+ // this group does not have parent
63
+ groupId={undefined}
64
+ >
65
+ <View style={[styles.flexOne, { marginTop: -1 * topMenuHeight }]}>
66
+ <Component {...props} groupId={focusableId} />
67
+ </View>
68
+ </FocusableGroup>
69
+ );
70
+ };
71
+ };
@@ -88,6 +88,7 @@ exports[`withConfigurationProvider correctly passes all the configuration keys c
88
88
  tab_cell_padding_right={10}
89
89
  tab_cell_padding_top={14}
90
90
  tablet_theme={false}
91
+ tabs_screen_background_color="transparent"
91
92
  target={false}
92
93
  target_screen_switch={false}
93
94
  text_label_active_font_color="rgba(239, 239, 239, 0.5)"
@@ -208,4 +208,5 @@ export const keysMap: Record<string, Function> = {
208
208
  tab_bar_item_margin_right: castOrDefaultValue(Number, 0),
209
209
  tab_bar_item_margin_bottom: castOrDefaultValue(Number, 0),
210
210
  tab_bar_item_margin_left: castOrDefaultValue(Number, 0),
211
+ tabs_screen_background_color: castOrDefaultValue(R.identity, "transparent"),
211
212
  };
@@ -3,7 +3,7 @@ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUt
3
3
 
4
4
  import * as zappPipesRedux from "@applicaster/zapp-react-native-redux/ZappPipes";
5
5
  import configureStore from "redux-mock-store";
6
- import thunk from "redux-thunk";
6
+ import { thunk } from "redux-thunk";
7
7
 
8
8
  import { zappPipesDataConnector } from "../index";
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-ui-components",
3
- "version": "15.0.0-alpha.1812021122",
3
+ "version": "15.0.0-alpha.1954787293",
4
4
  "description": "Applicaster Zapp React Native ui components for the Quick Brick App",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -28,10 +28,10 @@
28
28
  },
29
29
  "homepage": "https://github.com/applicaster/quickbrick#readme",
30
30
  "dependencies": {
31
- "@applicaster/applicaster-types": "15.0.0-alpha.1812021122",
32
- "@applicaster/zapp-react-native-bridge": "15.0.0-alpha.1812021122",
33
- "@applicaster/zapp-react-native-redux": "15.0.0-alpha.1812021122",
34
- "@applicaster/zapp-react-native-utils": "15.0.0-alpha.1812021122",
31
+ "@applicaster/applicaster-types": "15.0.0-alpha.1954787293",
32
+ "@applicaster/zapp-react-native-bridge": "15.0.0-alpha.1954787293",
33
+ "@applicaster/zapp-react-native-redux": "15.0.0-alpha.1954787293",
34
+ "@applicaster/zapp-react-native-utils": "15.0.0-alpha.1954787293",
35
35
  "fast-json-stable-stringify": "^2.1.0",
36
36
  "promise": "^8.3.0",
37
37
  "url": "^0.11.0",
@@ -1,27 +0,0 @@
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
- };