@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5114122134 → 12.0.0-alpha.5208030571
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.
- package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
- package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
- package/Components/AudioPlayer/helpers.tsx +1 -0
- package/Components/BaseFocusable/index.tsx +1 -1
- package/Components/Cell/Cell.tsx +7 -3
- package/Components/Cell/CellWithFocusable.tsx +38 -19
- package/Components/Cell/TvOSCellComponent.tsx +2 -2
- package/Components/ErrorScreen/index.tsx +1 -1
- package/Components/FocusableCell/index.tsx +7 -7
- package/Components/FocusableGroup/index.tsx +1 -0
- package/Components/FocusableList/FocusableChild.ts +24 -14
- package/Components/FocusableList/FocusableItem.tsx +1 -1
- package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
- package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
- package/Components/FocusableList/index.tsx +26 -25
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +4 -1
- package/Components/HookRenderer/index.tsx +1 -6
- package/Components/Image/Image.android.tsx +3 -25
- package/Components/Image/Image.tsx +18 -2
- package/Components/Image/utils/index.ts +39 -0
- package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
- package/Components/Layout/TV/__tests__/index.test.tsx +1 -34
- package/Components/Layout/TV/index.tsx +141 -58
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +0 -1
- package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
- package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +8 -10
- package/Components/MasterCell/utils/index.ts +16 -5
- package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
- package/Components/ModalComponent/Button/Item.tsx +82 -0
- package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
- package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
- package/Components/ModalComponent/Button/assets.ts +4 -0
- package/Components/ModalComponent/Button/index.tsx +111 -0
- package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
- package/Components/ModalComponent/Header/Title.tsx +70 -0
- package/Components/ModalComponent/Header/assets.ts +4 -0
- package/Components/ModalComponent/Header/index.tsx +71 -0
- package/Components/ModalComponent/Header/utils.ts +79 -0
- package/Components/ModalComponent/index.tsx +38 -0
- package/Components/ModalComponent/utils.ts +126 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +63 -31
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
- package/Components/PlayerImageBackground/index.tsx +34 -8
- package/Components/River/ComponentsMap/ComponentsMap.tsx +90 -2
- package/Components/River/TV/River.tsx +2 -3
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
- package/Components/River/__tests__/componentsMap.test.js +7 -0
- package/Components/River/useScreenConfiguration.ts +2 -2
- package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
- package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
- package/Components/RouteManager/index.tsx +5 -3
- package/Components/Screen/TV/index.web.tsx +151 -0
- package/Components/Screen/__tests__/Screen.test.tsx +88 -74
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +24 -6
- package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
- package/Components/Screen/index.tsx +103 -43
- package/Components/Screen/navigationHandler.ts +0 -82
- package/Components/ScreenResolver/index.tsx +4 -2
- package/Components/Tabs/TV/styles.ts +1 -1
- package/Components/TrackedView/index.tsx +6 -3
- package/Components/VideoLive/LiveImageManager.ts +37 -10
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +81 -72
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +18 -28
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +342 -0
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +151 -0
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +438 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +143 -0
- package/Components/VideoModal/ModalAnimation/index.ts +15 -0
- package/Components/VideoModal/ModalAnimation/utils.ts +299 -0
- package/Components/VideoModal/PlayerDetails.tsx +3 -0
- package/Components/VideoModal/PlayerWrapper.tsx +65 -42
- package/Components/VideoModal/VideoModal.tsx +8 -25
- package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +0 -2
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +5 -1
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +246 -166
- package/Components/VideoModal/utils.ts +3 -3
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
- package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +1 -1
- package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
- package/Components/Viewport/ViewportTracker/index.tsx +1 -0
- package/Components/Viewport/utils.js +1 -1
- package/Components/ZappUIComponent/index.tsx +22 -12
- package/Components/index.js +2 -0
- package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
- package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
- package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
- package/Contexts/ScreenContext/index.tsx +95 -29
- package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
- package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
- package/package.json +5 -5
- package/Components/VideoModal/hooks/usePosition.ts +0 -50
- /package/Contexts/{index.js → index.ts} +0 -0
|
@@ -19,6 +19,7 @@ type Props = {
|
|
|
19
19
|
modalHeight?: number;
|
|
20
20
|
modalWidth?: number;
|
|
21
21
|
mode: VideoModalMode;
|
|
22
|
+
groupId?: string;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const YOUTUBE_PLUGIN_ID = "youtube-player-qb";
|
|
@@ -93,6 +94,7 @@ export function HandlePlayable({
|
|
|
93
94
|
modalHeight,
|
|
94
95
|
modalWidth,
|
|
95
96
|
mode,
|
|
97
|
+
groupId,
|
|
96
98
|
}: Props): React.ReactElement | null {
|
|
97
99
|
const state = usePickFromState();
|
|
98
100
|
|
|
@@ -104,7 +106,7 @@ export function HandlePlayable({
|
|
|
104
106
|
const [Player, playerModuleProperties] = getPlayer(item, state);
|
|
105
107
|
|
|
106
108
|
const { module: CastPlugin } =
|
|
107
|
-
findPluginByIdentifier(CHROMECAST_PLUGIN_ID, state.plugins) || {};
|
|
109
|
+
findPluginByIdentifier(CHROMECAST_PLUGIN_ID, state.plugins, true) || {};
|
|
108
110
|
|
|
109
111
|
const [playable, setPlayable] =
|
|
110
112
|
React.useState<Nullable<PlayableComponent>>(null);
|
|
@@ -167,6 +169,7 @@ export function HandlePlayable({
|
|
|
167
169
|
isModal={isModal}
|
|
168
170
|
mode={mode}
|
|
169
171
|
disableCastAction={playerModuleProperties?.disableCastAction}
|
|
172
|
+
groupId={groupId}
|
|
170
173
|
/>
|
|
171
174
|
);
|
|
172
175
|
}
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { withNavigator } from "@applicaster/zapp-react-native-ui-components/Decorators/Navigator/";
|
|
4
|
-
import { HookRenderer as HookRendererComponent } from "./HookRenderer";
|
|
5
|
-
|
|
6
|
-
export const HookRenderer = R.compose(withNavigator)(HookRendererComponent);
|
|
1
|
+
export { HookRenderer } from "./HookRenderer";
|
|
@@ -3,32 +3,10 @@ import FastImage, {
|
|
|
3
3
|
FastImageProps,
|
|
4
4
|
} from "@applicaster/react-native-fast-image";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const source = React.useMemo(() => {
|
|
8
|
-
if (typeof props.source === "number") {
|
|
9
|
-
return props.source;
|
|
10
|
-
}
|
|
6
|
+
import { useImageSource } from "./utils";
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
headers: props.source.headers,
|
|
15
|
-
priority: props.source.priority,
|
|
16
|
-
cache: props.source.cache,
|
|
17
|
-
maxWidth: props.source.maxWidth,
|
|
18
|
-
maxHeight: props.source.maxHeight,
|
|
19
|
-
};
|
|
20
|
-
}, [
|
|
21
|
-
...(typeof props.source === "object"
|
|
22
|
-
? [
|
|
23
|
-
props.source?.uri,
|
|
24
|
-
props.source?.priority,
|
|
25
|
-
props.source?.cache,
|
|
26
|
-
props.source?.headers,
|
|
27
|
-
props.source?.maxWidth,
|
|
28
|
-
props.source?.maxHeight,
|
|
29
|
-
]
|
|
30
|
-
: []),
|
|
31
|
-
]);
|
|
8
|
+
export const QBImage = (props: FastImageProps) => {
|
|
9
|
+
const source = useImageSource(props.source);
|
|
32
10
|
|
|
33
11
|
return <FastImage {...props} source={source} />;
|
|
34
12
|
};
|
|
@@ -1,8 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
Image,
|
|
4
|
+
ImageBackground,
|
|
5
|
+
ImageProps,
|
|
6
|
+
ImageStyle,
|
|
7
|
+
StyleProp,
|
|
8
|
+
} from "react-native";
|
|
2
9
|
|
|
3
10
|
export interface Props extends ImageProps {
|
|
4
11
|
style?: StyleProp<ImageStyle> | undefined;
|
|
5
12
|
}
|
|
6
13
|
|
|
7
14
|
// TODO - React.FC<Props> QBImage should process incoming url and apply any transformations if needed
|
|
8
|
-
export const QBImage =
|
|
15
|
+
export const QBImage = ({
|
|
16
|
+
children,
|
|
17
|
+
...props
|
|
18
|
+
}: Props & { children?: React.ReactNode }) => {
|
|
19
|
+
if (children) {
|
|
20
|
+
return <ImageBackground {...props}>{children}</ImageBackground>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return <Image {...props} />;
|
|
24
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import FastImage, { Source } from "@applicaster/react-native-fast-image";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { ImageRequireSource } from "react-native";
|
|
4
|
+
|
|
5
|
+
function getSourceCache(cache: Source["cache"]) {
|
|
6
|
+
return cache || FastImage.cacheControl.web;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const useImageSource = (source?: Source | ImageRequireSource) => {
|
|
10
|
+
return useMemo(() => {
|
|
11
|
+
if (source) {
|
|
12
|
+
if (typeof source === "number") {
|
|
13
|
+
return source;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
uri: source.uri,
|
|
18
|
+
headers: source.headers,
|
|
19
|
+
priority: source.priority,
|
|
20
|
+
cache: getSourceCache(source?.cache),
|
|
21
|
+
maxWidth: source.maxWidth,
|
|
22
|
+
maxHeight: source.maxHeight,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return undefined;
|
|
27
|
+
}, [
|
|
28
|
+
...(typeof source === "object"
|
|
29
|
+
? [
|
|
30
|
+
source?.uri,
|
|
31
|
+
source?.priority,
|
|
32
|
+
source?.cache,
|
|
33
|
+
source?.headers,
|
|
34
|
+
source?.maxWidth,
|
|
35
|
+
source?.maxHeight,
|
|
36
|
+
]
|
|
37
|
+
: []),
|
|
38
|
+
]);
|
|
39
|
+
};
|
|
@@ -60,7 +60,7 @@ export function ScreenLayoutContextProvider(props: {
|
|
|
60
60
|
initialState
|
|
61
61
|
);
|
|
62
62
|
|
|
63
|
-
const
|
|
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={
|
|
72
|
+
<ScreenLayoutContext.Provider value={screenLayoutValue}>
|
|
73
73
|
{props.children}
|
|
74
74
|
</ScreenLayoutContext.Provider>
|
|
75
75
|
);
|
|
@@ -12,19 +12,7 @@ jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
|
|
|
12
12
|
useRiverInitialState: () => ({}),
|
|
13
13
|
}));
|
|
14
14
|
|
|
15
|
-
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen"
|
|
16
|
-
useCurrentScreenData: jest.fn(() => ({
|
|
17
|
-
navigations: [
|
|
18
|
-
{
|
|
19
|
-
category: "menu",
|
|
20
|
-
navigation_type: "menu",
|
|
21
|
-
styles: {
|
|
22
|
-
hide_secondary_level_menu: false,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
],
|
|
26
|
-
})),
|
|
27
|
-
}));
|
|
15
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen");
|
|
28
16
|
|
|
29
17
|
const Layout = require("..").default;
|
|
30
18
|
|
|
@@ -125,25 +113,4 @@ describe("Layout TV", () => {
|
|
|
125
113
|
expect(bgComponent.props.testProp).toBe(true);
|
|
126
114
|
});
|
|
127
115
|
});
|
|
128
|
-
|
|
129
|
-
describe("props passed to navbar component", () => {
|
|
130
|
-
it("passes correct props", () => {
|
|
131
|
-
const { getByTestId } = render(
|
|
132
|
-
<Layout
|
|
133
|
-
Components={Components}
|
|
134
|
-
ComponentsExtraProps={{
|
|
135
|
-
NavBar: { testProp: true },
|
|
136
|
-
}}
|
|
137
|
-
>
|
|
138
|
-
<Screen />
|
|
139
|
-
</Layout>,
|
|
140
|
-
{ wrapper }
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
const navbarComponent = getByTestId("nav-bar-component");
|
|
144
|
-
expect(navbarComponent.props.isVisible).toBe(isNavBarVisible);
|
|
145
|
-
expect(navbarComponent.props.menuPlugin).toBe(plugins[0]);
|
|
146
|
-
expect(navbarComponent.props.testProp).toBe(true);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
116
|
});
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
+
import { View, StyleSheet } from "react-native";
|
|
3
4
|
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
4
|
-
import {
|
|
5
|
-
useIsNavBarVisible,
|
|
6
|
-
useNavigation,
|
|
7
|
-
} from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
5
|
+
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
8
6
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
7
|
+
import { useNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
8
|
+
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
getNavigationProps,
|
|
12
|
+
getPathAttributes,
|
|
12
13
|
resolveNavigationPlugin,
|
|
13
14
|
routeIsPlayerScreen,
|
|
14
15
|
} from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
@@ -20,9 +21,25 @@ import { ScreenContainer } from "./ScreenContainer";
|
|
|
20
21
|
import { TransitionOverlay } from "./TransitionOverlay";
|
|
21
22
|
|
|
22
23
|
import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
|
|
23
|
-
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
24
24
|
import { PathnameContext } from "../../../Contexts/PathnameContext";
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
import { FocusableGroup } from "../../FocusableGroup";
|
|
27
|
+
import { Screen } from "../../Screen/TV/index.web";
|
|
28
|
+
|
|
29
|
+
export const ScreenDataContext = React.createContext(null);
|
|
30
|
+
|
|
31
|
+
const styles = StyleSheet.create({
|
|
32
|
+
container: {
|
|
33
|
+
position: "absolute",
|
|
34
|
+
width: 1920,
|
|
35
|
+
height: 1080,
|
|
36
|
+
backgroundColor: "black",
|
|
37
|
+
// opacity: 0.5,
|
|
38
|
+
|
|
39
|
+
borderWidth: 4,
|
|
40
|
+
borderColor: "magenta",
|
|
41
|
+
},
|
|
42
|
+
});
|
|
26
43
|
|
|
27
44
|
type Components = {
|
|
28
45
|
NavBar: React.ComponentType<any>;
|
|
@@ -30,20 +47,77 @@ type Components = {
|
|
|
30
47
|
};
|
|
31
48
|
|
|
32
49
|
type ComponentsExtraProps = {
|
|
33
|
-
Background?: Record<string, any>;
|
|
34
50
|
NavBar?: Record<string, any>;
|
|
51
|
+
Background?: Record<string, any>;
|
|
35
52
|
};
|
|
36
53
|
|
|
37
54
|
type Props = {
|
|
38
55
|
Components: Components;
|
|
39
|
-
|
|
40
|
-
|
|
56
|
+
componentsExtraProps?: ComponentsExtraProps;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const StackNavigator = ({ Components }: Props) => {
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
const { mainStack, currentRoute } = useNavigation();
|
|
62
|
+
|
|
63
|
+
// console.log("debug_2", "DEBUG", { mainStack });
|
|
64
|
+
|
|
65
|
+
const renderScreen = ({ route, action, key, state }, index) => {
|
|
66
|
+
const pathAttributes = getPathAttributes({ pathname: route });
|
|
67
|
+
|
|
68
|
+
if (R.isEmpty(pathAttributes)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// console.log("debug_2", "StackNavigator - renderScreen", {
|
|
73
|
+
// index,
|
|
74
|
+
// route,
|
|
75
|
+
// state,
|
|
76
|
+
// key,
|
|
77
|
+
// action,
|
|
78
|
+
// });
|
|
79
|
+
|
|
80
|
+
const id = `${route}`;
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<FocusableGroup
|
|
84
|
+
id={id}
|
|
85
|
+
preferredFocus={route === currentRoute}
|
|
86
|
+
excludeFromFocusSearching
|
|
87
|
+
key={id}
|
|
88
|
+
>
|
|
89
|
+
<View style={styles.container}>
|
|
90
|
+
<ScreenDataContext.Provider value={state}>
|
|
91
|
+
<PathnameContext.Provider value={route}>
|
|
92
|
+
<Screen route={route} Components={Components} />
|
|
93
|
+
</PathnameContext.Provider>
|
|
94
|
+
</ScreenDataContext.Provider>
|
|
95
|
+
</View>
|
|
96
|
+
</FocusableGroup>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return mainStack.map(renderScreen);
|
|
41
101
|
};
|
|
42
102
|
|
|
103
|
+
const TransitionerTV = ({ componentsExtraProps, Components }: Props) => {
|
|
104
|
+
return (
|
|
105
|
+
<>
|
|
106
|
+
<ScreenLayoutContextProvider>
|
|
107
|
+
<StackNavigator
|
|
108
|
+
componentsExtraProps={componentsExtraProps}
|
|
109
|
+
Components={Components}
|
|
110
|
+
/>
|
|
111
|
+
</ScreenLayoutContextProvider>
|
|
112
|
+
</>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// @ts-ignore
|
|
43
117
|
const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
44
118
|
const navigator = useNavigation();
|
|
45
119
|
const theme = useTheme();
|
|
46
|
-
const { title } = useNavbarState();
|
|
120
|
+
const { title, visible } = useNavbarState();
|
|
47
121
|
const { activeRiver, currentRoute } = navigator;
|
|
48
122
|
|
|
49
123
|
const {
|
|
@@ -80,66 +154,75 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
|
80
154
|
[]
|
|
81
155
|
);
|
|
82
156
|
|
|
83
|
-
const navBarVisible = useIsNavBarVisible();
|
|
84
|
-
|
|
85
157
|
const [navBarReady, setNavBarReady] = React.useState(false);
|
|
86
158
|
|
|
159
|
+
const navBarContainer = (
|
|
160
|
+
<NavBarContainer isVisible={visible} onReady={setNavBarReady}>
|
|
161
|
+
<NavBar
|
|
162
|
+
ref={ComponentsExtraProps?.NavBar?.ref}
|
|
163
|
+
isVisible={visible && !routeIsPlayerScreen(currentRoute)}
|
|
164
|
+
menuPlugin={menuPlugin}
|
|
165
|
+
navigationProps={navigationProps}
|
|
166
|
+
{...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
|
|
167
|
+
/>
|
|
168
|
+
</NavBarContainer>
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const isTVOS = isApplePlatform();
|
|
172
|
+
|
|
87
173
|
if (!appReady) {
|
|
88
174
|
return null;
|
|
89
175
|
}
|
|
90
176
|
|
|
91
177
|
return (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
/>
|
|
108
|
-
</NavBarContainer>
|
|
109
|
-
) : null}
|
|
110
|
-
|
|
111
|
-
{navBarReady ? (
|
|
112
|
-
/**
|
|
178
|
+
// @ts-ignore
|
|
179
|
+
<TransitionerTV
|
|
180
|
+
componentsExtraProps={ComponentsExtraProps}
|
|
181
|
+
Components={Components}
|
|
182
|
+
>
|
|
183
|
+
<LayoutContainer>
|
|
184
|
+
<ScreenLayoutContextProvider>
|
|
185
|
+
<Components.Background
|
|
186
|
+
backgroundColor={backgroundColor}
|
|
187
|
+
backgroundImageUrl={backgroundImageUrl}
|
|
188
|
+
{...R.omit(["ref"])(ComponentsExtraProps?.Background)}
|
|
189
|
+
>
|
|
190
|
+
{!isTVOS ? navBarContainer : null}
|
|
191
|
+
{navBarReady ? (
|
|
192
|
+
/**
|
|
113
193
|
Need to re-mount this component on each navigation event
|
|
114
194
|
It's a hack to work around the fact that we don't support
|
|
115
195
|
correct screen rendering in our navigator (shared currentRoute state)
|
|
116
196
|
**/
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
ref={ComponentsExtraProps?.NavBar?.ref}
|
|
130
|
-
isVisible={navBarVisible && !routeIsPlayerScreen(currentRoute)}
|
|
131
|
-
menuPlugin={menuPlugin}
|
|
132
|
-
navigationProps={navigationProps}
|
|
133
|
-
{...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
|
|
134
|
-
/>
|
|
135
|
-
</NavBarContainer>
|
|
136
|
-
) : null}
|
|
137
|
-
|
|
138
|
-
<TransitionOverlay />
|
|
139
|
-
</Components.Background>
|
|
140
|
-
</ScreenLayoutContextProvider>
|
|
141
|
-
</LayoutContainer>
|
|
197
|
+
<PathnameContext.Provider value={navigator.currentRoute}>
|
|
198
|
+
<ScreenContainer>{children}</ScreenContainer>
|
|
199
|
+
</PathnameContext.Provider>
|
|
200
|
+
) : null}
|
|
201
|
+
{/** Render NavBarContainer over ScreenContainer to let tvOS native
|
|
202
|
+
focus manager works correctly to focus nav bar items first **/}
|
|
203
|
+
{isTVOS ? navBarContainer : null}
|
|
204
|
+
<TransitionOverlay />
|
|
205
|
+
</Components.Background>
|
|
206
|
+
</ScreenLayoutContextProvider>
|
|
207
|
+
</LayoutContainer>
|
|
208
|
+
</TransitionerTV>
|
|
142
209
|
);
|
|
143
210
|
};
|
|
144
211
|
|
|
145
212
|
export default Layout;
|
|
213
|
+
|
|
214
|
+
/// Transitioner:
|
|
215
|
+
/*
|
|
216
|
+
Component: Transitioner
|
|
217
|
+
Responsibilities:
|
|
218
|
+
- Transitione between screens.
|
|
219
|
+
Tehere is no transition on Tv so no need for transitioner.
|
|
220
|
+
|
|
221
|
+
Component: StackNavigator
|
|
222
|
+
Responsibilities:
|
|
223
|
+
- Render the screens in the stack.
|
|
224
|
+
- Render the NavBar? I guess navbar should be part of the screen
|
|
225
|
+
- Render the screen.
|
|
226
|
+
|
|
227
|
+
Q
|
|
228
|
+
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
compact,
|
|
3
3
|
ifElseFocusedSelected,
|
|
4
|
+
ifElseSelected,
|
|
4
5
|
} from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
5
6
|
|
|
6
7
|
import { getPositionStyles } from "./utils";
|
|
@@ -12,9 +13,15 @@ type Props = {
|
|
|
12
13
|
value: Function;
|
|
13
14
|
state: string;
|
|
14
15
|
visible?: Boolean;
|
|
16
|
+
isMobile?: Boolean;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
|
-
export const CellBadge = ({
|
|
19
|
+
export const CellBadge = ({
|
|
20
|
+
value,
|
|
21
|
+
state,
|
|
22
|
+
visible = true,
|
|
23
|
+
isMobile = false,
|
|
24
|
+
}: Props) => {
|
|
18
25
|
if (!value("cell_badge_switch") || !visible) {
|
|
19
26
|
return null;
|
|
20
27
|
}
|
|
@@ -45,13 +52,19 @@ export const CellBadge = ({ value, state, visible = true }: Props) => {
|
|
|
45
52
|
data: [
|
|
46
53
|
{
|
|
47
54
|
func: () => {
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
return isMobile
|
|
56
|
+
? ifElseSelected(
|
|
57
|
+
state,
|
|
58
|
+
value("cell_badge_selected"),
|
|
59
|
+
value("cell_badge_default")
|
|
60
|
+
)
|
|
61
|
+
: ifElseFocusedSelected(
|
|
62
|
+
state,
|
|
63
|
+
value("cell_badge_focused"),
|
|
64
|
+
value("cell_badge_selected"),
|
|
65
|
+
value("cell_badge_focused_selected"),
|
|
66
|
+
value("cell_badge_default")
|
|
67
|
+
);
|
|
55
68
|
},
|
|
56
69
|
args: [],
|
|
57
70
|
propName: "uri",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { ImageStyle, View } from "react-native";
|
|
3
3
|
import * as R from "ramda";
|
|
4
|
-
import
|
|
4
|
+
import { QBImage } from "@applicaster/zapp-react-native-ui-components/Components/Image";
|
|
5
5
|
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
6
|
-
import { toFiniteNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
7
6
|
|
|
8
7
|
import { useImageSourceWithDefault, useCheckFailedUri } from "./hooks";
|
|
8
|
+
import useImageSize from "./hooks/useImageSize";
|
|
9
9
|
|
|
10
10
|
type Source = {
|
|
11
11
|
uri: string;
|
|
@@ -19,32 +19,13 @@ type Props = Record<string, any> & {
|
|
|
19
19
|
withDimensions: (source: Source) => Source;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
const MemoizedImage = React.memo(
|
|
23
|
-
|
|
24
|
-
const normalize = Math.ceil;
|
|
22
|
+
const MemoizedImage = React.memo(QBImage, R.equals);
|
|
25
23
|
|
|
26
24
|
const isValidExternalSource = (source): boolean =>
|
|
27
25
|
source?.uri?.startsWith("http") ||
|
|
28
26
|
source?.uri?.startsWith("data:image") ||
|
|
29
27
|
source?.uri?.startsWith("file://");
|
|
30
28
|
|
|
31
|
-
const isValidSize = (size: unknown): boolean =>
|
|
32
|
-
toFiniteNumberWithDefault(0, size) > 0;
|
|
33
|
-
|
|
34
|
-
const getSourceWithMaxSizes = (applyMaxSize, style, source) => {
|
|
35
|
-
const { width, height } = style;
|
|
36
|
-
|
|
37
|
-
if (!applyMaxSize || !isValidSize(width) || !isValidSize(height)) {
|
|
38
|
-
return source;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
...source,
|
|
43
|
-
maxWidth: normalize(width),
|
|
44
|
-
maxHeight: normalize(height),
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
|
|
48
29
|
export default function Image({
|
|
49
30
|
style,
|
|
50
31
|
uri,
|
|
@@ -82,10 +63,12 @@ export default function Image({
|
|
|
82
63
|
|
|
83
64
|
const applyMaxSize = toBooleanWithDefaultFalse(otherProps?.applyMaxSize);
|
|
84
65
|
|
|
85
|
-
const sourceWithMaxSizes =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
66
|
+
const sourceWithMaxSizes = useImageSize({
|
|
67
|
+
style,
|
|
68
|
+
applyMaxSize,
|
|
69
|
+
source,
|
|
70
|
+
withDimensions,
|
|
71
|
+
});
|
|
89
72
|
|
|
90
73
|
if (isFailedToLoadUri(uri) || !isValidExternalSource(source)) {
|
|
91
74
|
return <View style={style} />;
|
|
@@ -43,14 +43,16 @@ function Image({
|
|
|
43
43
|
[source, error]
|
|
44
44
|
);
|
|
45
45
|
|
|
46
|
+
const _source = shouldRenderImg
|
|
47
|
+
? withDimensions(source)
|
|
48
|
+
: defaultPlaceHolderImage;
|
|
49
|
+
|
|
46
50
|
return (
|
|
47
51
|
// @ts-ignore
|
|
48
52
|
<MemoizedImage
|
|
49
53
|
style={style as ImageStyle}
|
|
50
54
|
onError={() => setErrorState(true)}
|
|
51
|
-
source={
|
|
52
|
-
shouldRenderImg ? withDimensions(source) : defaultPlaceHolderImage
|
|
53
|
-
}
|
|
55
|
+
source={_source?.uri ? _source : undefined}
|
|
54
56
|
{...R.omit(["source"], otherProps)}
|
|
55
57
|
/>
|
|
56
58
|
);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { ImageStyle } from "react-native";
|
|
3
|
+
import { toFiniteNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
4
|
+
|
|
5
|
+
type Source = {
|
|
6
|
+
uri: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type Props = Record<string, any> & {
|
|
10
|
+
style: ImageStyle;
|
|
11
|
+
uri?: string;
|
|
12
|
+
placeholderImage?: string;
|
|
13
|
+
entry?: ZappEntry | ZappFeed;
|
|
14
|
+
withDimensions: (source: Source) => Source;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const normalize = Math.ceil;
|
|
18
|
+
|
|
19
|
+
const isValidSize = (size: unknown): boolean =>
|
|
20
|
+
toFiniteNumberWithDefault(0, size) > 0;
|
|
21
|
+
|
|
22
|
+
type WidthDimensions = { aspectRatio?: number; width: number; height?: number };
|
|
23
|
+
type HeightDimensions = {
|
|
24
|
+
aspectRatio?: number;
|
|
25
|
+
width?: number;
|
|
26
|
+
height: number;
|
|
27
|
+
};
|
|
28
|
+
type FullDimensions = { aspectRatio?: number; width: number; height: number };
|
|
29
|
+
type Dimensions = WidthDimensions | HeightDimensions | FullDimensions;
|
|
30
|
+
|
|
31
|
+
const calculateDimensions = ({ aspectRatio, width, height }: Dimensions) => {
|
|
32
|
+
if (aspectRatio) {
|
|
33
|
+
return {
|
|
34
|
+
width: width || height * aspectRatio,
|
|
35
|
+
height: height || width / aspectRatio,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { width, height };
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const getSourceWithMaxSizes = (applyMaxSize, style, source) => {
|
|
43
|
+
const { width, height } = calculateDimensions(style);
|
|
44
|
+
|
|
45
|
+
if (!applyMaxSize || !isValidSize(width) || !isValidSize(height)) {
|
|
46
|
+
return source;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
...source,
|
|
51
|
+
maxWidth: normalize(width),
|
|
52
|
+
maxHeight: normalize(height),
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default function useImageSize(props: Props) {
|
|
57
|
+
const { style, applyMaxSize, source, withDimensions } = props;
|
|
58
|
+
|
|
59
|
+
const sourceWithMaxSizes = React.useMemo(
|
|
60
|
+
() => withDimensions(getSourceWithMaxSizes(applyMaxSize, style, source)),
|
|
61
|
+
[applyMaxSize, style?.height, style?.width, source?.uri, withDimensions]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return sourceWithMaxSizes;
|
|
65
|
+
}
|