@applicaster/zapp-react-native-ui-components 13.0.5-rc.0 → 14.0.0-alpha.1101330035
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/tv/Layout.tsx +4 -2
- package/Components/Cell/index.js +6 -2
- package/Components/Focusable/Focusable.tsx +5 -3
- package/Components/Focusable/__tests__/index.android.test.tsx +3 -0
- package/Components/Focusable/index.android.tsx +12 -8
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +0 -2
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -27
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +1 -0
- package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
- package/Components/MasterCell/utils/index.ts +23 -3
- package/Components/PlayerContainer/PlayerContainer.tsx +17 -15
- package/Components/River/TV/River.tsx +0 -3
- package/Components/TopMarginApplicator/TopMarginApplicator.tsx +16 -15
- package/Components/Transitioner/Scene.tsx +1 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +9 -1
- package/Components/VideoModal/PlayerDetails.tsx +24 -2
- package/Components/VideoModal/PlayerWrapper.tsx +26 -142
- package/Components/VideoModal/VideoModal.tsx +3 -17
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -7
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +44 -240
- package/Components/VideoModal/hooks/index.ts +0 -2
- package/Components/VideoModal/hooks/useModalSize.ts +18 -2
- package/Components/VideoModal/utils.ts +6 -0
- package/Components/Viewport/VisibilitySensor/VisibilitySensor.tsx +3 -3
- package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/index.ts +3 -3
- package/Decorators/RiverFeedLoader/index.tsx +8 -2
- package/Decorators/RiverFeedLoader/utils/index.ts +7 -2
- package/Decorators/ZappPipesDataConnector/index.tsx +20 -2
- package/index.d.ts +0 -1
- package/package.json +5 -9
- package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
- package/Decorators/Navigator/__tests__/react-router-native-mock.js +0 -11
|
@@ -110,22 +110,24 @@ export function AudioPlayerTVLayout({
|
|
|
110
110
|
...style,
|
|
111
111
|
},
|
|
112
112
|
samsung_tv: {
|
|
113
|
+
position: "absolute",
|
|
113
114
|
margin: "auto",
|
|
114
115
|
display: "flex",
|
|
115
116
|
flexWrap: "wrap",
|
|
117
|
+
height: "100vh",
|
|
116
118
|
width: "100vw",
|
|
117
|
-
flex: 1,
|
|
118
119
|
alignItems: "center",
|
|
119
120
|
justifyContent: "center",
|
|
120
121
|
flexDirection: directionStyles(isRTL).flexDirection,
|
|
121
122
|
backgroundColor: backgroundColorStyle,
|
|
122
123
|
},
|
|
123
124
|
lg_tv: {
|
|
125
|
+
position: "absolute",
|
|
124
126
|
margin: "auto",
|
|
125
127
|
display: "flex",
|
|
126
128
|
flexWrap: "wrap",
|
|
129
|
+
height: "100vh",
|
|
127
130
|
width: "100vw",
|
|
128
|
-
flex: 1,
|
|
129
131
|
alignItems: "center",
|
|
130
132
|
justifyContent: "center",
|
|
131
133
|
flexDirection: directionStyles(isRTL).flexDirection,
|
package/Components/Cell/index.js
CHANGED
|
@@ -3,11 +3,15 @@ import * as R from "ramda";
|
|
|
3
3
|
import { connectToStore } from "@applicaster/zapp-react-native-redux";
|
|
4
4
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
HorizontalScrollContext,
|
|
8
|
+
RiverOffsetContext,
|
|
9
|
+
ScreenScrollingContext,
|
|
10
|
+
} from "../../Contexts";
|
|
11
|
+
|
|
7
12
|
import { CellComponent } from "./Cell";
|
|
8
13
|
import { TvOSCellComponent } from "./TvOSCellComponent";
|
|
9
14
|
import { withConsumer } from "../../Contexts/HeaderOffsetContext";
|
|
10
|
-
import { ScreenScrollingContext } from "../../Contexts/ScreenScrollingContext";
|
|
11
15
|
|
|
12
16
|
import { ScreenLayoutContextConsumer } from "../../Contexts/ScreenLayoutContext";
|
|
13
17
|
import { createContext } from "@applicaster/zapp-react-native-utils/reactUtils/createContext";
|
|
@@ -5,6 +5,7 @@ import { BaseFocusable } from "../BaseFocusable";
|
|
|
5
5
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
6
6
|
import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
|
|
7
7
|
import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
|
|
8
|
+
import { StyleSheet, ViewStyle } from "react-native";
|
|
8
9
|
|
|
9
10
|
type Props = {
|
|
10
11
|
initialFocus?: boolean;
|
|
@@ -21,7 +22,7 @@ type Props = {
|
|
|
21
22
|
handleFocus?: ({ mouse }: { mouse: boolean }) => void;
|
|
22
23
|
children: (boolean, string) => React.ComponentType<any>;
|
|
23
24
|
selected?: boolean;
|
|
24
|
-
style?:
|
|
25
|
+
style?: ViewStyle[] | ViewStyle;
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
class Focusable extends BaseFocusable<Props> {
|
|
@@ -122,7 +123,7 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
render() {
|
|
125
|
-
const { children, style } = this.props;
|
|
126
|
+
const { children, style, ...otherProps } = this.props;
|
|
126
127
|
const { focused } = this.state;
|
|
127
128
|
|
|
128
129
|
const id = this.getId();
|
|
@@ -139,7 +140,8 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
139
140
|
onMouseUp={this.pressOut}
|
|
140
141
|
data-testid={focusableId}
|
|
141
142
|
focused-teststate={focused ? "focused" : "default"}
|
|
142
|
-
style={style}
|
|
143
|
+
style={StyleSheet.flatten(style) as any as React.CSSProperties}
|
|
144
|
+
{...otherProps}
|
|
143
145
|
>
|
|
144
146
|
{children(focused, { mouse: this.mouse })}
|
|
145
147
|
</div>
|
|
@@ -38,6 +38,9 @@ describe("Focusable", () => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
it("updates disableFocus state when disableFocus prop changes", () => {
|
|
41
|
+
const unregister = jest.fn();
|
|
42
|
+
mockFocusManager.registerFocusable.mockReturnValue(unregister);
|
|
43
|
+
|
|
41
44
|
const { rerender } = render(
|
|
42
45
|
<Focusable id="test-id" disableFocus={false}>
|
|
43
46
|
<Touchable testID="touchable" />
|
|
@@ -43,11 +43,13 @@ export const FocusableContext = React.createContext<
|
|
|
43
43
|
// eslint-disable-next-line
|
|
44
44
|
setIsFocusable: (enableFocus: boolean) => void;
|
|
45
45
|
ref: FocusManager.FocusableRef;
|
|
46
|
+
parentFocusableId: Option<string>;
|
|
46
47
|
} & ParentFocus
|
|
47
48
|
>({
|
|
48
49
|
focused: false,
|
|
49
50
|
setIsFocusable: () => {},
|
|
50
51
|
ref: { current: null },
|
|
52
|
+
parentFocusableId: undefined,
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
export const useFocusable = () => React.useContext(FocusableContext);
|
|
@@ -74,7 +76,7 @@ function FocusableComponent(props: Props, forwardedRef) {
|
|
|
74
76
|
|
|
75
77
|
const isRTL = useIsRTL();
|
|
76
78
|
const focusManager = useFocusManager();
|
|
77
|
-
const { ref:
|
|
79
|
+
const { ref: parentFocusableRef, parentFocusableId } = useFocusable();
|
|
78
80
|
const touchableRef = React.useRef(null);
|
|
79
81
|
|
|
80
82
|
const [focused, setFocused] = React.useState(() =>
|
|
@@ -98,13 +100,14 @@ function FocusableComponent(props: Props, forwardedRef) {
|
|
|
98
100
|
}
|
|
99
101
|
}, [disableFocus]);
|
|
100
102
|
|
|
101
|
-
React.
|
|
103
|
+
React.useLayoutEffect(() => {
|
|
102
104
|
if (id) {
|
|
103
|
-
const unregister = focusManager.registerFocusable(
|
|
105
|
+
const unregister = focusManager.registerFocusable({
|
|
104
106
|
touchableRef,
|
|
105
|
-
|
|
106
|
-
isFocusableCell
|
|
107
|
-
|
|
107
|
+
parentFocusableRef,
|
|
108
|
+
isFocusableCell,
|
|
109
|
+
parentFocusableId,
|
|
110
|
+
});
|
|
108
111
|
|
|
109
112
|
onRegister();
|
|
110
113
|
|
|
@@ -112,7 +115,7 @@ function FocusableComponent(props: Props, forwardedRef) {
|
|
|
112
115
|
unregister();
|
|
113
116
|
};
|
|
114
117
|
}
|
|
115
|
-
}, [id, onRegister, isFocusableCell]);
|
|
118
|
+
}, [id, onRegister, isFocusableCell, parentFocusableId]);
|
|
116
119
|
|
|
117
120
|
if (R.isNil(id)) {
|
|
118
121
|
// eslint-disable-next-line no-console
|
|
@@ -164,8 +167,9 @@ function FocusableComponent(props: Props, forwardedRef) {
|
|
|
164
167
|
...parentFocus,
|
|
165
168
|
ref: touchableRef,
|
|
166
169
|
setIsFocusable,
|
|
170
|
+
parentFocusableId: id,
|
|
167
171
|
};
|
|
168
|
-
}, [parentFocus, focused]);
|
|
172
|
+
}, [parentFocus, focused, id]);
|
|
169
173
|
|
|
170
174
|
return (
|
|
171
175
|
<Touchable
|
|
@@ -30,7 +30,6 @@ export const GeneralContentScreen = ({
|
|
|
30
30
|
isScreenWrappedInContainer,
|
|
31
31
|
componentsMapExtraProps = {},
|
|
32
32
|
focused,
|
|
33
|
-
extraOffset,
|
|
34
33
|
parentFocus,
|
|
35
34
|
containerHeight,
|
|
36
35
|
preferredFocus = false,
|
|
@@ -122,7 +121,6 @@ export const GeneralContentScreen = ({
|
|
|
122
121
|
isScreenWrappedInContainer={isScreenWrappedInContainer}
|
|
123
122
|
parentFocus={parentFocus}
|
|
124
123
|
focused={focused}
|
|
125
|
-
extraOffset={extraOffset}
|
|
126
124
|
containerHeight={containerHeight}
|
|
127
125
|
preferredFocus={preferredFocus}
|
|
128
126
|
{...componentsMapExtraProps}
|
|
@@ -2,7 +2,6 @@ import React, { useMemo } from "react";
|
|
|
2
2
|
import { ImageStyle } from "react-native";
|
|
3
3
|
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
|
|
4
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
|
-
import * as R from "ramda";
|
|
6
5
|
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
7
6
|
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
8
7
|
import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
|
|
@@ -67,32 +66,10 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
67
66
|
const handleFocus = (focusable) => {
|
|
68
67
|
const focusedButtonId = getFocusedButtonId(focusable);
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const right = left + width;
|
|
75
|
-
|
|
76
|
-
const boundingRect = {
|
|
77
|
-
x,
|
|
78
|
-
y,
|
|
79
|
-
pageX,
|
|
80
|
-
pageY,
|
|
81
|
-
width,
|
|
82
|
-
height,
|
|
83
|
-
top,
|
|
84
|
-
bottom,
|
|
85
|
-
left,
|
|
86
|
-
right,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
otherProps?.onToggleFocus?.({
|
|
90
|
-
focusable: {
|
|
91
|
-
getRect: R.always(boundingRect),
|
|
92
|
-
},
|
|
93
|
-
focusedButtonId,
|
|
94
|
-
mouse: focusable.mouse,
|
|
95
|
-
});
|
|
69
|
+
otherProps?.onToggleFocus?.({
|
|
70
|
+
focusable: wrapperRef.current,
|
|
71
|
+
focusedButtonId,
|
|
72
|
+
mouse: focusable.mouse,
|
|
96
73
|
});
|
|
97
74
|
|
|
98
75
|
if (ttsLabel) {
|
|
@@ -1,28 +1,58 @@
|
|
|
1
1
|
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
|
|
2
|
-
import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-
|
|
2
|
+
import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/StorageSingleSelectProvider";
|
|
3
3
|
import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
|
|
4
|
-
import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-
|
|
4
|
+
import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/StorageMultiSelectProvider";
|
|
5
5
|
import React, { useEffect } from "react";
|
|
6
6
|
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
|
|
7
7
|
import { BehaviorSubject } from "rxjs";
|
|
8
8
|
import { masterCellLogger } from "../logger";
|
|
9
9
|
import get from "lodash/get";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
import { ScreenMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenStateMultiSelectProvider";
|
|
11
|
+
import { ScreenSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenSingleValueProvider";
|
|
12
|
+
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
13
|
+
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
|
|
14
|
+
|
|
15
|
+
const parseContextKey = (
|
|
16
|
+
key: string,
|
|
17
|
+
context: string = "ctx"
|
|
18
|
+
): string | null => {
|
|
19
|
+
if (!key?.startsWith(`@{${context}/`)) return null;
|
|
20
|
+
|
|
21
|
+
return key.substring(`@{${context}/`.length, key.length - 1);
|
|
15
22
|
};
|
|
16
23
|
|
|
17
24
|
const getDataSourceProvider = (
|
|
18
|
-
behavior: Behavior
|
|
25
|
+
behavior: Behavior,
|
|
26
|
+
screenRoute: string,
|
|
27
|
+
screenStateStore: ScreenStateStore
|
|
19
28
|
): BehaviorSubject<string[] | string> | null => {
|
|
20
29
|
if (!behavior) return null;
|
|
21
30
|
|
|
22
31
|
const selection = String(behavior.current_selection);
|
|
32
|
+
const screenKey = parseContextKey(selection, "screen");
|
|
33
|
+
|
|
34
|
+
if (screenKey) {
|
|
35
|
+
if (behavior.select_mode === "multi") {
|
|
36
|
+
return ScreenMultiSelectProvider.getProvider(
|
|
37
|
+
screenKey,
|
|
38
|
+
screenRoute,
|
|
39
|
+
screenStateStore
|
|
40
|
+
).getObservable();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (behavior.select_mode === "single") {
|
|
44
|
+
return ScreenSingleValueProvider.getProvider(
|
|
45
|
+
screenKey,
|
|
46
|
+
screenRoute,
|
|
47
|
+
screenStateStore
|
|
48
|
+
).getObservable();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
23
52
|
const contextKey = parseContextKey(selection);
|
|
24
53
|
|
|
25
54
|
if (contextKey) {
|
|
55
|
+
// TODO: Add storage scope to behavior
|
|
26
56
|
if (behavior.select_mode === "multi") {
|
|
27
57
|
return StorageMultiSelectProvider.getProvider(contextKey).getObservable();
|
|
28
58
|
}
|
|
@@ -41,6 +71,8 @@ const getDataSourceProvider = (
|
|
|
41
71
|
|
|
42
72
|
export const useBehaviorUpdate = (behavior: Behavior) => {
|
|
43
73
|
const [lastUpdate, setLastUpdate] = React.useState<number | null>(null);
|
|
74
|
+
const screenRoute = useRoute()?.pathname || "";
|
|
75
|
+
const screenStateStore = useScreenStateStore();
|
|
44
76
|
const player = usePlayer();
|
|
45
77
|
|
|
46
78
|
const triggerUpdate = () => setLastUpdate(Date.now());
|
|
@@ -48,7 +80,11 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
|
|
|
48
80
|
useEffect(() => {
|
|
49
81
|
if (!behavior) return;
|
|
50
82
|
|
|
51
|
-
const dataSource = getDataSourceProvider(
|
|
83
|
+
const dataSource = getDataSourceProvider(
|
|
84
|
+
behavior,
|
|
85
|
+
screenRoute,
|
|
86
|
+
screenStateStore
|
|
87
|
+
);
|
|
52
88
|
|
|
53
89
|
if (dataSource) {
|
|
54
90
|
const subscription = dataSource.subscribe(triggerUpdate);
|
|
@@ -72,10 +108,17 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
|
|
|
72
108
|
|
|
73
109
|
// We cant use async in this function (its inside render),
|
|
74
110
|
// so we rely on useBehaviorUpdate to update current value and trigger re-render
|
|
75
|
-
export const isCellSelected = (
|
|
76
|
-
item
|
|
77
|
-
|
|
78
|
-
|
|
111
|
+
export const isCellSelected = ({
|
|
112
|
+
item,
|
|
113
|
+
screenRoute,
|
|
114
|
+
screenStateStore,
|
|
115
|
+
behavior,
|
|
116
|
+
}: {
|
|
117
|
+
item: ZappEntry;
|
|
118
|
+
screenRoute: string;
|
|
119
|
+
screenStateStore: ScreenStateStore;
|
|
120
|
+
behavior?: Behavior;
|
|
121
|
+
}): boolean => {
|
|
79
122
|
if (!behavior) return false;
|
|
80
123
|
|
|
81
124
|
const id = behavior.selector ? get(item, behavior.selector) : item.id;
|
|
@@ -99,7 +142,32 @@ export const isCellSelected = (
|
|
|
99
142
|
}
|
|
100
143
|
|
|
101
144
|
const selection = String(behavior.current_selection);
|
|
102
|
-
|
|
145
|
+
|
|
146
|
+
const screenKey = parseContextKey(selection, "screen");
|
|
147
|
+
|
|
148
|
+
if (screenKey) {
|
|
149
|
+
if (behavior.select_mode === "single") {
|
|
150
|
+
const selectedItem = ScreenSingleValueProvider.getProvider(
|
|
151
|
+
screenKey,
|
|
152
|
+
screenRoute,
|
|
153
|
+
screenStateStore
|
|
154
|
+
).getValue();
|
|
155
|
+
|
|
156
|
+
return selectedItem === String(id);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (behavior.select_mode === "multi") {
|
|
160
|
+
const selectedItems = ScreenMultiSelectProvider.getProvider(
|
|
161
|
+
screenKey,
|
|
162
|
+
screenRoute,
|
|
163
|
+
screenStateStore
|
|
164
|
+
).getSelectedItems();
|
|
165
|
+
|
|
166
|
+
return selectedItems?.includes(String(id));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const contextKey = parseContextKey(selection, "ctx");
|
|
103
171
|
|
|
104
172
|
if (contextKey) {
|
|
105
173
|
if (behavior.select_mode === "single") {
|
|
@@ -8,6 +8,8 @@ import { masterCellLogger } from "../logger";
|
|
|
8
8
|
import { getCellState } from "../../Cell/utils";
|
|
9
9
|
import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
10
10
|
import { isCellSelected, useBehaviorUpdate } from "./behaviorProvider";
|
|
11
|
+
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
12
|
+
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
|
|
11
13
|
|
|
12
14
|
const hasElementSpecificViewType = (viewType) => (element) => {
|
|
13
15
|
if (R.isNil(element)) {
|
|
@@ -190,8 +192,18 @@ export const getFocusedButtonId = (focusable) => {
|
|
|
190
192
|
});
|
|
191
193
|
};
|
|
192
194
|
|
|
193
|
-
export const isSelected = (
|
|
194
|
-
|
|
195
|
+
export const isSelected = ({
|
|
196
|
+
item,
|
|
197
|
+
screenRoute,
|
|
198
|
+
screenStateStore,
|
|
199
|
+
behavior,
|
|
200
|
+
}: {
|
|
201
|
+
item: ZappEntry;
|
|
202
|
+
screenRoute: string;
|
|
203
|
+
screenStateStore: ScreenStateStore;
|
|
204
|
+
behavior?: Behavior;
|
|
205
|
+
}) => {
|
|
206
|
+
return isCellSelected({ item, screenRoute, screenStateStore, behavior });
|
|
195
207
|
};
|
|
196
208
|
|
|
197
209
|
export const useCellState = ({
|
|
@@ -204,9 +216,17 @@ export const useCellState = ({
|
|
|
204
216
|
focused: boolean;
|
|
205
217
|
}): CellState => {
|
|
206
218
|
const lastUpdate = useBehaviorUpdate(behavior);
|
|
219
|
+
const router = useRoute();
|
|
220
|
+
const screenStateStore = useScreenStateStore();
|
|
207
221
|
|
|
208
222
|
const _isSelected = useMemo(
|
|
209
|
-
() =>
|
|
223
|
+
() =>
|
|
224
|
+
isSelected({
|
|
225
|
+
item,
|
|
226
|
+
screenRoute: router?.pathname,
|
|
227
|
+
screenStateStore,
|
|
228
|
+
behavior,
|
|
229
|
+
}),
|
|
210
230
|
[behavior, item, lastUpdate]
|
|
211
231
|
);
|
|
212
232
|
|
|
@@ -88,7 +88,7 @@ export const VideoModalMode = {
|
|
|
88
88
|
MAXIMIZED: "MAXIMIZED",
|
|
89
89
|
MINIMIZED: "MINIMIZED",
|
|
90
90
|
FULLSCREEN: "FULLSCREEN",
|
|
91
|
-
};
|
|
91
|
+
} as const;
|
|
92
92
|
|
|
93
93
|
export type PlayNextData = {
|
|
94
94
|
state: PlayNextState;
|
|
@@ -105,11 +105,6 @@ const isTvOS = isTvOSPlatform();
|
|
|
105
105
|
// height for container with additional content below player
|
|
106
106
|
const INLINE_CONTAINER_CONTENT_HEIGHT = 400;
|
|
107
107
|
|
|
108
|
-
// Default Y offset for anchor points on non-Android TV platforms
|
|
109
|
-
const DEFAULT_OFFSET_Y = -600;
|
|
110
|
-
|
|
111
|
-
const EXTRA_ANCHOR_POINT_Y_OFFSET = isAndroidTV ? 0 : DEFAULT_OFFSET_Y;
|
|
112
|
-
|
|
113
108
|
const withBorderHack = () => {
|
|
114
109
|
if (isAndroidTV) {
|
|
115
110
|
/* @HACK: see GH#7269 */
|
|
@@ -132,7 +127,7 @@ const webStyles = {
|
|
|
132
127
|
playerScreen: {
|
|
133
128
|
flex: 1,
|
|
134
129
|
height: "100vh",
|
|
135
|
-
|
|
130
|
+
backgroundColor: "black",
|
|
136
131
|
},
|
|
137
132
|
playerWrapper: {
|
|
138
133
|
height: "100%",
|
|
@@ -150,7 +145,6 @@ const nativeStyles = {
|
|
|
150
145
|
},
|
|
151
146
|
playerScreen: {
|
|
152
147
|
flex: 1,
|
|
153
|
-
backgroundColor: "black",
|
|
154
148
|
overflow: "hidden",
|
|
155
149
|
},
|
|
156
150
|
playerWrapper: {
|
|
@@ -570,8 +564,9 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
570
564
|
const isInlineTV = isInlineTVUtil(screenData);
|
|
571
565
|
|
|
572
566
|
const inline =
|
|
573
|
-
[VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(
|
|
574
|
-
|
|
567
|
+
[VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(
|
|
568
|
+
mode as any
|
|
569
|
+
) || isInlineTV;
|
|
575
570
|
|
|
576
571
|
const value = React.useMemo(
|
|
577
572
|
() => ({ playerId: state.playerId }),
|
|
@@ -592,7 +587,11 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
592
587
|
);
|
|
593
588
|
}
|
|
594
589
|
|
|
595
|
-
if (
|
|
590
|
+
if (
|
|
591
|
+
screen_background_color &&
|
|
592
|
+
mode !== VideoModalMode.FULLSCREEN &&
|
|
593
|
+
isTV()
|
|
594
|
+
) {
|
|
596
595
|
updatedStyles.playerScreen.backgroundColor = screen_background_color;
|
|
597
596
|
}
|
|
598
597
|
|
|
@@ -622,6 +621,8 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
622
621
|
playNextData,
|
|
623
622
|
};
|
|
624
623
|
|
|
624
|
+
const pointerEventsProp = mode === "MINIMIZED" ? "box-none" : "auto";
|
|
625
|
+
|
|
625
626
|
return (
|
|
626
627
|
<PlayerStateContext.Provider value={value}>
|
|
627
628
|
<PlayerContainerContextProvider
|
|
@@ -642,14 +643,17 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
642
643
|
preferredFocus
|
|
643
644
|
shouldUsePreferredFocus
|
|
644
645
|
groupId={groupId}
|
|
646
|
+
pointerEvents={pointerEventsProp}
|
|
645
647
|
>
|
|
646
648
|
{/* Video player and components */}
|
|
647
649
|
<View
|
|
648
650
|
style={styles.playerScreen}
|
|
649
651
|
testID={"player-screen-container"}
|
|
652
|
+
pointerEvents={pointerEventsProp}
|
|
650
653
|
>
|
|
651
654
|
{/* Player container */}
|
|
652
655
|
<View
|
|
656
|
+
pointerEvents={pointerEventsProp}
|
|
653
657
|
style={[
|
|
654
658
|
styles.playerWrapper,
|
|
655
659
|
// eslint-disable-next-line react-native/no-inline-styles, react-native/no-color-literals
|
|
@@ -721,13 +725,11 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
721
725
|
key={item.id}
|
|
722
726
|
groupId={FocusableGroupMainContainerId}
|
|
723
727
|
cellTapAction={onCellTap}
|
|
724
|
-
extraAnchorPointYOffset={
|
|
725
|
-
EXTRA_ANCHOR_POINT_Y_OFFSET
|
|
726
|
-
}
|
|
728
|
+
extraAnchorPointYOffset={0}
|
|
727
729
|
isScreenWrappedInContainer={true}
|
|
728
730
|
containerHeight={styles.inlineRiver.height}
|
|
729
731
|
componentsMapExtraProps={{
|
|
730
|
-
isNestedComponentsMap:
|
|
732
|
+
isNestedComponentsMap: true,
|
|
731
733
|
}}
|
|
732
734
|
/>
|
|
733
735
|
)}
|
|
@@ -26,7 +26,6 @@ type Props = {
|
|
|
26
26
|
componentsMapExtraProps?: any;
|
|
27
27
|
isInsideContainer?: boolean;
|
|
28
28
|
extraAnchorPointYOffset: number;
|
|
29
|
-
extraOffset: number;
|
|
30
29
|
river?: ZappRiver | ZappEntry;
|
|
31
30
|
};
|
|
32
31
|
|
|
@@ -39,7 +38,6 @@ export const River = (props: Props) => {
|
|
|
39
38
|
componentsMapExtraProps,
|
|
40
39
|
isInsideContainer,
|
|
41
40
|
extraAnchorPointYOffset,
|
|
42
|
-
extraOffset,
|
|
43
41
|
} = props;
|
|
44
42
|
|
|
45
43
|
const { title: screenTitle, summary: screenSummary } = useNavbarState();
|
|
@@ -120,7 +118,6 @@ export const River = (props: Props) => {
|
|
|
120
118
|
<GeneralContentScreen
|
|
121
119
|
feed={feedData}
|
|
122
120
|
screenId={screenId}
|
|
123
|
-
extraOffset={extraOffset}
|
|
124
121
|
isScreenWrappedInContainer={isInsideContainer}
|
|
125
122
|
extraAnchorPointYOffset={extraAnchorPointYOffset}
|
|
126
123
|
componentsMapExtraProps={componentsMapExtraProps}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { View,
|
|
2
|
+
import { View, ViewStyle } from "react-native";
|
|
3
3
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
4
4
|
import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
5
5
|
import { isFirstComponentScreenPicker } from "@applicaster/zapp-react-native-utils/componentsUtils";
|
|
6
|
+
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
6
7
|
|
|
7
8
|
interface IProps {
|
|
8
9
|
targetScreenId?: string;
|
|
9
10
|
children?: React.ReactNode;
|
|
10
11
|
style?: ViewStyle;
|
|
11
|
-
|
|
12
|
+
extraVerticalOffset: Option<number>;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
type CombinedProps = IProps & ViewProps;
|
|
15
15
|
/**
|
|
16
16
|
* The MarginTop is essentially a feature used for managing the visibility of components on your screen.
|
|
17
17
|
* A more accurate term for this might be something like a 'component visibility threshold' or 'cut-off point'.
|
|
@@ -47,7 +47,7 @@ export const useMarginTop = (targetScreenId: string): number => {
|
|
|
47
47
|
|
|
48
48
|
// Empty string means that value is blank in the CMS. Fallback to theme
|
|
49
49
|
if (String(screenData?.styles?.screen_margin_top) === "") {
|
|
50
|
-
return
|
|
50
|
+
return toNumberWithDefault(0, theme.screen_margin_top);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -58,28 +58,29 @@ export const useMarginTop = (targetScreenId: string): number => {
|
|
|
58
58
|
*/
|
|
59
59
|
if (screenData?.styles?.screen_margin_top === undefined) {
|
|
60
60
|
if (isGeneralContentScreen || supportsUiComponents) {
|
|
61
|
-
return
|
|
61
|
+
return toNumberWithDefault(0, theme.screen_margin_top);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
return 0;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
return
|
|
67
|
+
return toNumberWithDefault(0, screenData?.styles?.screen_margin_top);
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
-
export const TopMarginApplicator: React.FC<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
export const TopMarginApplicator: React.FC<IProps> = ({
|
|
71
|
+
targetScreenId,
|
|
72
|
+
style,
|
|
73
|
+
children,
|
|
74
|
+
extraVerticalOffset,
|
|
75
|
+
}: IProps) => {
|
|
76
|
+
const extraOffset = toNumberWithDefault(0, extraVerticalOffset);
|
|
74
77
|
|
|
75
78
|
// HACK: Remove extraOffset when focusIssue with absolute elements is fixed on tvos
|
|
76
|
-
const marginTop = useMarginTop(
|
|
77
|
-
const style = { ...((props.style as {}) || {}), marginTop };
|
|
79
|
+
const marginTop = useMarginTop(targetScreenId);
|
|
78
80
|
|
|
79
|
-
// Then, spread the rest of the props on your returned JSX.
|
|
80
81
|
return (
|
|
81
|
-
<View
|
|
82
|
-
{
|
|
82
|
+
<View style={[style, { marginTop: marginTop + extraOffset }]}>
|
|
83
|
+
{children}
|
|
83
84
|
</View>
|
|
84
85
|
);
|
|
85
86
|
};
|
|
@@ -36,6 +36,7 @@ export function CurrentScreenContextProvider({
|
|
|
36
36
|
screenData: NavigationScreenData;
|
|
37
37
|
}) {
|
|
38
38
|
const { pathname, isActive = false, screenData } = props;
|
|
39
|
+
console.log("CurrentScreenContextProvider", { screenData });
|
|
39
40
|
|
|
40
41
|
const [initialScreenData, setInitialScreenData] = React.useState(screenData);
|
|
41
42
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect } from "react";
|
|
2
|
-
import { Animated } from "react-native";
|
|
2
|
+
import { Animated, Dimensions } from "react-native";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
useSafeAreaInsets,
|
|
@@ -23,6 +23,7 @@ export enum PlayerAnimationStateEnum {
|
|
|
23
23
|
export type PlayerAnimationStateT = number | PlayerAnimationStateEnum | null;
|
|
24
24
|
|
|
25
25
|
export type ModalAnimationContextT = {
|
|
26
|
+
yTranslate: React.MutableRefObject<Animated.Value | null>;
|
|
26
27
|
isActiveGesture: boolean;
|
|
27
28
|
playerAnimationState: PlayerAnimationStateT;
|
|
28
29
|
setPlayerAnimationState: (value: PlayerAnimationStateT) => void;
|
|
@@ -48,6 +49,7 @@ export type ModalAnimationContextT = {
|
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
export const ReactContext = React.createContext<ModalAnimationContextT>({
|
|
52
|
+
yTranslate: React.createRef<Animated.Value | null>(),
|
|
51
53
|
isActiveGesture: false,
|
|
52
54
|
playerAnimationState: null,
|
|
53
55
|
setPlayerAnimationState: () => null,
|
|
@@ -73,6 +75,10 @@ export const ReactContext = React.createContext<ModalAnimationContextT>({
|
|
|
73
75
|
});
|
|
74
76
|
|
|
75
77
|
const Provider = ({ children }: { children: React.ReactNode }) => {
|
|
78
|
+
const yTranslate = React.useRef(
|
|
79
|
+
new Animated.Value(Dimensions.get("window").height)
|
|
80
|
+
);
|
|
81
|
+
|
|
76
82
|
const [playerAnimationState, setPlayerAnimationState] =
|
|
77
83
|
React.useState<PlayerAnimationStateT>(null);
|
|
78
84
|
|
|
@@ -100,6 +106,7 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
|
|
|
100
106
|
// Reset player animation state when video modal is closed
|
|
101
107
|
if (!visible) {
|
|
102
108
|
resetPlayerAnimationState();
|
|
109
|
+
yTranslate.current?.setValue(Dimensions.get("window").height);
|
|
103
110
|
}
|
|
104
111
|
}, [visible, resetPlayerAnimationState]);
|
|
105
112
|
|
|
@@ -141,6 +148,7 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
|
|
|
141
148
|
return (
|
|
142
149
|
<ReactContext.Provider
|
|
143
150
|
value={{
|
|
151
|
+
yTranslate,
|
|
144
152
|
startComponentsAnimation,
|
|
145
153
|
setStartComponentsAnimation,
|
|
146
154
|
isActiveGesture: playerAnimationState !== null,
|