@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5135101838 → 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/BaseFocusable/index.tsx +1 -1
- package/Components/Cell/Cell.tsx +5 -1
- package/Components/Cell/CellWithFocusable.tsx +17 -5
- package/Components/FocusableGroup/index.tsx +1 -0
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
- package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
- package/Components/Layout/TV/index.tsx +124 -25
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
- package/Components/PlayerContainer/PlayerContainer.tsx +11 -6
- package/Components/River/ComponentsMap/ComponentsMap.tsx +85 -22
- package/Components/River/TV/River.tsx +0 -1
- package/Components/Screen/TV/index.web.tsx +151 -0
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +4 -0
- package/Components/Screen/index.tsx +35 -24
- package/Components/ScreenResolver/index.tsx +3 -1
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +0 -5
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +11 -4
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +9 -2
- package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +1 -1
- package/Components/Viewport/ViewportTracker/index.tsx +1 -0
- package/Components/Viewport/utils.js +1 -1
- 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 +94 -25
- package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
- package/package.json +5 -5
- /package/Contexts/{index.js → index.ts} +0 -0
|
@@ -73,7 +73,7 @@ export class BaseFocusable<
|
|
|
73
73
|
getId(props?: { id: string; groupId: string }) {
|
|
74
74
|
const { id, groupId = null } = props || this.props;
|
|
75
75
|
|
|
76
|
-
return groupId ?
|
|
76
|
+
return groupId ? `groupId:${groupId}___${id}` : id;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
getRect() {
|
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -207,7 +207,11 @@ export class CellComponent extends React.Component<Props> {
|
|
|
207
207
|
|
|
208
208
|
const { id } = item;
|
|
209
209
|
|
|
210
|
-
const focusableId = R.join("-", [
|
|
210
|
+
const focusableId = R.join("-", [
|
|
211
|
+
`componentId:${component?.id}`,
|
|
212
|
+
`id:${id}`,
|
|
213
|
+
`index:${index}`,
|
|
214
|
+
]);
|
|
211
215
|
|
|
212
216
|
const handleFocus = (focusable, mouse) => {
|
|
213
217
|
const focusFn = onFocus || noop;
|
|
@@ -6,12 +6,20 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
|
6
6
|
import { isAppleTV } from "../../Helpers/Platform";
|
|
7
7
|
import { useCellState } from "../MasterCell/utils";
|
|
8
8
|
|
|
9
|
-
const useCellFocusedState = (
|
|
9
|
+
const useCellFocusedState = (
|
|
10
|
+
skipFocusManagerRegistration: boolean,
|
|
11
|
+
groupId: string,
|
|
12
|
+
id: string
|
|
13
|
+
) => {
|
|
10
14
|
const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
|
|
11
15
|
|
|
12
16
|
React.useEffect(() => {
|
|
13
|
-
const isGroupItemFocused = () =>
|
|
14
|
-
|
|
17
|
+
const isGroupItemFocused = () => {
|
|
18
|
+
if (!skipFocusManagerRegistration) {
|
|
19
|
+
const isFocused = focusManager.isGroupItemFocused(groupId, id);
|
|
20
|
+
setCurrentCellFocused(isFocused);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
15
23
|
|
|
16
24
|
const handler = () => {
|
|
17
25
|
// tvOS hack for properly checking focus
|
|
@@ -29,7 +37,7 @@ const useCellFocusedState = (groupId: string, id: string) => {
|
|
|
29
37
|
return () => {
|
|
30
38
|
focusManager.removeHandler(FOCUS_EVENTS.FOCUS, handler);
|
|
31
39
|
};
|
|
32
|
-
}, [groupId]);
|
|
40
|
+
}, [groupId, skipFocusManagerRegistration]);
|
|
33
41
|
|
|
34
42
|
return currentCellFocused;
|
|
35
43
|
};
|
|
@@ -65,7 +73,11 @@ export function CellWithFocusable(props: Props) {
|
|
|
65
73
|
selected,
|
|
66
74
|
} = props;
|
|
67
75
|
|
|
68
|
-
const isFocused = useCellFocusedState(
|
|
76
|
+
const isFocused = useCellFocusedState(
|
|
77
|
+
skipFocusManagerRegistration,
|
|
78
|
+
groupId,
|
|
79
|
+
id
|
|
80
|
+
);
|
|
69
81
|
|
|
70
82
|
const state = useCellState({
|
|
71
83
|
id: item.id,
|
|
@@ -50,7 +50,11 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
50
50
|
);
|
|
51
51
|
|
|
52
52
|
expect(transformed_preset).toBeDefined();
|
|
53
|
-
|
|
53
|
+
|
|
54
|
+
expect(transformed_preset.id).toEqual(
|
|
55
|
+
`${mockComp.id}-${mockPreset.feed_url}-0`
|
|
56
|
+
); // id was transformed
|
|
57
|
+
|
|
54
58
|
expect(transformed_preset.data.source).toEqual(mockPreset.feed_url); // source was replaced with preset feed_url.
|
|
55
59
|
});
|
|
56
60
|
});
|
|
@@ -117,7 +121,10 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
117
121
|
|
|
118
122
|
expect(Array.isArray(result)).toBe(true);
|
|
119
123
|
expect(result).toHaveLength(2);
|
|
120
|
-
|
|
124
|
+
|
|
125
|
+
expect(result[0].id).toEqual(
|
|
126
|
+
`${mockSmartComp.id}-${mockFeeds.preset_source.data.entry[0].feed_url}-0`
|
|
127
|
+
);
|
|
121
128
|
|
|
122
129
|
expect(result[0].data.source).toEqual(
|
|
123
130
|
mockFeeds.preset_source.data.entry[0].feed_url
|
|
@@ -287,7 +294,7 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
287
294
|
mockComponents[0],
|
|
288
295
|
{
|
|
289
296
|
...mockLayoutPresets.preset_one,
|
|
290
|
-
id: mockComponents[1].id
|
|
297
|
+
id: `${mockComponents[1].id}-${mockPresetEntry[0].feed_url}-1`,
|
|
291
298
|
data: { source: mockPresetEntry[0].feed_url },
|
|
292
299
|
},
|
|
293
300
|
];
|
|
@@ -344,12 +351,12 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
344
351
|
const mockTransformedComponents = [
|
|
345
352
|
{
|
|
346
353
|
...mockLayoutPresets.preset_one,
|
|
347
|
-
id: mockComponents[0].id
|
|
354
|
+
id: `${mockComponents[0].id}-${mockPresetEntry1[0].feed_url}-0`,
|
|
348
355
|
data: { source: mockPresetEntry1[0].feed_url },
|
|
349
356
|
},
|
|
350
357
|
{
|
|
351
358
|
...mockLayoutPresets.preset_one,
|
|
352
|
-
id: mockComponents[1].id
|
|
359
|
+
id: `${mockComponents[1].id}-${mockPresetEntry2[0].feed_url}-1`,
|
|
353
360
|
data: { source: mockPresetEntry2[0].feed_url },
|
|
354
361
|
},
|
|
355
362
|
];
|
|
@@ -412,7 +419,7 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
412
419
|
const mockTransformedComponents = mockPresetEntry.map((preset, i) => {
|
|
413
420
|
return {
|
|
414
421
|
...mockLayoutPresets[preset.preset_name],
|
|
415
|
-
id: mockComponents[0].id
|
|
422
|
+
id: `${mockComponents[0].id}-${preset.feed_url}-${i}`,
|
|
416
423
|
data: { source: preset.feed_url },
|
|
417
424
|
};
|
|
418
425
|
});
|
|
@@ -41,7 +41,7 @@ export const getTransformedPreset = (
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const updatedPresetComponent = produce(presetComponent, (draft) => {
|
|
44
|
-
draft.id = `${componentId}
|
|
44
|
+
draft.id = `${componentId}-${preset?.feed_url}-${index}`;
|
|
45
45
|
draft.data.source = preset?.feed_url;
|
|
46
46
|
|
|
47
47
|
if (draft.ui_components) {
|
|
@@ -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
|
|
|
@@ -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
|
}
|
|
@@ -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
|
);
|
|
@@ -1,11 +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
5
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
5
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";
|
|
6
9
|
|
|
7
10
|
import {
|
|
8
11
|
getNavigationProps,
|
|
12
|
+
getPathAttributes,
|
|
9
13
|
resolveNavigationPlugin,
|
|
10
14
|
routeIsPlayerScreen,
|
|
11
15
|
} from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
@@ -17,9 +21,25 @@ import { ScreenContainer } from "./ScreenContainer";
|
|
|
17
21
|
import { TransitionOverlay } from "./TransitionOverlay";
|
|
18
22
|
|
|
19
23
|
import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
|
|
20
|
-
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
21
24
|
import { PathnameContext } from "../../../Contexts/PathnameContext";
|
|
22
|
-
|
|
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
|
+
});
|
|
23
43
|
|
|
24
44
|
type Components = {
|
|
25
45
|
NavBar: React.ComponentType<any>;
|
|
@@ -27,16 +47,73 @@ type Components = {
|
|
|
27
47
|
};
|
|
28
48
|
|
|
29
49
|
type ComponentsExtraProps = {
|
|
30
|
-
Background?: Record<string, any>;
|
|
31
50
|
NavBar?: Record<string, any>;
|
|
51
|
+
Background?: Record<string, any>;
|
|
32
52
|
};
|
|
33
53
|
|
|
34
54
|
type Props = {
|
|
35
55
|
Components: Components;
|
|
36
|
-
|
|
37
|
-
|
|
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);
|
|
101
|
+
};
|
|
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
|
+
);
|
|
38
114
|
};
|
|
39
115
|
|
|
116
|
+
// @ts-ignore
|
|
40
117
|
const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
41
118
|
const navigator = useNavigation();
|
|
42
119
|
const theme = useTheme();
|
|
@@ -98,32 +175,54 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
|
98
175
|
}
|
|
99
176
|
|
|
100
177
|
return (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
+
/**
|
|
111
193
|
Need to re-mount this component on each navigation event
|
|
112
194
|
It's a hack to work around the fact that we don't support
|
|
113
195
|
correct screen rendering in our navigator (shared currentRoute state)
|
|
114
196
|
**/
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
197
|
+
<PathnameContext.Provider value={navigator.currentRoute}>
|
|
198
|
+
<ScreenContainer>{children}</ScreenContainer>
|
|
199
|
+
</PathnameContext.Provider>
|
|
200
|
+
) : null}
|
|
201
|
+
{/** Render NavBarContainer over ScreenContainer to let tvOS native
|
|
120
202
|
focus manager works correctly to focus nav bar items first **/}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
203
|
+
{isTVOS ? navBarContainer : null}
|
|
204
|
+
<TransitionOverlay />
|
|
205
|
+
</Components.Background>
|
|
206
|
+
</ScreenLayoutContextProvider>
|
|
207
|
+
</LayoutContainer>
|
|
208
|
+
</TransitionerTV>
|
|
126
209
|
);
|
|
127
210
|
};
|
|
128
211
|
|
|
129
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
|
+
*/
|
|
@@ -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
|
);
|
|
@@ -54,7 +54,10 @@ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
|
54
54
|
import { usePlayNextOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayNextOverlay";
|
|
55
55
|
import { PlayNextState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/OverlayObserver/OverlaysObserver";
|
|
56
56
|
|
|
57
|
-
import {
|
|
57
|
+
import {
|
|
58
|
+
useModalAnimationContext,
|
|
59
|
+
PlayerAnimationStateEnum,
|
|
60
|
+
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
58
61
|
|
|
59
62
|
type Props = {
|
|
60
63
|
Player: React.ComponentType<any>;
|
|
@@ -65,6 +68,7 @@ type Props = {
|
|
|
65
68
|
mode?: VideoModalMode;
|
|
66
69
|
loadPipesData: (url: string, options: {}) => void;
|
|
67
70
|
disableCastAction?: boolean;
|
|
71
|
+
groupId?: string;
|
|
68
72
|
};
|
|
69
73
|
|
|
70
74
|
type ScreenStyles = ZappRiver["styles"] & {
|
|
@@ -197,6 +201,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
197
201
|
style: videoStyle,
|
|
198
202
|
mode = VideoModalMode.MAXIMIZED,
|
|
199
203
|
disableCastAction = false,
|
|
204
|
+
groupId,
|
|
200
205
|
} = props;
|
|
201
206
|
|
|
202
207
|
// Hooks
|
|
@@ -231,6 +236,9 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
231
236
|
const screenData = useTargetScreenData(item);
|
|
232
237
|
const { setVisible: showNavBar } = useSetNavbarState();
|
|
233
238
|
|
|
239
|
+
const { isActiveGesture, startComponentsAnimation, setPlayerAnimationState } =
|
|
240
|
+
useModalAnimationContext();
|
|
241
|
+
|
|
234
242
|
const playerEvent = (event, ...args) => {
|
|
235
243
|
playerManager.invokeHandler(event, ...args);
|
|
236
244
|
};
|
|
@@ -243,7 +251,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
243
251
|
return;
|
|
244
252
|
}
|
|
245
253
|
|
|
246
|
-
navigator.goBack();
|
|
247
254
|
showNavBar(true);
|
|
248
255
|
}, [isModal, navigator.goBack, showNavBar]);
|
|
249
256
|
|
|
@@ -448,7 +455,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
448
455
|
if (disableMiniPlayer) {
|
|
449
456
|
navigator.closeVideoModal();
|
|
450
457
|
} else {
|
|
451
|
-
|
|
458
|
+
setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
|
|
452
459
|
}
|
|
453
460
|
}
|
|
454
461
|
|
|
@@ -600,9 +607,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
600
607
|
pluginConfiguration,
|
|
601
608
|
};
|
|
602
609
|
|
|
603
|
-
const { isActiveGesture, startComponentsAnimation } =
|
|
604
|
-
useModalAnimationContext();
|
|
605
|
-
|
|
606
610
|
return (
|
|
607
611
|
<PlayerStateContext.Provider value={value}>
|
|
608
612
|
<PlayerContainerContextProvider
|
|
@@ -626,6 +630,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
626
630
|
style={styles.focusableGroup}
|
|
627
631
|
preferredFocus
|
|
628
632
|
shouldUsePreferredFocus
|
|
633
|
+
groupId={groupId}
|
|
629
634
|
>
|
|
630
635
|
{/* Video player and components */}
|
|
631
636
|
<View
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
-
import { View,
|
|
3
|
+
import { View, StyleSheet, Platform, Animated, FlatList } from "react-native";
|
|
4
4
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
5
5
|
import { RiverItem } from "../RiverItem";
|
|
6
6
|
import { RiverFooter } from "../RiverFooter";
|
|
@@ -21,7 +21,8 @@ import {
|
|
|
21
21
|
import { useScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenData";
|
|
22
22
|
import { isLast } from "@applicaster/zapp-react-native-utils/arrayUtils";
|
|
23
23
|
import { withComponentsMapProvider } from "@applicaster/zapp-react-native-ui-components/Decorators/ComponentsMapWrapper";
|
|
24
|
-
import {
|
|
24
|
+
import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
25
|
+
import { useShallow } from "zustand/react/shallow";
|
|
25
26
|
|
|
26
27
|
type Props = {
|
|
27
28
|
feed: ZappFeed;
|
|
@@ -41,8 +42,10 @@ const styles = StyleSheet.create({
|
|
|
41
42
|
},
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
const
|
|
45
|
+
const getNearestValue = (value, optionA, optionB) =>
|
|
46
|
+
Math.abs(value - optionA) < Math.abs(value - optionB) ? optionA : optionB;
|
|
45
47
|
|
|
48
|
+
const scrollIndicatorInsets = { right: 1 };
|
|
46
49
|
const keyExtractor = R.compose(String, R.prop("id"));
|
|
47
50
|
|
|
48
51
|
function ComponentsMapComponent(props: Props) {
|
|
@@ -60,6 +63,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
60
63
|
refreshingPipesV1,
|
|
61
64
|
} = props;
|
|
62
65
|
|
|
66
|
+
const flatListRef = React.useRef<FlatList | null>(null);
|
|
63
67
|
const screenConfig = useScreenConfiguration(riverId);
|
|
64
68
|
const screenData = useScreenData(riverId);
|
|
65
69
|
const pullToRefreshEnabled = screenData?.rules?.pull_to_refresh_enabled;
|
|
@@ -194,26 +198,67 @@ function ComponentsMapComponent(props: Props) {
|
|
|
194
198
|
[]
|
|
195
199
|
);
|
|
196
200
|
|
|
197
|
-
const
|
|
198
|
-
({ nativeEvent }, isScrollingDone = false) => {
|
|
199
|
-
const {
|
|
200
|
-
contentOffset: { y },
|
|
201
|
-
} = nativeEvent;
|
|
201
|
+
const navBarStore = useScreenContextV2()._navBarStore;
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
scrollPosition: y,
|
|
206
|
-
});
|
|
207
|
-
},
|
|
208
|
-
[]
|
|
203
|
+
const scrollY = navBarStore(
|
|
204
|
+
useShallow(({ scrollYAnimated }) => scrollYAnimated)
|
|
209
205
|
);
|
|
210
206
|
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
207
|
+
const canMomentum = React.useRef(false);
|
|
208
|
+
|
|
209
|
+
const onScrollBeginDrag = React.useCallback(() => {
|
|
210
|
+
canMomentum.current = true;
|
|
211
|
+
}, []);
|
|
212
|
+
|
|
213
|
+
/* onMomentumScrollEnd: Handling snap to expanded/collapsed state ( for navbar ) */
|
|
214
|
+
const _onMomentumScrollEnd = React.useCallback(({ nativeEvent }) => {
|
|
215
|
+
if (!canMomentum.current) return;
|
|
216
|
+
canMomentum.current = false;
|
|
217
|
+
|
|
218
|
+
const { height: headerHeight, scrollState } = navBarStore.getState();
|
|
219
|
+
|
|
220
|
+
const offsetY = nativeEvent.contentOffset.y;
|
|
221
|
+
|
|
222
|
+
if (
|
|
223
|
+
!(scrollState === 0 || scrollState === -headerHeight) &&
|
|
224
|
+
flatListRef.current
|
|
225
|
+
) {
|
|
226
|
+
const offset =
|
|
227
|
+
getNearestValue(Math.round(scrollState), -headerHeight, 0) ===
|
|
228
|
+
-headerHeight
|
|
229
|
+
? Math.round(offsetY + scrollState + headerHeight)
|
|
230
|
+
: Math.round(offsetY + scrollState);
|
|
231
|
+
|
|
232
|
+
flatListRef.current.scrollToOffset({
|
|
233
|
+
animated: true,
|
|
234
|
+
offset,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}, []);
|
|
238
|
+
|
|
239
|
+
/* onScrollEnd: Handling snap to expanded/collapsed state ( for navbar ) */
|
|
240
|
+
const _onScrollEndDrag = React.useCallback(({ nativeEvent }) => {
|
|
241
|
+
const { height: headerHeight, scrollState } = navBarStore.getState();
|
|
242
|
+
|
|
243
|
+
const offsetY = nativeEvent.contentOffset.y;
|
|
244
|
+
|
|
245
|
+
if (
|
|
246
|
+
!(scrollState === 0 || scrollState === -headerHeight) &&
|
|
247
|
+
flatListRef.current &&
|
|
248
|
+
nativeEvent.velocity.y === 0
|
|
249
|
+
) {
|
|
250
|
+
const offset =
|
|
251
|
+
getNearestValue(Math.round(scrollState), -headerHeight, 0) ===
|
|
252
|
+
-headerHeight
|
|
253
|
+
? Math.round(offsetY + scrollState + headerHeight)
|
|
254
|
+
: Math.round(offsetY + scrollState);
|
|
255
|
+
|
|
256
|
+
flatListRef.current.scrollToOffset({
|
|
257
|
+
animated: true,
|
|
258
|
+
offset,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}, []);
|
|
217
262
|
|
|
218
263
|
if (shouldShowLoadingError) {
|
|
219
264
|
return <RiverError />;
|
|
@@ -230,6 +275,9 @@ function ComponentsMapComponent(props: Props) {
|
|
|
230
275
|
>
|
|
231
276
|
<ViewportTracker>
|
|
232
277
|
<FlatList
|
|
278
|
+
ref={(ref) => {
|
|
279
|
+
flatListRef.current = ref;
|
|
280
|
+
}}
|
|
233
281
|
// Fix for WebView rerender crashes on Android API 28+
|
|
234
282
|
// https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
|
|
235
283
|
overScrollMode={Platform.OS === "android" ? "never" : "auto"}
|
|
@@ -249,8 +297,23 @@ function ComponentsMapComponent(props: Props) {
|
|
|
249
297
|
}
|
|
250
298
|
ListFooterComponent={renderFooter}
|
|
251
299
|
refreshControl={refreshControl}
|
|
252
|
-
|
|
253
|
-
|
|
300
|
+
onScrollBeginDrag={onScrollBeginDrag}
|
|
301
|
+
onScroll={Animated.event(
|
|
302
|
+
[
|
|
303
|
+
{
|
|
304
|
+
nativeEvent: {
|
|
305
|
+
contentOffset: {
|
|
306
|
+
y: scrollY,
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
{
|
|
312
|
+
useNativeDriver: false,
|
|
313
|
+
}
|
|
314
|
+
)}
|
|
315
|
+
onMomentumScrollEnd={_onMomentumScrollEnd}
|
|
316
|
+
onScrollEndDrag={_onScrollEndDrag}
|
|
254
317
|
scrollEventThrottle={16}
|
|
255
318
|
{...scrollViewExtraProps}
|
|
256
319
|
/>
|