@applicaster/zapp-react-native-ui-components 13.0.0-alpha.7223526501 → 13.0.0-alpha.7429912110
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/AnimatedInOut/index.tsx +3 -5
- package/Components/AudioPlayer/AudioPlayer.tsx +5 -9
- package/Components/AudioPlayer/AudioPlayerLayout.tsx +8 -9
- package/Components/AudioPlayer/helpers.tsx +1 -2
- package/Components/Cell/Cell.tsx +3 -13
- package/Components/Cell/CellWithFocusable.tsx +21 -15
- package/Components/Cell/CellWrapper.tsx +18 -0
- package/Components/Cell/styles.ts +17 -0
- package/Components/CellRendererResolver/index.ts +2 -1
- package/Components/ErrorScreen/index.tsx +8 -0
- package/Components/FocusableCell/index.tsx +1 -1
- package/Components/FocusableGroup/FocusableTvOS.tsx +3 -6
- package/Components/FocusableGroup/index.tsx +0 -1
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +3 -0
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +4 -2
- package/Components/GeneralContentScreen/utils/useEventAlerts.ts +30 -0
- package/Components/HandlePlayable/HandlePlayable.tsx +21 -27
- package/Components/Layout/TV/LayoutBackground.tsx +28 -0
- package/Components/Layout/TV/ScreenContainer.tsx +8 -2
- package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +5 -0
- package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +2 -1
- package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +15 -10
- package/Components/Layout/TV/__tests__/index.test.tsx +8 -2
- package/Components/Layout/TV/index.tsx +6 -20
- package/Components/Layout/TV/index.web.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +4 -3
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +11 -7
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/const.ts +3 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -6
- package/Components/MasterCell/utils/behaviorProvider.ts +136 -0
- package/Components/MasterCell/utils/index.ts +8 -134
- package/Components/ModalComponent/BottomSheetModalContent.tsx +66 -54
- package/Components/ModalComponent/Button/Item.tsx +6 -5
- package/Components/ModalComponent/Button/assets.ts +1 -1
- package/Components/ModalComponent/Button/index.tsx +45 -62
- package/Components/ModalComponent/Header/index.tsx +9 -8
- package/Components/ModalComponent/utils.ts +55 -7
- package/Components/OfflineHandler/utils/index.ts +1 -1
- package/Components/PlayerContainer/PlayerContainer.tsx +55 -46
- package/Components/PlayerImageBackground/index.tsx +4 -24
- package/Components/River/ComponentsMap/ComponentsMap.tsx +18 -3
- package/Components/River/ComponentsMap/hooks/useLoadingState.ts +3 -3
- package/Components/River/RiverItem.tsx +25 -5
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +7 -1
- package/Components/RouteManager/TestId.tsx +1 -5
- package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +0 -1
- package/Components/RouteManager/__tests__/testId.test.js +0 -4
- package/Components/Screen/TV/__tests__/index.web.test.tsx +26 -0
- package/Components/Screen/TV/index.web.tsx +5 -6
- package/Components/Screen/__tests__/Screen.test.tsx +22 -14
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -2
- package/Components/Screen/hooks.ts +3 -6
- package/Components/TopMarginApplicator/TopMarginApplicator.tsx +2 -3
- package/Components/TrackedView/index.tsx +1 -0
- package/Components/Transitioner/AnimationManager.js +15 -15
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +4 -0
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
- package/Components/VideoLive/animationUtils.ts +4 -7
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +1 -1
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +35 -19
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +3 -3
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +38 -14
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +11 -3
- package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +108 -0
- package/Components/VideoModal/ModalAnimation/const.ts +5 -0
- package/Components/VideoModal/ModalAnimation/index.ts +2 -0
- package/Components/VideoModal/ModalAnimation/utils.ts +30 -0
- package/Components/VideoModal/PlayerDetails.tsx +9 -4
- package/Components/VideoModal/PlayerWrapper.tsx +35 -22
- package/Components/VideoModal/VideoModal.tsx +19 -4
- package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -40
- package/Components/VideoModal/utils.ts +3 -4
- package/Components/ZappUIComponent/index.tsx +4 -4
- package/Contexts/FocusableGroupContext/withFocusableContext.tsx +4 -10
- package/Contexts/HeaderOffsetContext/index.tsx +4 -6
- package/Contexts/ScreenContext/index.tsx +3 -10
- package/Contexts/ScreenLayoutContext/index.tsx +5 -3
- package/Decorators/RiverResolver/index.tsx +5 -7
- package/Decorators/ZappPipesDataConnector/index.tsx +4 -30
- package/package.json +5 -9
- package/tsconfig.json +2 -3
- package/.babelrc +0 -8
- package/Components/Cell/CellStyles/FallbackCellStyle/index.js +0 -157
- package/Components/Cell/CellStyles/Hero/index.js +0 -111
- package/Components/Cell/CellStyles/ScreenSelector/index.js +0 -68
- package/Components/Cell/CellStyles/cellStylesResolver.ts +0 -19
- package/Components/Cell/CellStyles/colors.js +0 -40
- package/Components/Cell/CellStyles/index.js +0 -15
|
@@ -6,10 +6,15 @@ import * as R from "ramda";
|
|
|
6
6
|
import uuid from "uuid/v4";
|
|
7
7
|
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
|
|
8
8
|
import {
|
|
9
|
-
isApplePlatform,
|
|
10
9
|
isTV,
|
|
11
10
|
platformSelect,
|
|
11
|
+
isAndroidTVPlatform,
|
|
12
|
+
isTvOSPlatform,
|
|
12
13
|
} from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
14
|
+
import {
|
|
15
|
+
isAudioItem,
|
|
16
|
+
isInlineTV as isInlineTVUtil,
|
|
17
|
+
} from "@applicaster/zapp-react-native-utils/playerUtils";
|
|
13
18
|
|
|
14
19
|
import { TVEventHandlerComponent } from "@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent";
|
|
15
20
|
import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
|
|
@@ -20,7 +25,7 @@ import {
|
|
|
20
25
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
21
26
|
|
|
22
27
|
import { PlayerStateContext } from "../../Contexts/PlayerStateContext";
|
|
23
|
-
|
|
28
|
+
|
|
24
29
|
import {
|
|
25
30
|
QUICK_BRICK_EVENTS,
|
|
26
31
|
QuickBrickEvent,
|
|
@@ -93,6 +98,25 @@ export type PlayNextData = {
|
|
|
93
98
|
|
|
94
99
|
const focusableBottomContainerId = "player-container-bottom";
|
|
95
100
|
|
|
101
|
+
const isAndroidTV = isAndroidTVPlatform();
|
|
102
|
+
|
|
103
|
+
const isTvOS = isTvOSPlatform();
|
|
104
|
+
|
|
105
|
+
// height for container with additional content below player
|
|
106
|
+
const INLINE_CONTAINER_CONTENT_HEIGHT = 400;
|
|
107
|
+
|
|
108
|
+
const withBorderHack = () => {
|
|
109
|
+
if (isAndroidTV) {
|
|
110
|
+
/* @HACK: see GH#7269 */
|
|
111
|
+
return {
|
|
112
|
+
borderWidth: 1,
|
|
113
|
+
borderColor: "transparent",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {};
|
|
118
|
+
};
|
|
119
|
+
|
|
96
120
|
// Styles
|
|
97
121
|
const webStyles = {
|
|
98
122
|
focusableGroup: {
|
|
@@ -110,7 +134,7 @@ const webStyles = {
|
|
|
110
134
|
flex: "initial",
|
|
111
135
|
},
|
|
112
136
|
inlineRiver: {
|
|
113
|
-
height:
|
|
137
|
+
height: INLINE_CONTAINER_CONTENT_HEIGHT,
|
|
114
138
|
},
|
|
115
139
|
};
|
|
116
140
|
|
|
@@ -128,7 +152,7 @@ const nativeStyles = {
|
|
|
128
152
|
flex: 1,
|
|
129
153
|
},
|
|
130
154
|
inlineRiver: {
|
|
131
|
-
height:
|
|
155
|
+
height: INLINE_CONTAINER_CONTENT_HEIGHT,
|
|
132
156
|
},
|
|
133
157
|
};
|
|
134
158
|
|
|
@@ -148,12 +172,11 @@ const renderApplePlayer = ({
|
|
|
148
172
|
videoStyle,
|
|
149
173
|
playNextData,
|
|
150
174
|
}) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (!isAppleTV() || !player) {
|
|
175
|
+
if (!isTvOS || !player) {
|
|
154
176
|
return null;
|
|
155
177
|
}
|
|
156
178
|
|
|
179
|
+
// render audio_player for tvos
|
|
157
180
|
if (isAudioContent) {
|
|
158
181
|
return (
|
|
159
182
|
<AudioPlayer
|
|
@@ -167,6 +190,8 @@ const renderApplePlayer = ({
|
|
|
167
190
|
return null;
|
|
168
191
|
}
|
|
169
192
|
|
|
193
|
+
const { title, summary } = item || {};
|
|
194
|
+
|
|
170
195
|
return (
|
|
171
196
|
<ProgramInfo
|
|
172
197
|
title={title}
|
|
@@ -177,21 +202,6 @@ const renderApplePlayer = ({
|
|
|
177
202
|
);
|
|
178
203
|
};
|
|
179
204
|
|
|
180
|
-
const renderRestPlayers = ({ item, showAudioPlayer, pluginConfiguration }) => {
|
|
181
|
-
if (isApplePlatform()) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return (
|
|
186
|
-
showAudioPlayer && (
|
|
187
|
-
<AudioPlayer
|
|
188
|
-
audio_item={item}
|
|
189
|
-
plugin_configuration={pluginConfiguration}
|
|
190
|
-
/>
|
|
191
|
-
)
|
|
192
|
-
);
|
|
193
|
-
};
|
|
194
|
-
|
|
195
205
|
const PlayerContainerComponent = (props: Props) => {
|
|
196
206
|
const {
|
|
197
207
|
Player,
|
|
@@ -321,7 +331,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
321
331
|
|
|
322
332
|
setState({ error: errorObj });
|
|
323
333
|
|
|
324
|
-
if (!
|
|
334
|
+
if (!isTvOS) {
|
|
325
335
|
setTimeout(() => {
|
|
326
336
|
close();
|
|
327
337
|
}, 800);
|
|
@@ -335,9 +345,9 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
335
345
|
|
|
336
346
|
const resumeTime = Number(item?.extensions?.resumeTime);
|
|
337
347
|
|
|
338
|
-
//
|
|
348
|
+
// Сhecking that the player itself knows where to start playing, and there is no need to call seekTo after returning from the Сhromecast
|
|
339
349
|
if (
|
|
340
|
-
|
|
350
|
+
!playerManager.getActivePlayer()?.hasResumePosition() &&
|
|
341
351
|
!isNaN(resumeTime)
|
|
342
352
|
) {
|
|
343
353
|
player?.seekTo(resumeTime);
|
|
@@ -478,9 +488,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
478
488
|
// TODO: Skip hook call on TV platform
|
|
479
489
|
useBackHandler(backHandler);
|
|
480
490
|
|
|
481
|
-
const isAudioContent =
|
|
482
|
-
typeof item?.content?.type === "string" &&
|
|
483
|
-
item?.content?.type.includes("audio");
|
|
491
|
+
const isAudioContent = isAudioItem(item);
|
|
484
492
|
|
|
485
493
|
useEffect(() => {
|
|
486
494
|
if (!isAudioContent) {
|
|
@@ -514,6 +522,12 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
514
522
|
useEffect(() => {
|
|
515
523
|
playerEvent("source_changed", { item });
|
|
516
524
|
|
|
525
|
+
return () => {
|
|
526
|
+
playerEvent("player_did_close", { item });
|
|
527
|
+
};
|
|
528
|
+
}, [item?.id, Player]);
|
|
529
|
+
|
|
530
|
+
useEffect(() => {
|
|
517
531
|
if (!isModal) {
|
|
518
532
|
showNavBar(false);
|
|
519
533
|
}
|
|
@@ -523,13 +537,11 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
523
537
|
}
|
|
524
538
|
|
|
525
539
|
return () => {
|
|
526
|
-
playerEvent("player_did_close", { item });
|
|
527
|
-
|
|
528
540
|
if (!isModal) {
|
|
529
541
|
showNavBar(true);
|
|
530
542
|
}
|
|
531
543
|
};
|
|
532
|
-
}, [showNavBar,
|
|
544
|
+
}, [showNavBar, isModal, Player]);
|
|
533
545
|
|
|
534
546
|
useEffect(() => {
|
|
535
547
|
if (prevItemId && !R.equals(prevItemId, item?.id)) {
|
|
@@ -550,10 +562,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
550
562
|
|
|
551
563
|
const uri = item?.content ? item.content?.src : null;
|
|
552
564
|
|
|
553
|
-
const isInlineTV =
|
|
554
|
-
screenData?.ui_components &&
|
|
555
|
-
screenData?.ui_components?.length > 0 &&
|
|
556
|
-
isTV();
|
|
565
|
+
const isInlineTV = isInlineTVUtil(screenData);
|
|
557
566
|
|
|
558
567
|
const inline =
|
|
559
568
|
[VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(mode) ||
|
|
@@ -578,7 +587,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
578
587
|
);
|
|
579
588
|
}
|
|
580
589
|
|
|
581
|
-
if (screen_background_color) {
|
|
590
|
+
if (screen_background_color && mode !== VideoModalMode.FULLSCREEN) {
|
|
582
591
|
updatedStyles.playerScreen.backgroundColor = screen_background_color;
|
|
583
592
|
}
|
|
584
593
|
|
|
@@ -596,6 +605,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
596
605
|
tv_component_container_height,
|
|
597
606
|
screen_background_color,
|
|
598
607
|
isInlineTV,
|
|
608
|
+
mode,
|
|
599
609
|
]);
|
|
600
610
|
|
|
601
611
|
const applePlayerProps = {
|
|
@@ -607,12 +617,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
607
617
|
playNextData,
|
|
608
618
|
};
|
|
609
619
|
|
|
610
|
-
const restPlayerProps = {
|
|
611
|
-
item,
|
|
612
|
-
showAudioPlayer: isAudioContent,
|
|
613
|
-
pluginConfiguration,
|
|
614
|
-
};
|
|
615
|
-
|
|
616
620
|
return (
|
|
617
621
|
<PlayerStateContext.Provider value={value}>
|
|
618
622
|
<PlayerContainerContextProvider
|
|
@@ -640,7 +644,14 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
640
644
|
testID={"player-screen-container"}
|
|
641
645
|
>
|
|
642
646
|
{/* Player container */}
|
|
643
|
-
<View
|
|
647
|
+
<View
|
|
648
|
+
style={[
|
|
649
|
+
styles.playerWrapper,
|
|
650
|
+
// eslint-disable-next-line react-native/no-inline-styles, react-native/no-color-literals
|
|
651
|
+
withBorderHack(),
|
|
652
|
+
]}
|
|
653
|
+
testID={"player-wrapper"}
|
|
654
|
+
>
|
|
644
655
|
<PlayerFocusableWrapperView
|
|
645
656
|
nextFocusDown={context.bottomFocusableId}
|
|
646
657
|
>
|
|
@@ -681,8 +692,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
681
692
|
</Player>
|
|
682
693
|
</PlayerFocusableWrapperView>
|
|
683
694
|
|
|
684
|
-
{renderRestPlayers(restPlayerProps)}
|
|
685
|
-
|
|
686
695
|
{state.error ? <ErrorDisplay error={state.error} /> : null}
|
|
687
696
|
</View>
|
|
688
697
|
{/* Components container */}
|
|
@@ -706,7 +715,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
706
715
|
screenId={screenData.id}
|
|
707
716
|
key={item.id}
|
|
708
717
|
groupId={FocusableGroupMainContainerId}
|
|
709
|
-
cellTapAction={
|
|
718
|
+
cellTapAction={onCellTap}
|
|
710
719
|
extraAnchorPointYOffset={-600}
|
|
711
720
|
isScreenWrappedInContainer={true}
|
|
712
721
|
containerHeight={styles.inlineRiver.height}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { PropsWithChildren
|
|
1
|
+
import React, { PropsWithChildren } from "react";
|
|
2
2
|
import { ImageBackground, View } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
type Props = PropsWithChildren<{
|
|
13
13
|
entry: ZappEntry;
|
|
14
14
|
style?: { [K: string]: any };
|
|
15
|
-
docked?: boolean;
|
|
16
15
|
imageStyle?: { [K: string]: any };
|
|
17
16
|
imageKey?: string;
|
|
18
17
|
defaultImageDimensions?: { [K: string]: any };
|
|
@@ -24,7 +23,6 @@ const PlayerImageBackgroundComponent = ({
|
|
|
24
23
|
entry,
|
|
25
24
|
children,
|
|
26
25
|
style,
|
|
27
|
-
docked,
|
|
28
26
|
imageStyle,
|
|
29
27
|
imageKey,
|
|
30
28
|
defaultImageDimensions,
|
|
@@ -36,42 +34,24 @@ const PlayerImageBackgroundComponent = ({
|
|
|
36
34
|
|
|
37
35
|
const { playerAnimationState } = useModalAnimationContext();
|
|
38
36
|
|
|
39
|
-
const [lastNonNullAnimationState, setLastNonNullAnimationState] =
|
|
40
|
-
useState(playerAnimationState);
|
|
41
|
-
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
if (playerAnimationState !== null) {
|
|
44
|
-
setLastNonNullAnimationState(playerAnimationState);
|
|
45
|
-
}
|
|
46
|
-
}, [playerAnimationState]);
|
|
47
|
-
|
|
48
37
|
if (!source) return <>{children}</>;
|
|
49
38
|
|
|
50
|
-
const imageBackgroundStyle =
|
|
51
|
-
lastNonNullAnimationState !== PlayerAnimationStateEnum.minimize && !docked
|
|
52
|
-
? defaultImageDimensions
|
|
53
|
-
: imageSize;
|
|
54
|
-
|
|
55
39
|
return (
|
|
56
40
|
<View
|
|
57
41
|
style={
|
|
58
|
-
playerAnimationState === PlayerAnimationStateEnum.
|
|
42
|
+
playerAnimationState === PlayerAnimationStateEnum.maximize
|
|
59
43
|
? defaultImageDimensions
|
|
60
44
|
: style
|
|
61
45
|
}
|
|
62
46
|
>
|
|
63
47
|
<AnimationComponent
|
|
64
|
-
style={
|
|
65
|
-
playerAnimationState === PlayerAnimationStateEnum.maximaze
|
|
66
|
-
? defaultImageDimensions
|
|
67
|
-
: style
|
|
68
|
-
}
|
|
48
|
+
style={style}
|
|
69
49
|
animationType={ComponentAnimationType.player}
|
|
70
50
|
additionalData={defaultImageDimensions}
|
|
71
51
|
>
|
|
72
52
|
<ImageBackground
|
|
73
53
|
resizeMode="cover"
|
|
74
|
-
style={
|
|
54
|
+
style={imageSize}
|
|
75
55
|
imageStyle={imageStyle}
|
|
76
56
|
source={source}
|
|
77
57
|
>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
-
import { View, StyleSheet,
|
|
3
|
+
import { View, StyleSheet, 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";
|
|
@@ -24,6 +24,10 @@ import { withComponentsMapProvider } from "@applicaster/zapp-react-native-ui-com
|
|
|
24
24
|
import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
25
25
|
import { useShallow } from "zustand/react/shallow";
|
|
26
26
|
|
|
27
|
+
import { isAndroidPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
28
|
+
|
|
29
|
+
const isAndroid = isAndroidPlatform();
|
|
30
|
+
|
|
27
31
|
type Props = {
|
|
28
32
|
feed: ZappFeed;
|
|
29
33
|
groupId?: string;
|
|
@@ -34,6 +38,7 @@ type Props = {
|
|
|
34
38
|
getStaticComponentFeed: any;
|
|
35
39
|
pullToRefreshPipesV1RefreshingStateUpdater: () => boolean;
|
|
36
40
|
refreshingPipesV1?: boolean;
|
|
41
|
+
stickyHeaderIndices?: number[];
|
|
37
42
|
};
|
|
38
43
|
|
|
39
44
|
const styles = StyleSheet.create({
|
|
@@ -61,6 +66,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
61
66
|
// TODO: Remove when pipes v1 is deprecated.
|
|
62
67
|
pullToRefreshPipesV1RefreshingStateUpdater,
|
|
63
68
|
refreshingPipesV1,
|
|
69
|
+
stickyHeaderIndices,
|
|
64
70
|
} = props;
|
|
65
71
|
|
|
66
72
|
const flatListRef = React.useRef<FlatList | null>(null);
|
|
@@ -87,6 +93,13 @@ function ComponentsMapComponent(props: Props) {
|
|
|
87
93
|
const { loadingState, onLoadFinished, onLoadFailed, shouldShowLoadingError } =
|
|
88
94
|
useLoadingState(riverComponents.length, onLoadDone);
|
|
89
95
|
|
|
96
|
+
// console.log("RiverItem ComponentsMapComponent useLoadingState", {
|
|
97
|
+
// riverId,
|
|
98
|
+
// riverComponents,
|
|
99
|
+
// loadingState: loadingState.getValue(),
|
|
100
|
+
// arePreviousComponentsLoaded,
|
|
101
|
+
// });
|
|
102
|
+
|
|
90
103
|
const theme = useTheme();
|
|
91
104
|
|
|
92
105
|
const renderRiverItem = React.useCallback(
|
|
@@ -271,14 +284,16 @@ function ComponentsMapComponent(props: Props) {
|
|
|
271
284
|
}}
|
|
272
285
|
// Fix for WebView rerender crashes on Android API 28+
|
|
273
286
|
// https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
|
|
274
|
-
overScrollMode={
|
|
287
|
+
overScrollMode={isAndroid ? "never" : "auto"}
|
|
275
288
|
scrollIndicatorInsets={scrollIndicatorInsets}
|
|
276
289
|
extraData={feed}
|
|
290
|
+
stickyHeaderIndices={stickyHeaderIndices}
|
|
291
|
+
removeClippedSubviews={isAndroid}
|
|
277
292
|
onLayout={handleOnLayout}
|
|
278
|
-
removeClippedSubviews
|
|
279
293
|
initialNumToRender={3}
|
|
280
294
|
maxToRenderPerBatch={10}
|
|
281
295
|
windowSize={12}
|
|
296
|
+
listKey={riverId}
|
|
282
297
|
keyExtractor={keyExtractor}
|
|
283
298
|
renderItem={renderRiverItem}
|
|
284
299
|
data={riverComponents}
|
|
@@ -28,10 +28,10 @@ type Return = {
|
|
|
28
28
|
// TODO: Take this value from Zapp configuration, when feature is added to GeneralScreen
|
|
29
29
|
const SHOULD_FAIL_ON_COMPONENT_LOADING = false;
|
|
30
30
|
|
|
31
|
-
const createLoadingStateObservable = () =>
|
|
31
|
+
const createLoadingStateObservable = (count: number) =>
|
|
32
32
|
new BehaviorSubject<LoadingState>({
|
|
33
33
|
index: -1,
|
|
34
|
-
done:
|
|
34
|
+
done: count === 0,
|
|
35
35
|
waitForAllComponents: SHOULD_FAIL_ON_COMPONENT_LOADING,
|
|
36
36
|
});
|
|
37
37
|
|
|
@@ -43,7 +43,7 @@ export const useLoadingState = (
|
|
|
43
43
|
const [loadingError, setLoadingError] = React.useState(null);
|
|
44
44
|
|
|
45
45
|
const loadingState = useRefWithInitialValue<BehaviorSubject<LoadingState>>(
|
|
46
|
-
createLoadingStateObservable
|
|
46
|
+
() => createLoadingStateObservable(count)
|
|
47
47
|
);
|
|
48
48
|
|
|
49
49
|
const arePreviousComponentsLoaded = React.useCallback((index) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useEffect } from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
+
import { isGroup } from "@applicaster/zapp-react-native-utils/componentsUtils";
|
|
3
4
|
|
|
4
5
|
import { applyDecorators } from "../../Decorators";
|
|
5
6
|
|
|
@@ -38,6 +39,10 @@ function getFeedUrl(feed: ZappFeed, index: number) {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
const isNextIndex = (index, readyIndex) => {
|
|
43
|
+
return readyIndex + 1 >= index;
|
|
44
|
+
};
|
|
45
|
+
|
|
41
46
|
/**
|
|
42
47
|
* useLoadingState for RiverItemComponent
|
|
43
48
|
* takes currentIndex and loadingState as arguments
|
|
@@ -47,12 +52,18 @@ const useLoadingState = (
|
|
|
47
52
|
loadingState: RiverItemType["loadingState"]
|
|
48
53
|
) => {
|
|
49
54
|
const [readyToBeDisplayed, setReadyToBeDisplayed] = React.useState(
|
|
50
|
-
loadingState.getValue().index
|
|
55
|
+
isNextIndex(currentIndex, loadingState.getValue().index)
|
|
51
56
|
);
|
|
52
57
|
|
|
53
58
|
useEffect(() => {
|
|
54
59
|
const subscription = loadingState.subscribe(({ index }) => {
|
|
55
|
-
|
|
60
|
+
// console.log("RiverItem useLoadingState subscription", {
|
|
61
|
+
// currentIndex,
|
|
62
|
+
// index,
|
|
63
|
+
// readyToBeDisplayed,
|
|
64
|
+
// });
|
|
65
|
+
|
|
66
|
+
if (isNextIndex(currentIndex, index)) {
|
|
56
67
|
setReadyToBeDisplayed(true);
|
|
57
68
|
}
|
|
58
69
|
});
|
|
@@ -60,9 +71,18 @@ const useLoadingState = (
|
|
|
60
71
|
return () => {
|
|
61
72
|
subscription.unsubscribe();
|
|
62
73
|
};
|
|
63
|
-
}, [
|
|
74
|
+
}, [currentIndex]);
|
|
64
75
|
|
|
65
|
-
|
|
76
|
+
// console.log("RiverItem useLoadingState", {
|
|
77
|
+
// currentIndex,
|
|
78
|
+
// readyToBeDisplayed,
|
|
79
|
+
// loadingState: loadingState.getValue(),
|
|
80
|
+
// });
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
readyToBeDisplayed ||
|
|
84
|
+
isNextIndex(loadingState.getValue().index, currentIndex)
|
|
85
|
+
);
|
|
66
86
|
};
|
|
67
87
|
|
|
68
88
|
function RiverItemComponent(props: RiverItemType) {
|
|
@@ -112,7 +132,7 @@ function RiverItemComponent(props: RiverItemType) {
|
|
|
112
132
|
jsOnly: true,
|
|
113
133
|
});
|
|
114
134
|
|
|
115
|
-
if (!CellRenderer && item
|
|
135
|
+
if (!CellRenderer && !isGroup(item)) {
|
|
116
136
|
riverLogger.warning({
|
|
117
137
|
message: "Cell Renderer is null - will fallback to default cell",
|
|
118
138
|
data: { item, CellRenderer },
|
|
@@ -135,6 +135,12 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
135
135
|
getItemCount={[Function]}
|
|
136
136
|
initialNumToRender={3}
|
|
137
137
|
keyExtractor={[Function]}
|
|
138
|
+
maintainVisibleContentPosition={
|
|
139
|
+
{
|
|
140
|
+
"autoscrollToTopThreshold": 10,
|
|
141
|
+
"minIndexForVisible": 0,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
138
144
|
maxToRenderPerBatch={10}
|
|
139
145
|
onContentSizeChange={[Function]}
|
|
140
146
|
onLayout={[Function]}
|
|
@@ -145,7 +151,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
145
151
|
onScrollEndDrag={[Function]}
|
|
146
152
|
overScrollMode="auto"
|
|
147
153
|
refreshControl={null}
|
|
148
|
-
removeClippedSubviews={
|
|
154
|
+
removeClippedSubviews={false}
|
|
149
155
|
renderItem={[Function]}
|
|
150
156
|
scrollEventThrottle={16}
|
|
151
157
|
scrollIndicatorInsets={
|
|
@@ -12,11 +12,7 @@ const styles = StyleSheet.create({
|
|
|
12
12
|
|
|
13
13
|
export function TestId({ screenId, children }: Props) {
|
|
14
14
|
return (
|
|
15
|
-
<View
|
|
16
|
-
testID={screenId}
|
|
17
|
-
accessibilityLabel={screenId}
|
|
18
|
-
style={styles.container}
|
|
19
|
-
>
|
|
15
|
+
<View testID={screenId} style={styles.container}>
|
|
20
16
|
{children}
|
|
21
17
|
</View>
|
|
22
18
|
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const {
|
|
2
|
+
useScreenBackgroundColor,
|
|
3
|
+
} = require("@applicaster/zapp-react-native-utils/reactHooks/screen");
|
|
4
|
+
|
|
5
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
|
|
6
|
+
useScreenBackgroundColor: jest.fn(),
|
|
7
|
+
useNavbarState: jest.fn().mockReturnValue({ visible: true }),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe("TV Screen Component", () => {
|
|
11
|
+
it("uses the useScreenBackgroundColor hook with the correct screen ID", () => {
|
|
12
|
+
useScreenBackgroundColor.mockReturnValue("#FF0000");
|
|
13
|
+
|
|
14
|
+
expect(useScreenBackgroundColor("test-screen-id")).toBe("#FF0000");
|
|
15
|
+
|
|
16
|
+
// Verify the hook was called with the correct screen ID
|
|
17
|
+
expect(useScreenBackgroundColor).toHaveBeenCalledWith("test-screen-id");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("returns 'transparent' when the screen background color is not defined", () => {
|
|
21
|
+
useScreenBackgroundColor.mockReturnValue("transparent");
|
|
22
|
+
|
|
23
|
+
// Verify the hook returns 'transparent'
|
|
24
|
+
expect(useScreenBackgroundColor("test-screen-id")).toBe("transparent");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -92,6 +92,10 @@ const getNavigations = ({
|
|
|
92
92
|
return fallbackNavigations;
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
+
const onRelease = () => {
|
|
96
|
+
focusManager.setInitialFocus();
|
|
97
|
+
};
|
|
98
|
+
|
|
95
99
|
export const Screen = ({ route, Components }: Props) => {
|
|
96
100
|
if (isNilOrEmpty(route)) {
|
|
97
101
|
throw Error("Required props: route is missing");
|
|
@@ -152,12 +156,7 @@ export const Screen = ({ route, Components }: Props) => {
|
|
|
152
156
|
const isScreenActive = useIsScreenActive();
|
|
153
157
|
|
|
154
158
|
return (
|
|
155
|
-
<FreezeWithCallback
|
|
156
|
-
freeze={!isScreenActive}
|
|
157
|
-
onRelease={() => {
|
|
158
|
-
focusManager.setInitialFocus();
|
|
159
|
-
}}
|
|
160
|
-
>
|
|
159
|
+
<FreezeWithCallback freeze={!isScreenActive} onRelease={onRelease}>
|
|
161
160
|
<View style={[styles.container, { backgroundColor }]}>
|
|
162
161
|
<NavBarContainer isVisible={isNavBarVisible} onReady={noop}>
|
|
163
162
|
<NavBar
|
|
@@ -68,21 +68,21 @@ jest.mock(
|
|
|
68
68
|
})
|
|
69
69
|
);
|
|
70
70
|
|
|
71
|
-
jest.mock(
|
|
72
|
-
"@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenData",
|
|
73
|
-
() => ({
|
|
74
|
-
useScreenData: jest.fn(() => ({
|
|
75
|
-
id: "testId",
|
|
76
|
-
navigations: [{ id: "testId", category: "nav_bar" }],
|
|
77
|
-
})),
|
|
78
|
-
})
|
|
79
|
-
);
|
|
80
|
-
|
|
81
71
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
82
72
|
isNavBarVisible: mockIsNavBarVisible,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
useIsScreenActive: jest.fn().mockReturnValue(true),
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
77
|
+
useCurrentScreenData: jest.fn(() => ({
|
|
78
|
+
id: "testId",
|
|
79
|
+
})),
|
|
80
|
+
useNavbarState: jest.fn(() => ({
|
|
81
|
+
title: "Test Title",
|
|
82
|
+
})),
|
|
83
|
+
useScreenData: jest.fn(() => ({
|
|
84
|
+
id: "testId",
|
|
85
|
+
navigations: [{ id: "testId", category: "nav_bar" }],
|
|
86
86
|
})),
|
|
87
87
|
useNavigation: jest.fn(() => ({
|
|
88
88
|
canGoBack: () => false,
|
|
@@ -91,7 +91,15 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
|
91
91
|
screenData: { id: "testId" },
|
|
92
92
|
data: { screen: { id: "testId" } },
|
|
93
93
|
})),
|
|
94
|
-
|
|
94
|
+
useRoute: jest.fn(() => ({
|
|
95
|
+
pathname: "/river/testId",
|
|
96
|
+
screenData: { id: "testId" },
|
|
97
|
+
})),
|
|
98
|
+
useDimensions: jest.fn(() => ({
|
|
99
|
+
width: 1920,
|
|
100
|
+
height: 1080,
|
|
101
|
+
})),
|
|
102
|
+
useIsTablet: jest.fn(() => false),
|
|
95
103
|
}));
|
|
96
104
|
|
|
97
105
|
jest.mock("@applicaster/zapp-react-native-redux/hooks/usePickFromState", () => {
|
|
@@ -16,7 +16,7 @@ exports[`<Screen Component /> when the navbar should be hidden renders correctly
|
|
|
16
16
|
pathname="/river/testId"
|
|
17
17
|
selected="testId"
|
|
18
18
|
testID="navBar"
|
|
19
|
-
title=""
|
|
19
|
+
title="Test Title"
|
|
20
20
|
/>
|
|
21
21
|
<View>
|
|
22
22
|
<View
|
|
@@ -48,7 +48,7 @@ exports[`<Screen Component /> when the navbar should show renders correctly 1`]
|
|
|
48
48
|
pathname="/river/testId"
|
|
49
49
|
selected="testId"
|
|
50
50
|
testID="navBar"
|
|
51
|
-
title=""
|
|
51
|
+
title="Test Title"
|
|
52
52
|
/>
|
|
53
53
|
<View>
|
|
54
54
|
<View
|
|
@@ -7,15 +7,12 @@ import {
|
|
|
7
7
|
useCurrentScreenData,
|
|
8
8
|
useDimensions,
|
|
9
9
|
useRoute,
|
|
10
|
+
useIsTablet,
|
|
10
11
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
11
12
|
import { useMemo, useEffect, useState } from "react";
|
|
12
13
|
|
|
13
14
|
export const useWaitForValidOrientation = () => {
|
|
14
|
-
const {
|
|
15
|
-
width: screenWidth,
|
|
16
|
-
height,
|
|
17
|
-
deviceInfo,
|
|
18
|
-
} = useDimensions("screen", {
|
|
15
|
+
const { width: screenWidth, height } = useDimensions("screen", {
|
|
19
16
|
fullDimensions: true,
|
|
20
17
|
updateForInactiveScreens: false,
|
|
21
18
|
});
|
|
@@ -26,7 +23,7 @@ export const useWaitForValidOrientation = () => {
|
|
|
26
23
|
|
|
27
24
|
const [readyState, setReadyState] = useState(false);
|
|
28
25
|
|
|
29
|
-
const isTablet =
|
|
26
|
+
const isTablet = useIsTablet();
|
|
30
27
|
|
|
31
28
|
const { appData } = usePickFromState(["appData"]);
|
|
32
29
|
const isTabletPortrait = appData?.isTabletPortrait;
|