@applicaster/zapp-react-native-ui-components 14.0.0-alpha.1661204539 → 14.0.0-alpha.2175196485
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 +5 -3
- package/Components/AudioPlayer/index.tsx +15 -0
- package/Components/AudioPlayer/mobile/Layout.tsx +66 -0
- package/Components/AudioPlayer/{__tests__/__snapshots__/audioPlayer.test.js.snap → mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap} +2 -2
- package/Components/AudioPlayer/mobile/__tests__/audioPlayerMobileLayout.test.js +18 -0
- package/Components/AudioPlayer/mobile/index.tsx +18 -0
- package/Components/AudioPlayer/tv/Artwork.tsx +41 -0
- package/Components/AudioPlayer/{Channel.tsx → tv/Channel.tsx} +7 -7
- package/Components/AudioPlayer/tv/Layout.tsx +168 -0
- package/Components/AudioPlayer/{Runtime.tsx → tv/Runtime.tsx} +9 -2
- package/Components/AudioPlayer/{Summary.tsx → tv/Summary.tsx} +17 -10
- package/Components/AudioPlayer/tv/Title.tsx +46 -0
- package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/Runtime.test.js.snap +4 -4
- package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +9 -4
- package/Components/AudioPlayer/tv/__tests__/__snapshots__/audioPlayer.test.js.snap +164 -0
- package/Components/AudioPlayer/tv/__tests__/__snapshots__/channel.test.js.snap +19 -0
- package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/summary.test.js.snap +2 -3
- package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/title.test.js.snap +2 -3
- package/Components/AudioPlayer/{__tests__ → tv/__tests__}/audioPlayer.test.js +7 -3
- package/Components/AudioPlayer/{helpers.tsx → tv/helpers.tsx} +3 -4
- package/Components/AudioPlayer/{AudioPlayer.tsx → tv/index.tsx} +18 -57
- package/Components/AudioPlayer/types.ts +40 -0
- package/Components/Cell/index.js +6 -2
- package/Components/Focusable/Focusable.tsx +5 -3
- package/Components/Focusable/FocusableTvOS.tsx +3 -3
- package/Components/Focusable/FocusableiOS.tsx +1 -1
- package/Components/Focusable/__tests__/index.android.test.tsx +3 -0
- package/Components/Focusable/index.android.tsx +12 -8
- package/Components/FocusableList/index.tsx +4 -0
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +0 -2
- package/Components/HandlePlayable/HandlePlayable.tsx +25 -9
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -27
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +1 -0
- package/Components/MasterCell/elementMapper.tsx +1 -2
- package/Components/MasterCell/index.tsx +1 -1
- package/Components/MasterCell/utils/behaviorProvider.ts +12 -67
- package/Components/MasterCell/utils/index.ts +3 -13
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
- package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +62 -66
- package/Components/PlayerImageBackground/index.tsx +1 -1
- package/Components/River/ComponentsMap/ComponentsMap.tsx +1 -6
- package/Components/River/RiverItem.tsx +8 -8
- package/Components/River/TV/River.tsx +0 -3
- package/Components/River/TV/withTVEventHandler.tsx +1 -1
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -6
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
- package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
- package/Components/ScreenRevealManager/index.ts +1 -0
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +79 -0
- package/Components/Tabs/TV/Tabs.android.tsx +0 -2
- package/Components/TopMarginApplicator/TopMarginApplicator.tsx +16 -15
- package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
- package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
- package/Components/VideoLive/animationUtils.ts +3 -3
- 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 -180
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +17 -55
- package/Components/VideoModal/hooks/index.ts +0 -2
- package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +15 -26
- package/Components/VideoModal/hooks/useModalSize.ts +18 -2
- package/Components/VideoModal/utils.ts +6 -0
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
- package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
- package/Components/Viewport/VisibilitySensor/VisibilitySensor.tsx +3 -3
- package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/index.ts +3 -3
- package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
- package/Decorators/RiverFeedLoader/index.tsx +2 -8
- package/Decorators/RiverFeedLoader/utils/index.ts +2 -7
- package/Decorators/ZappPipesDataConnector/index.tsx +2 -16
- package/index.d.ts +0 -1
- package/package.json +5 -9
- package/Components/AudioPlayer/Artwork.tsx +0 -35
- package/Components/AudioPlayer/AudioPlayerLayout.tsx +0 -202
- package/Components/AudioPlayer/Title.tsx +0 -39
- package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +0 -66
- package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +0 -28
- package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
- package/Components/AudioPlayer/index.ts +0 -1
- package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
- /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
- /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
- /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
- /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
- /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useEffect, useReducer } from "react";
|
|
3
|
-
|
|
4
|
-
import { TVMenuControl, View, ViewStyle } from "react-native";
|
|
3
|
+
import { View, ViewStyle } from "react-native";
|
|
5
4
|
import * as R from "ramda";
|
|
6
5
|
import uuid from "uuid/v4";
|
|
7
6
|
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
|
|
8
7
|
import {
|
|
9
|
-
isApplePlatform,
|
|
10
8
|
isTV,
|
|
11
9
|
platformSelect,
|
|
12
10
|
isAndroidTVPlatform,
|
|
11
|
+
isTvOSPlatform,
|
|
13
12
|
} from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
14
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
isAudioItem,
|
|
15
|
+
isInlineTV as isInlineTVUtil,
|
|
16
|
+
} from "@applicaster/zapp-react-native-utils/playerUtils";
|
|
15
17
|
|
|
16
18
|
import { TVEventHandlerComponent } from "@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent";
|
|
17
19
|
import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
|
|
@@ -22,7 +24,7 @@ import {
|
|
|
22
24
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
23
25
|
|
|
24
26
|
import { PlayerStateContext } from "../../Contexts/PlayerStateContext";
|
|
25
|
-
|
|
27
|
+
|
|
26
28
|
import {
|
|
27
29
|
QUICK_BRICK_EVENTS,
|
|
28
30
|
QuickBrickEvent,
|
|
@@ -59,6 +61,11 @@ import {
|
|
|
59
61
|
useModalAnimationContext,
|
|
60
62
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
61
63
|
|
|
64
|
+
import {
|
|
65
|
+
PlayerNativeCommandTypes,
|
|
66
|
+
PlayerNativeSendCommand,
|
|
67
|
+
} from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerNativeCommand";
|
|
68
|
+
|
|
62
69
|
type Props = {
|
|
63
70
|
Player: React.ComponentType<any>;
|
|
64
71
|
PlayerLoadingView?: React.ComponentType<any>; // 👀 we are not receiving this prop
|
|
@@ -85,7 +92,7 @@ export const VideoModalMode = {
|
|
|
85
92
|
MAXIMIZED: "MAXIMIZED",
|
|
86
93
|
MINIMIZED: "MINIMIZED",
|
|
87
94
|
FULLSCREEN: "FULLSCREEN",
|
|
88
|
-
};
|
|
95
|
+
} as const;
|
|
89
96
|
|
|
90
97
|
export type PlayNextData = {
|
|
91
98
|
state: PlayNextState;
|
|
@@ -97,6 +104,11 @@ const focusableBottomContainerId = "player-container-bottom";
|
|
|
97
104
|
|
|
98
105
|
const isAndroidTV = isAndroidTVPlatform();
|
|
99
106
|
|
|
107
|
+
const isTvOS = isTvOSPlatform();
|
|
108
|
+
|
|
109
|
+
// height for container with additional content below player
|
|
110
|
+
const INLINE_CONTAINER_CONTENT_HEIGHT = 400;
|
|
111
|
+
|
|
100
112
|
const withBorderHack = () => {
|
|
101
113
|
if (isAndroidTV) {
|
|
102
114
|
/* @HACK: see GH#7269 */
|
|
@@ -119,14 +131,14 @@ const webStyles = {
|
|
|
119
131
|
playerScreen: {
|
|
120
132
|
flex: 1,
|
|
121
133
|
height: "100vh",
|
|
122
|
-
|
|
134
|
+
backgroundColor: "black",
|
|
123
135
|
},
|
|
124
136
|
playerWrapper: {
|
|
125
137
|
height: "100%",
|
|
126
138
|
flex: "initial",
|
|
127
139
|
},
|
|
128
140
|
inlineRiver: {
|
|
129
|
-
height:
|
|
141
|
+
height: INLINE_CONTAINER_CONTENT_HEIGHT,
|
|
130
142
|
},
|
|
131
143
|
};
|
|
132
144
|
|
|
@@ -137,14 +149,13 @@ const nativeStyles = {
|
|
|
137
149
|
},
|
|
138
150
|
playerScreen: {
|
|
139
151
|
flex: 1,
|
|
140
|
-
backgroundColor: "black",
|
|
141
152
|
overflow: "hidden",
|
|
142
153
|
},
|
|
143
154
|
playerWrapper: {
|
|
144
155
|
flex: 1,
|
|
145
156
|
},
|
|
146
157
|
inlineRiver: {
|
|
147
|
-
height:
|
|
158
|
+
height: INLINE_CONTAINER_CONTENT_HEIGHT,
|
|
148
159
|
},
|
|
149
160
|
};
|
|
150
161
|
|
|
@@ -164,12 +175,11 @@ const renderApplePlayer = ({
|
|
|
164
175
|
videoStyle,
|
|
165
176
|
playNextData,
|
|
166
177
|
}) => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (!isAppleTV() || !player) {
|
|
178
|
+
if (!isTvOS || !player) {
|
|
170
179
|
return null;
|
|
171
180
|
}
|
|
172
181
|
|
|
182
|
+
// render audio_player for tvos
|
|
173
183
|
if (isAudioContent) {
|
|
174
184
|
return (
|
|
175
185
|
<AudioPlayer
|
|
@@ -183,6 +193,8 @@ const renderApplePlayer = ({
|
|
|
183
193
|
return null;
|
|
184
194
|
}
|
|
185
195
|
|
|
196
|
+
const { title, summary } = item || {};
|
|
197
|
+
|
|
186
198
|
return (
|
|
187
199
|
<ProgramInfo
|
|
188
200
|
title={title}
|
|
@@ -193,21 +205,6 @@ const renderApplePlayer = ({
|
|
|
193
205
|
);
|
|
194
206
|
};
|
|
195
207
|
|
|
196
|
-
const renderRestPlayers = ({ item, showAudioPlayer, pluginConfiguration }) => {
|
|
197
|
-
if (isApplePlatform()) {
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return (
|
|
202
|
-
showAudioPlayer && (
|
|
203
|
-
<AudioPlayer
|
|
204
|
-
audio_item={item}
|
|
205
|
-
plugin_configuration={pluginConfiguration}
|
|
206
|
-
/>
|
|
207
|
-
)
|
|
208
|
-
);
|
|
209
|
-
};
|
|
210
|
-
|
|
211
208
|
const PlayerContainerComponent = (props: Props) => {
|
|
212
209
|
const {
|
|
213
210
|
Player,
|
|
@@ -266,9 +263,15 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
266
263
|
return;
|
|
267
264
|
}
|
|
268
265
|
|
|
266
|
+
// send command to clear and stop player
|
|
267
|
+
PlayerNativeSendCommand(
|
|
268
|
+
PlayerNativeCommandTypes.clearPlayerData,
|
|
269
|
+
state.playerId
|
|
270
|
+
);
|
|
271
|
+
|
|
269
272
|
showNavBar(true);
|
|
270
273
|
navigator.goBack();
|
|
271
|
-
}, [isModal, navigator.goBack, showNavBar]);
|
|
274
|
+
}, [isModal, navigator.goBack, state.playerId, showNavBar]);
|
|
272
275
|
|
|
273
276
|
const playEntry = (entry) => navigator.replaceTop(entry, { mode });
|
|
274
277
|
|
|
@@ -337,7 +340,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
337
340
|
|
|
338
341
|
setState({ error: errorObj });
|
|
339
342
|
|
|
340
|
-
if (!
|
|
343
|
+
if (!isTvOS) {
|
|
341
344
|
setTimeout(() => {
|
|
342
345
|
close();
|
|
343
346
|
}, 800);
|
|
@@ -396,13 +399,17 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
396
399
|
}
|
|
397
400
|
};
|
|
398
401
|
|
|
399
|
-
const playerRemoteHandler = (
|
|
400
|
-
|
|
402
|
+
const playerRemoteHandler = React.useCallback(
|
|
403
|
+
(isLanguageOverlayVisible = false) =>
|
|
404
|
+
(event) => {
|
|
405
|
+
const { eventType } = event;
|
|
401
406
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
407
|
+
if (!isLanguageOverlayVisible && eventType === "menu") {
|
|
408
|
+
close();
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
[close]
|
|
412
|
+
);
|
|
406
413
|
|
|
407
414
|
// Effects
|
|
408
415
|
useEffect(() => {
|
|
@@ -515,16 +522,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
515
522
|
}
|
|
516
523
|
}, [isAudioContent]);
|
|
517
524
|
|
|
518
|
-
// Needs to handle back button on Apple TV
|
|
519
|
-
// https://github.com/facebook/react-native/issues/18930
|
|
520
|
-
useEffect(() => {
|
|
521
|
-
TVMenuControl?.enableTVMenuKey();
|
|
522
|
-
|
|
523
|
-
return () => {
|
|
524
|
-
TVMenuControl?.disableTVMenuKey();
|
|
525
|
-
};
|
|
526
|
-
}, []);
|
|
527
|
-
|
|
528
525
|
useEffect(() => {
|
|
529
526
|
playerEvent("source_changed", { item });
|
|
530
527
|
|
|
@@ -568,14 +565,12 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
568
565
|
|
|
569
566
|
const uri = item?.content ? item.content?.src : null;
|
|
570
567
|
|
|
571
|
-
const isInlineTV =
|
|
572
|
-
screenData?.ui_components &&
|
|
573
|
-
screenData?.ui_components?.length > 0 &&
|
|
574
|
-
isTV();
|
|
568
|
+
const isInlineTV = isInlineTVUtil(screenData);
|
|
575
569
|
|
|
576
570
|
const inline =
|
|
577
|
-
[VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(
|
|
578
|
-
|
|
571
|
+
[VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(
|
|
572
|
+
mode as any
|
|
573
|
+
) || isInlineTV;
|
|
579
574
|
|
|
580
575
|
const value = React.useMemo(
|
|
581
576
|
() => ({ playerId: state.playerId }),
|
|
@@ -596,7 +591,11 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
596
591
|
);
|
|
597
592
|
}
|
|
598
593
|
|
|
599
|
-
if (
|
|
594
|
+
if (
|
|
595
|
+
screen_background_color &&
|
|
596
|
+
mode !== VideoModalMode.FULLSCREEN &&
|
|
597
|
+
isTV()
|
|
598
|
+
) {
|
|
600
599
|
updatedStyles.playerScreen.backgroundColor = screen_background_color;
|
|
601
600
|
}
|
|
602
601
|
|
|
@@ -626,11 +625,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
626
625
|
playNextData,
|
|
627
626
|
};
|
|
628
627
|
|
|
629
|
-
const
|
|
630
|
-
item,
|
|
631
|
-
showAudioPlayer: isAudioContent,
|
|
632
|
-
pluginConfiguration,
|
|
633
|
-
};
|
|
628
|
+
const pointerEventsProp = mode === "MINIMIZED" ? "box-none" : "auto";
|
|
634
629
|
|
|
635
630
|
return (
|
|
636
631
|
<PlayerStateContext.Provider value={value}>
|
|
@@ -642,9 +637,9 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
642
637
|
<PlayerContainerContext.Consumer>
|
|
643
638
|
{(context) => (
|
|
644
639
|
<TVEventHandlerComponent
|
|
645
|
-
tvEventHandler={(
|
|
646
|
-
|
|
647
|
-
}
|
|
640
|
+
tvEventHandler={playerRemoteHandler(
|
|
641
|
+
context.isLanguageOverlayVisible
|
|
642
|
+
)}
|
|
648
643
|
>
|
|
649
644
|
<FocusableGroup
|
|
650
645
|
id={FocusableGroupMainContainerId}
|
|
@@ -652,14 +647,17 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
652
647
|
preferredFocus
|
|
653
648
|
shouldUsePreferredFocus
|
|
654
649
|
groupId={groupId}
|
|
650
|
+
pointerEvents={pointerEventsProp}
|
|
655
651
|
>
|
|
656
652
|
{/* Video player and components */}
|
|
657
653
|
<View
|
|
658
654
|
style={styles.playerScreen}
|
|
659
655
|
testID={"player-screen-container"}
|
|
656
|
+
pointerEvents={pointerEventsProp}
|
|
660
657
|
>
|
|
661
658
|
{/* Player container */}
|
|
662
659
|
<View
|
|
660
|
+
pointerEvents={pointerEventsProp}
|
|
663
661
|
style={[
|
|
664
662
|
styles.playerWrapper,
|
|
665
663
|
// eslint-disable-next-line react-native/no-inline-styles, react-native/no-color-literals
|
|
@@ -707,8 +705,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
707
705
|
</Player>
|
|
708
706
|
</PlayerFocusableWrapperView>
|
|
709
707
|
|
|
710
|
-
{renderRestPlayers(restPlayerProps)}
|
|
711
|
-
|
|
712
708
|
{state.error ? <ErrorDisplay error={state.error} /> : null}
|
|
713
709
|
</View>
|
|
714
710
|
{/* Components container */}
|
|
@@ -732,12 +728,12 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
732
728
|
screenId={screenData.id}
|
|
733
729
|
key={item.id}
|
|
734
730
|
groupId={FocusableGroupMainContainerId}
|
|
735
|
-
cellTapAction={
|
|
736
|
-
extraAnchorPointYOffset={
|
|
731
|
+
cellTapAction={onCellTap}
|
|
732
|
+
extraAnchorPointYOffset={0}
|
|
737
733
|
isScreenWrappedInContainer={true}
|
|
738
734
|
containerHeight={styles.inlineRiver.height}
|
|
739
735
|
componentsMapExtraProps={{
|
|
740
|
-
isNestedComponentsMap:
|
|
736
|
+
isNestedComponentsMap: true,
|
|
741
737
|
}}
|
|
742
738
|
/>
|
|
743
739
|
)}
|
|
@@ -28,7 +28,7 @@ const PlayerImageBackgroundComponent = ({
|
|
|
28
28
|
defaultImageDimensions,
|
|
29
29
|
}: Props) => {
|
|
30
30
|
const source = React.useMemo(
|
|
31
|
-
() => ({ uri: imageSrcFromMediaItem(entry, imageKey) }),
|
|
31
|
+
() => ({ uri: imageSrcFromMediaItem(entry, [imageKey]) }),
|
|
32
32
|
[imageKey, entry]
|
|
33
33
|
);
|
|
34
34
|
|
|
@@ -281,12 +281,11 @@ function ComponentsMapComponent(props: Props) {
|
|
|
281
281
|
scrollIndicatorInsets={scrollIndicatorInsets}
|
|
282
282
|
extraData={feed}
|
|
283
283
|
stickyHeaderIndices={stickyHeaderIndices}
|
|
284
|
-
onLayout={handleOnLayout}
|
|
285
284
|
removeClippedSubviews={isAndroid}
|
|
285
|
+
onLayout={handleOnLayout}
|
|
286
286
|
initialNumToRender={3}
|
|
287
287
|
maxToRenderPerBatch={10}
|
|
288
288
|
windowSize={12}
|
|
289
|
-
listKey={riverId}
|
|
290
289
|
keyExtractor={keyExtractor}
|
|
291
290
|
renderItem={renderRiverItem}
|
|
292
291
|
data={riverComponents}
|
|
@@ -303,10 +302,6 @@ function ComponentsMapComponent(props: Props) {
|
|
|
303
302
|
onMomentumScrollEnd={_onMomentumScrollEnd}
|
|
304
303
|
onScrollEndDrag={_onScrollEndDrag}
|
|
305
304
|
scrollEventThrottle={16}
|
|
306
|
-
maintainVisibleContentPosition={{
|
|
307
|
-
minIndexForVisible: 0,
|
|
308
|
-
autoscrollToTopThreshold: 10,
|
|
309
|
-
}}
|
|
310
305
|
{...scrollViewExtraProps}
|
|
311
306
|
/>
|
|
312
307
|
</ViewportTracker>
|
|
@@ -39,6 +39,10 @@ function getFeedUrl(feed: ZappFeed, index: number) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
const isNextIndex = (index, readyIndex) => {
|
|
43
|
+
return readyIndex + 1 >= index;
|
|
44
|
+
};
|
|
45
|
+
|
|
42
46
|
/**
|
|
43
47
|
* useLoadingState for RiverItemComponent
|
|
44
48
|
* takes currentIndex and loadingState as arguments
|
|
@@ -48,24 +52,20 @@ const useLoadingState = (
|
|
|
48
52
|
loadingState: RiverItemType["loadingState"]
|
|
49
53
|
) => {
|
|
50
54
|
const [readyToBeDisplayed, setReadyToBeDisplayed] = React.useState(
|
|
51
|
-
loadingState.getValue().index
|
|
55
|
+
isNextIndex(currentIndex, loadingState.getValue().index)
|
|
52
56
|
);
|
|
53
57
|
|
|
54
58
|
useEffect(() => {
|
|
55
59
|
const subscription = loadingState.subscribe(({ index }) => {
|
|
56
|
-
if (index
|
|
60
|
+
if (isNextIndex(currentIndex, index)) {
|
|
57
61
|
setReadyToBeDisplayed(true);
|
|
58
62
|
}
|
|
59
63
|
});
|
|
60
64
|
|
|
61
|
-
if (loadingState.getValue().index + 1 >= currentIndex) {
|
|
62
|
-
setReadyToBeDisplayed(true);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
65
|
return () => {
|
|
66
66
|
subscription.unsubscribe();
|
|
67
67
|
};
|
|
68
|
-
}, [
|
|
68
|
+
}, [currentIndex]);
|
|
69
69
|
|
|
70
70
|
return readyToBeDisplayed;
|
|
71
71
|
};
|
|
@@ -151,7 +151,7 @@ function RiverItemComponent(props: RiverItemType) {
|
|
|
151
151
|
component={item}
|
|
152
152
|
componentIndex={index}
|
|
153
153
|
onLoadFailed={onLoadFailed}
|
|
154
|
-
onLoadFinished={() => onLoadFinished(index)}
|
|
154
|
+
onLoadFinished={() => onLoadFinished(index)}
|
|
155
155
|
groupId={groupId}
|
|
156
156
|
feedUrl={feedUrl}
|
|
157
157
|
isLast={isLast}
|
|
@@ -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}
|
|
@@ -8,7 +8,7 @@ export const withTvEventHandler = (Component) => {
|
|
|
8
8
|
return function WithTVEventHandler(props) {
|
|
9
9
|
const navigator = useNavigation();
|
|
10
10
|
|
|
11
|
-
const remoteHandler = (
|
|
11
|
+
const remoteHandler = (event) => {
|
|
12
12
|
const { eventType } = event;
|
|
13
13
|
|
|
14
14
|
const canGoBack = navigator.canGoBack();
|
|
@@ -135,12 +135,6 @@ 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
|
-
}
|
|
144
138
|
maxToRenderPerBatch={10}
|
|
145
139
|
onContentSizeChange={[Function]}
|
|
146
140
|
onLayout={[Function]}
|
|
@@ -165,6 +159,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
165
159
|
>
|
|
166
160
|
<View>
|
|
167
161
|
<View
|
|
162
|
+
onFocusCapture={[Function]}
|
|
168
163
|
onLayout={[Function]}
|
|
169
164
|
style={null}
|
|
170
165
|
>
|
|
@@ -180,6 +175,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
180
175
|
</View>
|
|
181
176
|
</View>
|
|
182
177
|
<View
|
|
178
|
+
onFocusCapture={[Function]}
|
|
183
179
|
onLayout={[Function]}
|
|
184
180
|
style={null}
|
|
185
181
|
>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { makeListOf } from "@applicaster/zapp-react-native-utils/arrayUtils";
|
|
2
|
+
import { isFirstComponentGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
|
|
3
|
+
import { once } from "ramda";
|
|
4
|
+
|
|
5
|
+
const INITIAL_NUMBER_TO_LOAD = 3;
|
|
6
|
+
|
|
7
|
+
// Infer the values of COMPONENT_LOADING_STATE as a type
|
|
8
|
+
type ComponentLoadingState =
|
|
9
|
+
(typeof COMPONENT_LOADING_STATE)[keyof typeof COMPONENT_LOADING_STATE];
|
|
10
|
+
|
|
11
|
+
export const COMPONENT_LOADING_STATE = {
|
|
12
|
+
UNKNOWN: "UNKNOWN",
|
|
13
|
+
LOADED_WITH_SUCCESS: "LOADED_WITH_SUCCESS",
|
|
14
|
+
LOADED_WITH_FAILURE: "LOADED_WITH_FAILURE",
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
// Function to get the number of loaded components
|
|
18
|
+
const getNumberOfLoaded = (states: ComponentLoadingState[]): number => {
|
|
19
|
+
return states.filter((value) => value !== COMPONENT_LOADING_STATE.UNKNOWN)
|
|
20
|
+
.length;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getNumberOfComponentsWaitToLoadBeforePresent = (
|
|
24
|
+
componentsToRender: ZappUIComponent[]
|
|
25
|
+
): number => {
|
|
26
|
+
// when Gallery is the first component, no need to wait the others
|
|
27
|
+
if (isFirstComponentGallery(componentsToRender)) {
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return Math.min(INITIAL_NUMBER_TO_LOAD, componentsToRender.length);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export class ScreenRevealManager {
|
|
35
|
+
public numberOfComponentsWaitToLoadBeforePresent: number;
|
|
36
|
+
private renderingState: Array<ComponentLoadingState>;
|
|
37
|
+
private callback: Callback;
|
|
38
|
+
|
|
39
|
+
constructor(componentsToRender: ZappUIComponent[], callback: Callback) {
|
|
40
|
+
this.numberOfComponentsWaitToLoadBeforePresent =
|
|
41
|
+
getNumberOfComponentsWaitToLoadBeforePresent(componentsToRender);
|
|
42
|
+
|
|
43
|
+
this.renderingState = makeListOf<ComponentLoadingState>(
|
|
44
|
+
COMPONENT_LOADING_STATE.UNKNOWN,
|
|
45
|
+
this.numberOfComponentsWaitToLoadBeforePresent
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
this.callback = once(callback);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onLoadFinished = (index: number): void => {
|
|
52
|
+
this.renderingState[index] = COMPONENT_LOADING_STATE.LOADED_WITH_SUCCESS;
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
getNumberOfLoaded(this.renderingState) >=
|
|
56
|
+
this.numberOfComponentsWaitToLoadBeforePresent
|
|
57
|
+
) {
|
|
58
|
+
this.setIsReadyToShow();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
onLoadFailed = (index: number): void => {
|
|
63
|
+
this.renderingState[index] = COMPONENT_LOADING_STATE.LOADED_WITH_FAILURE;
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
getNumberOfLoaded(this.renderingState) >=
|
|
67
|
+
this.numberOfComponentsWaitToLoadBeforePresent
|
|
68
|
+
) {
|
|
69
|
+
this.setIsReadyToShow();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
setIsReadyToShow = (): void => {
|
|
74
|
+
this.callback();
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ScreenRevealManager,
|
|
3
|
+
COMPONENT_LOADING_STATE,
|
|
4
|
+
} from "../ScreenRevealManager";
|
|
5
|
+
|
|
6
|
+
describe("ScreenRevealManager", () => {
|
|
7
|
+
const mockCallback = jest.fn();
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should initialize with the correct number of components to wait for", () => {
|
|
14
|
+
const componentsToRender: ZappUIComponent[] = [
|
|
15
|
+
{ component_type: "component1" },
|
|
16
|
+
{ component_type: "component2" },
|
|
17
|
+
{ component_type: "component3" },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const manager = new ScreenRevealManager(componentsToRender, mockCallback);
|
|
21
|
+
|
|
22
|
+
expect(manager["numberOfComponentsWaitToLoadBeforePresent"]).toBe(3);
|
|
23
|
+
|
|
24
|
+
expect(manager["renderingState"]).toEqual([
|
|
25
|
+
COMPONENT_LOADING_STATE.UNKNOWN,
|
|
26
|
+
COMPONENT_LOADING_STATE.UNKNOWN,
|
|
27
|
+
COMPONENT_LOADING_STATE.UNKNOWN,
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should call the callback when the required number of components are loaded successfully", () => {
|
|
32
|
+
const componentsToRender: ZappUIComponent[] = [
|
|
33
|
+
{ component_type: "component1" },
|
|
34
|
+
{ component_type: "component2" },
|
|
35
|
+
{ component_type: "component3" },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const manager = new ScreenRevealManager(componentsToRender, mockCallback);
|
|
39
|
+
|
|
40
|
+
manager.onLoadFinished(0);
|
|
41
|
+
manager.onLoadFinished(1);
|
|
42
|
+
manager.onLoadFinished(2);
|
|
43
|
+
|
|
44
|
+
expect(mockCallback).toHaveBeenCalledTimes(1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should call the callback when the required number of components fail to load", () => {
|
|
48
|
+
const componentsToRender: ZappUIComponent[] = [
|
|
49
|
+
{ component_type: "component1" },
|
|
50
|
+
{ component_type: "component2" },
|
|
51
|
+
{ component_type: "component3" },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const manager = new ScreenRevealManager(componentsToRender, mockCallback);
|
|
55
|
+
|
|
56
|
+
manager.onLoadFailed(0);
|
|
57
|
+
manager.onLoadFailed(1);
|
|
58
|
+
manager.onLoadFailed(2);
|
|
59
|
+
|
|
60
|
+
expect(mockCallback).toHaveBeenCalledTimes(1);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should call the callback when a mix of successful and failed loads meet the required number", () => {
|
|
64
|
+
const componentsToRender: ZappUIComponent[] = [
|
|
65
|
+
{ component_type: "component1" },
|
|
66
|
+
{ component_type: "component2" },
|
|
67
|
+
{ component_type: "component3" },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const manager = new ScreenRevealManager(componentsToRender, mockCallback);
|
|
71
|
+
|
|
72
|
+
manager.onLoadFinished(0);
|
|
73
|
+
manager.onLoadFailed(1);
|
|
74
|
+
manager.onLoadFinished(2);
|
|
75
|
+
|
|
76
|
+
expect(mockCallback).toHaveBeenCalledTimes(1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should not call the callback if the required number of components are not loaded", () => {
|
|
80
|
+
const componentsToRender: ZappUIComponent[] = [
|
|
81
|
+
{ component_type: "component1" },
|
|
82
|
+
{ component_type: "component2" },
|
|
83
|
+
{ component_type: "component3" },
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const manager = new ScreenRevealManager(componentsToRender, mockCallback);
|
|
87
|
+
|
|
88
|
+
manager.onLoadFinished(0);
|
|
89
|
+
manager.onLoadFailed(1);
|
|
90
|
+
|
|
91
|
+
expect(mockCallback).not.toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should call the callback when the when first component is gallery and it was loaded successfully", () => {
|
|
95
|
+
const componentsToRender: ZappUIComponent[] = [
|
|
96
|
+
{ component_type: "gallery-qb" },
|
|
97
|
+
{ component_type: "component2" },
|
|
98
|
+
{ component_type: "component3" },
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
const manager = new ScreenRevealManager(componentsToRender, mockCallback);
|
|
102
|
+
|
|
103
|
+
manager.onLoadFinished(0);
|
|
104
|
+
|
|
105
|
+
expect(mockCallback).toHaveBeenCalledTimes(1);
|
|
106
|
+
});
|
|
107
|
+
});
|