@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5471145113 → 12.0.0-alpha.5521005225
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/BaseFocusable/index.tsx +2 -2
- package/Components/Cell/Cell.tsx +26 -5
- package/Components/Cell/CellWithFocusable.tsx +6 -4
- package/Components/Cell/TvOSCellComponent.tsx +3 -7
- package/Components/Focusable/Focusable.tsx +6 -1
- package/Components/FocusableGroup/index.tsx +10 -2
- package/Components/Layout/TV/ScreenContainer.tsx +79 -5
- package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
- package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
- package/Components/Layout/TV/__tests__/index.test.tsx +2 -1
- package/Components/Layout/TV/index.tsx +11 -65
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -1
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +7 -2
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +20 -0
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +6 -1
- package/Components/MasterCell/dataAdapter.ts +21 -7
- package/Components/MasterCell/index.tsx +3 -1
- package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
- package/Components/MasterCell/utils/index.ts +5 -1
- package/Components/Navigator/StackNavigator.tsx +14 -5
- package/Components/PlayerContainer/PlayerContainer.tsx +2 -5
- package/Components/Screen/TV/index.web.tsx +9 -23
- package/Components/Screen/index.tsx +27 -58
- package/Components/Screen/navigationHandler.ts +5 -0
- package/Components/ScreenResolver/index.tsx +6 -11
- package/Components/Transitioner/Scene.tsx +12 -11
- package/Components/Transitioner/Transitioner.tsx +17 -15
- package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +8 -4
- package/Components/VideoLive/LiveImageManager.ts +10 -0
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +52 -24
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +26 -24
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +2 -14
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +56 -43
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +42 -32
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +104 -86
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +32 -4
- package/Components/VideoModal/ModalAnimation/utils.ts +7 -4
- package/Components/VideoModal/PlayerWrapper.tsx +2 -2
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +32 -12
- package/Components/VideoModal/hooks/useModalSize.ts +10 -12
- package/Components/VideoModal/utils.ts +5 -2
- package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
- package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
- package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
- package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
- package/Contexts/FocusableGroupContext/index.ts +7 -0
- package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
- package/Contexts/ScreenContext/index.tsx +0 -6
- package/package.json +5 -5
- /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
- /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
|
@@ -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 =
|
|
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) {
|
|
@@ -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,7 +31,7 @@ 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
37
|
<HorizontalSeparator
|
|
@@ -37,14 +37,28 @@ function retrieveData(entry, func, args) {
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
export function configInflater(
|
|
40
|
-
entry,
|
|
41
|
-
{
|
|
40
|
+
entry: any,
|
|
41
|
+
{
|
|
42
|
+
type,
|
|
43
|
+
style,
|
|
44
|
+
additionalProps = {},
|
|
45
|
+
data = [],
|
|
46
|
+
elements,
|
|
47
|
+
}: {
|
|
48
|
+
type: any;
|
|
49
|
+
style: any;
|
|
50
|
+
additionalProps?: Record<string, any>;
|
|
51
|
+
data?: Array<{ propName: string; func: Function; args: any[] }>;
|
|
52
|
+
elements?: any[];
|
|
53
|
+
}
|
|
42
54
|
) {
|
|
43
|
-
const props =
|
|
44
|
-
(acc, curr) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
const props = data.reduce(
|
|
56
|
+
(acc, curr) => {
|
|
57
|
+
acc[curr.propName] = retrieveData(entry, curr.func, curr.args);
|
|
58
|
+
|
|
59
|
+
return acc;
|
|
60
|
+
},
|
|
61
|
+
{ ...additionalProps }
|
|
48
62
|
);
|
|
49
63
|
|
|
50
64
|
let adjustedElements;
|
|
@@ -52,6 +52,8 @@ export function masterCellBuilder({
|
|
|
52
52
|
const { View } = componentsMap;
|
|
53
53
|
const elementsBuilder = dataAdapter(cellConfiguration);
|
|
54
54
|
|
|
55
|
+
// This is very expensive operation
|
|
56
|
+
// Keep calls to minimum
|
|
55
57
|
const hasFocusableInside = hasFocusableInsideBuilder(elementsBuilder);
|
|
56
58
|
|
|
57
59
|
/**
|
|
@@ -84,7 +86,7 @@ export function masterCellBuilder({
|
|
|
84
86
|
entry: item,
|
|
85
87
|
state: getEntryState(state, entryIsSelected),
|
|
86
88
|
}),
|
|
87
|
-
[state, item, entryIsSelected]
|
|
89
|
+
[state, item?.id, entryIsSelected] // Assuming that item won't mutate
|
|
88
90
|
);
|
|
89
91
|
|
|
90
92
|
const wrapperRef = React.useRef(null);
|
|
@@ -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
|
|
|
@@ -52,7 +53,10 @@ function resolveColorForProp(entry, style, colorProp) {
|
|
|
52
53
|
const colorFromProp = style[colorProp];
|
|
53
54
|
const nestedEntryValue = R.path(colorFromProp.split("."), entry);
|
|
54
55
|
|
|
55
|
-
if (
|
|
56
|
+
if (
|
|
57
|
+
nestedEntryValue === undefined &&
|
|
58
|
+
!validateColor(colorFromProp.replace(".00", "")) // https://github.com/dreamyguy/validate-color/issues/44
|
|
59
|
+
) {
|
|
56
60
|
logWarning(colorFromProp, style, entry, nestedEntryValue);
|
|
57
61
|
}
|
|
58
62
|
|
|
@@ -8,6 +8,8 @@ import { ScreenDataContext } from "@applicaster/zapp-react-native-ui-components/
|
|
|
8
8
|
import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
|
|
9
9
|
|
|
10
10
|
import { Screen } from "../Screen/TV/index.web";
|
|
11
|
+
import { isLast } from "@applicaster/zapp-react-native-utils/arrayUtils";
|
|
12
|
+
import { ScreenContextProvider } from "../../Contexts/ScreenContext";
|
|
11
13
|
|
|
12
14
|
type Components = {
|
|
13
15
|
NavBar: React.ComponentType<any>;
|
|
@@ -19,25 +21,32 @@ type Props = {
|
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
export const StackNavigator = ({ Components }: Props) => {
|
|
22
|
-
const { mainStack
|
|
24
|
+
const { mainStack } = useNavigation();
|
|
23
25
|
|
|
24
|
-
const renderScreen = ({ route, state }) => {
|
|
26
|
+
const renderScreen = ({ route, state }, index) => {
|
|
25
27
|
const pathAttributes = getPathAttributes({ pathname: route });
|
|
26
28
|
|
|
27
29
|
if (isEmpty(pathAttributes)) {
|
|
28
30
|
return null;
|
|
29
31
|
}
|
|
30
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
|
+
|
|
31
38
|
return (
|
|
32
39
|
<FocusableGroup
|
|
33
40
|
id={route}
|
|
34
|
-
preferredFocus={
|
|
41
|
+
preferredFocus={isLast(index, mainStack.length)}
|
|
35
42
|
excludeFromFocusSearching
|
|
36
|
-
key={
|
|
43
|
+
key={routeId}
|
|
37
44
|
>
|
|
38
45
|
<ScreenDataContext.Provider value={state}>
|
|
39
46
|
<PathnameContext.Provider value={route}>
|
|
40
|
-
<
|
|
47
|
+
<ScreenContextProvider pathname={route}>
|
|
48
|
+
<Screen route={route} Components={Components} />
|
|
49
|
+
</ScreenContextProvider>
|
|
41
50
|
</PathnameContext.Provider>
|
|
42
51
|
</ScreenDataContext.Provider>
|
|
43
52
|
</FocusableGroup>
|
|
@@ -124,6 +124,7 @@ const nativeStyles = {
|
|
|
124
124
|
playerScreen: {
|
|
125
125
|
flex: 1,
|
|
126
126
|
backgroundColor: "black",
|
|
127
|
+
overflow: "hidden",
|
|
127
128
|
},
|
|
128
129
|
playerWrapper: {
|
|
129
130
|
flex: 1,
|
|
@@ -252,11 +253,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
252
253
|
}
|
|
253
254
|
|
|
254
255
|
showNavBar(true);
|
|
255
|
-
|
|
256
|
-
// Hack to close player screen on mobile only (tv handles that in InteractionManager)
|
|
257
|
-
if (!isTV()) {
|
|
258
|
-
navigator.goBack();
|
|
259
|
-
}
|
|
256
|
+
navigator.goBack();
|
|
260
257
|
}, [isModal, navigator.goBack, showNavBar]);
|
|
261
258
|
|
|
262
259
|
const playEntry = (entry) => navigator.replaceTop(entry, { mode });
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { last
|
|
2
|
+
import { last } from "ramda";
|
|
3
3
|
import { View, StyleSheet } from "react-native";
|
|
4
4
|
import {
|
|
5
|
+
findMenuPlugin,
|
|
5
6
|
getNavigationProps,
|
|
6
7
|
getPathAttributes,
|
|
7
|
-
resolveNavigationPlugin,
|
|
8
8
|
} from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
9
9
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
10
10
|
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
11
11
|
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
useNavbarState,
|
|
14
|
+
useScreenBackgroundColor,
|
|
15
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
13
16
|
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
14
17
|
|
|
15
18
|
import { NavBarContainer } from "../../Layout/TV/NavBarContainer";
|
|
16
19
|
import { ScreenResolver } from "../../ScreenResolver";
|
|
17
20
|
import { useInitialFocus, useRestoreFocus } from "./hooks";
|
|
21
|
+
import { isPlayerPlugin } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
18
22
|
|
|
19
23
|
const styles = StyleSheet.create({
|
|
20
24
|
container: {
|
|
@@ -36,18 +40,6 @@ type Props = {
|
|
|
36
40
|
Components: Components;
|
|
37
41
|
};
|
|
38
42
|
|
|
39
|
-
const getPluginType = pathOr("unknown_plugin_type", [
|
|
40
|
-
"state",
|
|
41
|
-
"screen",
|
|
42
|
-
"plugin_type",
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
const isPlayerPlugin = (routeState): boolean =>
|
|
46
|
-
getPluginType(routeState) === "player";
|
|
47
|
-
|
|
48
|
-
const isLoginPlugin = (routeState): boolean =>
|
|
49
|
-
getPluginType(routeState) === "login";
|
|
50
|
-
|
|
51
43
|
const getActiveRiver = ({
|
|
52
44
|
screenId,
|
|
53
45
|
screenType,
|
|
@@ -139,19 +131,14 @@ export const Screen = ({ route, Components }: Props) => {
|
|
|
139
131
|
fallbackNavigations: navigator.activeRiver.navigations,
|
|
140
132
|
});
|
|
141
133
|
|
|
142
|
-
const menuPlugin =
|
|
143
|
-
category: "menu",
|
|
144
|
-
navigations,
|
|
145
|
-
plugins,
|
|
146
|
-
}) as QuickBrickPlugin;
|
|
134
|
+
const menuPlugin = findMenuPlugin(navigations, plugins);
|
|
147
135
|
|
|
148
136
|
const NavBar = (Components.NavBar ||
|
|
149
137
|
menuPlugin.module) as React.ComponentType<any>;
|
|
150
138
|
|
|
151
139
|
const isPlayer = isPlayerPlugin(routeState);
|
|
152
|
-
const isLogin = isLoginPlugin(routeState);
|
|
153
140
|
|
|
154
|
-
const isNavBarVisible =
|
|
141
|
+
const { visible: isNavBarVisible } = useNavbarState();
|
|
155
142
|
|
|
156
143
|
const screenData =
|
|
157
144
|
screenType === "hooks" || isPlayer
|
|
@@ -162,7 +149,6 @@ export const Screen = ({ route, Components }: Props) => {
|
|
|
162
149
|
<View style={[styles.container, { backgroundColor }]}>
|
|
163
150
|
<NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
|
|
164
151
|
<NavBar
|
|
165
|
-
groupId={route}
|
|
166
152
|
isVisible={isNavBarVisible}
|
|
167
153
|
menuPlugin={menuPlugin}
|
|
168
154
|
navigationProps={navigationProps}
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
12
12
|
import { getComponentModule } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
shouldNavBarDisplayMenu,
|
|
15
|
+
getNavBarProps,
|
|
16
|
+
getScreenId,
|
|
16
17
|
} from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
17
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
18
18
|
import {
|
|
19
19
|
useDimensions,
|
|
20
20
|
useRoute,
|
|
@@ -25,20 +25,16 @@ import {
|
|
|
25
25
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
26
26
|
import { getNavigationPluginModule } from "@applicaster/zapp-react-native-app/App/Layout/layoutHelpers";
|
|
27
27
|
|
|
28
|
-
import { ScreenContextProvider } from "../../Contexts/ScreenContext";
|
|
29
28
|
import { RouteManager } from "../RouteManager";
|
|
30
29
|
import { useScreenConfiguration } from "../River/useScreenConfiguration";
|
|
31
30
|
import { useScreenOrientationHandler } from "./orientationHandler";
|
|
32
31
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
: "screen_id" in screenData
|
|
40
|
-
? screenData.screen_id
|
|
41
|
-
: fallbackRiver.id;
|
|
32
|
+
const screenStyles = {
|
|
33
|
+
flex: 1,
|
|
34
|
+
paddingTop: 0,
|
|
35
|
+
// paddingBottom: insets.bottom, // Should be applied directly by UI plugin
|
|
36
|
+
// paddingLeft: insets.left, // Should be applied directly by UI plugin
|
|
37
|
+
// paddingRight: insets.right, // Should be applied directly by UI plugin
|
|
42
38
|
};
|
|
43
39
|
|
|
44
40
|
function colorIsNotTransparent(color) {
|
|
@@ -49,9 +45,7 @@ function colorIsNotTransparent(color) {
|
|
|
49
45
|
R.replace("rgba(", "")
|
|
50
46
|
)(color);
|
|
51
47
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return alpha > 0;
|
|
48
|
+
return Number(layers?.[3]) > 0;
|
|
55
49
|
}
|
|
56
50
|
|
|
57
51
|
function isValidColor(string) {
|
|
@@ -71,7 +65,6 @@ export function Screen(_props: Props) {
|
|
|
71
65
|
// Gets the data for the current screen configuration
|
|
72
66
|
const currentScreenData = useCurrentScreenData();
|
|
73
67
|
const { backgroundColor } = useScreenConfiguration(currentScreenData.id);
|
|
74
|
-
const insets = useSafeAreaInsets();
|
|
75
68
|
|
|
76
69
|
const {
|
|
77
70
|
width: screenWidth,
|
|
@@ -96,30 +89,12 @@ export function Screen(_props: Props) {
|
|
|
96
89
|
|
|
97
90
|
const layoutData = { isTablet, isTabletPortrait, width: screenWidth, height };
|
|
98
91
|
|
|
99
|
-
const hasMenu =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(() => {
|
|
107
|
-
const props = getNavigationPropsV2({
|
|
108
|
-
currentRiver,
|
|
109
|
-
title,
|
|
110
|
-
category: "nav_bar",
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
if (props) {
|
|
114
|
-
return {
|
|
115
|
-
...props,
|
|
116
|
-
id: pathname,
|
|
117
|
-
pathname: pathname,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return null;
|
|
122
|
-
}, [currentRiver, pathname]);
|
|
92
|
+
const hasMenu = shouldNavBarDisplayMenu(currentRiver, plugins);
|
|
93
|
+
|
|
94
|
+
const navBarProps = React.useMemo<MobileNavBarPluginProps | null>(
|
|
95
|
+
getNavBarProps(currentRiver, pathname, title),
|
|
96
|
+
[currentRiver, pathname]
|
|
97
|
+
);
|
|
123
98
|
|
|
124
99
|
const NavBar = React.useMemo(
|
|
125
100
|
() =>
|
|
@@ -141,16 +116,12 @@ export function Screen(_props: Props) {
|
|
|
141
116
|
|
|
142
117
|
const style = React.useMemo(
|
|
143
118
|
() => ({
|
|
144
|
-
|
|
119
|
+
...screenStyles,
|
|
145
120
|
backgroundColor: isValidColor(backgroundColor)
|
|
146
121
|
? backgroundColor
|
|
147
122
|
: theme.app_background_color,
|
|
148
|
-
paddingTop: 0,
|
|
149
|
-
// paddingBottom: insets.bottom, // Should be applied directly by UI plugin
|
|
150
|
-
// paddingLeft: insets.left, // Should be applied directly by UI plugin
|
|
151
|
-
// paddingRight: insets.right, // Should be applied directly by UI plugin
|
|
152
123
|
}),
|
|
153
|
-
[
|
|
124
|
+
[theme.app_background_color, backgroundColor]
|
|
154
125
|
);
|
|
155
126
|
|
|
156
127
|
const targetScreenData =
|
|
@@ -168,19 +139,17 @@ export function Screen(_props: Props) {
|
|
|
168
139
|
layoutData,
|
|
169
140
|
})
|
|
170
141
|
) {
|
|
171
|
-
return
|
|
142
|
+
return <View style={style} />;
|
|
172
143
|
}
|
|
173
144
|
|
|
174
145
|
return (
|
|
175
|
-
<
|
|
176
|
-
<
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
</View>
|
|
184
|
-
</ScreenContextProvider>
|
|
146
|
+
<View style={style}>
|
|
147
|
+
{navBarProps && <NavBar {...navBarProps} hasMenu={hasMenu} />}
|
|
148
|
+
|
|
149
|
+
<OfflineFallbackScreen screenData={targetScreenData}>
|
|
150
|
+
{/* @TODO RouteManager doesn't use props, can they be removed ? */}
|
|
151
|
+
<RouteManager pathname={pathname} screenData={screenData} />
|
|
152
|
+
</OfflineFallbackScreen>
|
|
153
|
+
</View>
|
|
185
154
|
);
|
|
186
155
|
}
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* is similar to the navbar, except that it covers scenarios where only the navbar (and not)
|
|
6
6
|
* the menu, will be hidden
|
|
7
7
|
*
|
|
8
|
+
* - playable screens
|
|
9
|
+
* - qb_search_screen
|
|
10
|
+
* - screen hooks that specify showNavBar or presentFullScreen
|
|
11
|
+
* - screens or nested screens that have allow_screen_plugin_presentation set to true
|
|
12
|
+
*
|
|
8
13
|
* @param {String} route current route of the screen
|
|
9
14
|
* @param {Object} screenData payload associated with the currently presented screen
|
|
10
15
|
* @returns {Boolean}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
3
|
-
import { useDispatch } from "react-redux";
|
|
2
|
+
import { path, prop } from "ramda";
|
|
4
3
|
import {
|
|
5
4
|
findPluginByType,
|
|
6
5
|
findPluginByIdentifier,
|
|
@@ -15,7 +14,6 @@ import { componentsLogger } from "../../Helpers/logger";
|
|
|
15
14
|
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
16
15
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
17
16
|
import { useScreenAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks";
|
|
18
|
-
import { setAppStateHasFinishedTransitioning } from "@applicaster/zapp-react-native-redux/appState";
|
|
19
17
|
|
|
20
18
|
const logger = componentsLogger.addSubsystem("ScreenResolver");
|
|
21
19
|
|
|
@@ -54,11 +52,8 @@ export function ScreenResolverComponent(props: Props) {
|
|
|
54
52
|
|
|
55
53
|
const [, setScreenContext] = ZappPipesScreenContext.useZappPipesContext();
|
|
56
54
|
|
|
57
|
-
const dispatch = useDispatch();
|
|
58
|
-
|
|
59
55
|
React.useEffect(() => {
|
|
60
56
|
setScreenContext(rivers[screenId]);
|
|
61
|
-
dispatch(setAppStateHasFinishedTransitioning());
|
|
62
57
|
}, [screenId]);
|
|
63
58
|
|
|
64
59
|
const ScreenPlugin =
|
|
@@ -100,14 +95,14 @@ export function ScreenResolverComponent(props: Props) {
|
|
|
100
95
|
}
|
|
101
96
|
|
|
102
97
|
const ScreenComponent =
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
path(["module", "Component"], ScreenPlugin) ||
|
|
99
|
+
prop("module", ScreenPlugin) ||
|
|
100
|
+
prop("Component", ScreenPlugin) ||
|
|
106
101
|
ScreenPlugin;
|
|
107
102
|
|
|
108
103
|
const configuration =
|
|
109
|
-
|
|
110
|
-
|
|
104
|
+
prop("configuration", ScreenPlugin) ||
|
|
105
|
+
prop("__plugin_configuration", ScreenComponent);
|
|
111
106
|
|
|
112
107
|
if (ScreenComponent) {
|
|
113
108
|
return (
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
Animated,
|
|
4
|
-
useWindowDimensions,
|
|
5
|
-
ViewProps,
|
|
6
|
-
ViewStyle,
|
|
7
|
-
} from "react-native";
|
|
2
|
+
import { Animated, ViewProps, ViewStyle } from "react-native";
|
|
8
3
|
import { useSafeAreaFrame } from "react-native-safe-area-context";
|
|
9
4
|
import { PathnameContext } from "../../Contexts/PathnameContext";
|
|
10
5
|
import { ScreenDataContext } from "../../Contexts/ScreenDataContext";
|
|
6
|
+
import { ScreenContextProvider } from "../../Contexts/ScreenContext";
|
|
11
7
|
|
|
12
8
|
type Props = {
|
|
13
9
|
children: any;
|
|
@@ -53,12 +49,15 @@ export function Scene({
|
|
|
53
49
|
}, [screenData, isActive]);
|
|
54
50
|
|
|
55
51
|
const frame = useSafeAreaFrame();
|
|
56
|
-
const { height, width } = useWindowDimensions();
|
|
57
52
|
|
|
58
|
-
// Safe frame takes too long to update,
|
|
59
|
-
// so we can not rely on it there, and use height and width instead.
|
|
60
53
|
const isAnimating = animating && overlayStyle;
|
|
61
|
-
|
|
54
|
+
|
|
55
|
+
const dimensions = isAnimating
|
|
56
|
+
? {
|
|
57
|
+
height: "100%",
|
|
58
|
+
width: "100%",
|
|
59
|
+
}
|
|
60
|
+
: frame;
|
|
62
61
|
|
|
63
62
|
return (
|
|
64
63
|
<Animated.View
|
|
@@ -67,7 +66,9 @@ export function Scene({
|
|
|
67
66
|
>
|
|
68
67
|
<ScreenDataContext.Provider value={initialScreenData}>
|
|
69
68
|
<PathnameContext.Provider value={pathname}>
|
|
70
|
-
{
|
|
69
|
+
<ScreenContextProvider pathname={pathname}>
|
|
70
|
+
{children}
|
|
71
|
+
</ScreenContextProvider>
|
|
71
72
|
</PathnameContext.Provider>
|
|
72
73
|
</ScreenDataContext.Provider>
|
|
73
74
|
{isAnimating && <Animated.View style={overlayStyle} />}
|
|
@@ -166,21 +166,23 @@ export class TransitionerComponent extends React.PureComponent<Props, State> {
|
|
|
166
166
|
|
|
167
167
|
transitionWithoutAnimation(resetState) {
|
|
168
168
|
const { scenes } = this.state;
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
169
|
+
const newScene = prepareScene(this.props.children);
|
|
170
|
+
const isLastSceneHook = scenes[scenes.length - 1].key.startsWith("/hook");
|
|
171
|
+
|
|
172
|
+
let newState;
|
|
173
|
+
|
|
174
|
+
if (resetState) {
|
|
175
|
+
newState = {
|
|
176
|
+
scenes: [newScene],
|
|
177
|
+
to: { index: 0 },
|
|
178
|
+
};
|
|
179
|
+
} else if (isLastSceneHook || scenes.length === 1) {
|
|
180
|
+
newState = { scenes: R.update(scenes.length - 1, newScene, scenes) };
|
|
181
|
+
} else {
|
|
182
|
+
newState = {
|
|
183
|
+
scenes: R.compose(R.tail, R.append(newScene))(scenes),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
184
186
|
|
|
185
187
|
this.setState(newState);
|
|
186
188
|
}
|
|
@@ -22,7 +22,9 @@ exports[`<Scene /> renders correctly 1`] = `
|
|
|
22
22
|
}
|
|
23
23
|
>
|
|
24
24
|
<ContextProvider>
|
|
25
|
-
<ContextProvider
|
|
25
|
+
<ContextProvider>
|
|
26
|
+
<ScreenContextProvider />
|
|
27
|
+
</ContextProvider>
|
|
26
28
|
</ContextProvider>
|
|
27
29
|
</ForwardRef(AnimatedComponentWrapper)>
|
|
28
30
|
`;
|
|
@@ -39,14 +41,16 @@ exports[`<Scene /> renders the overlay while animating 1`] = `
|
|
|
39
41
|
"paddingBottom": 49,
|
|
40
42
|
},
|
|
41
43
|
Object {
|
|
42
|
-
"height":
|
|
43
|
-
"width":
|
|
44
|
+
"height": "100%",
|
|
45
|
+
"width": "100%",
|
|
44
46
|
},
|
|
45
47
|
]
|
|
46
48
|
}
|
|
47
49
|
>
|
|
48
50
|
<ContextProvider>
|
|
49
|
-
<ContextProvider
|
|
51
|
+
<ContextProvider>
|
|
52
|
+
<ScreenContextProvider />
|
|
53
|
+
</ContextProvider>
|
|
50
54
|
</ContextProvider>
|
|
51
55
|
<ForwardRef(AnimatedComponentWrapper)
|
|
52
56
|
style={
|
|
@@ -525,6 +525,16 @@ export class LiveImage implements QuickBrickPlayer.SharedPlayerCallBacks {
|
|
|
525
525
|
LiveImageManager.instance.onLiveImageError(this, error);
|
|
526
526
|
};
|
|
527
527
|
|
|
528
|
+
onVideoError = (error: Error) => {
|
|
529
|
+
log_error(
|
|
530
|
+
`onVideoError: live-image, fail to play, switching back to image. error: ${
|
|
531
|
+
error?.message
|
|
532
|
+
}, ${playerInfo(this.player)}`
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
LiveImageManager.instance.onLiveImageError(this, error);
|
|
536
|
+
};
|
|
537
|
+
|
|
528
538
|
onVideoEnd = (_event) => {
|
|
529
539
|
log_info(
|
|
530
540
|
`onVideoEnd: live-image, video completed, switching back to image, ${playerInfo(
|