@applicaster/zapp-react-native-ui-components 14.0.0-alpha.1661204539 → 14.0.0-alpha.2332850672
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/Artwork.tsx +17 -12
- package/Components/AudioPlayer/AudioPlayer.tsx +48 -11
- package/Components/AudioPlayer/AudioPlayerMobileLayout.tsx +61 -0
- package/Components/AudioPlayer/{AudioPlayerLayout.tsx → AudioPlayerTVLayout.tsx} +31 -72
- package/Components/AudioPlayer/Runtime.tsx +2 -1
- package/Components/AudioPlayer/Summary.tsx +12 -9
- package/Components/AudioPlayer/Title.tsx +12 -9
- package/Components/AudioPlayer/__tests__/__snapshots__/Runtime.test.js.snap +2 -2
- package/Components/AudioPlayer/__tests__/__snapshots__/artWork.test.js.snap +9 -4
- package/Components/AudioPlayer/__tests__/__snapshots__/{audioPlayerLayout.test.js.snap → audioPlayerMobileLayout.test.js.snap} +2 -2
- package/Components/AudioPlayer/__tests__/__snapshots__/summary.test.js.snap +1 -1
- package/Components/AudioPlayer/__tests__/__snapshots__/title.test.js.snap +1 -1
- package/Components/AudioPlayer/__tests__/audioPlayerMobileLayout.test.js +18 -0
- package/Components/AudioPlayer/helpers.tsx +2 -2
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +0 -2
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +1 -0
- package/Components/MasterCell/utils/behaviorProvider.ts +12 -67
- package/Components/MasterCell/utils/index.ts +3 -13
- package/Components/PlayerContainer/PlayerContainer.tsx +24 -38
- package/Components/River/ComponentsMap/ComponentsMap.tsx +1 -5
- package/Components/River/RiverItem.tsx +8 -8
- package/Components/River/TV/River.tsx +0 -3
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +0 -6
- package/Components/TopMarginApplicator/TopMarginApplicator.tsx +16 -15
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +60 -0
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +17 -55
- package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +15 -26
- 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 -5
- package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
|
@@ -1,54 +1,28 @@
|
|
|
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-bridge/ZappStorage/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-bridge/ZappStorage/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
|
-
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
10
|
|
|
14
|
-
const parseContextKey = (
|
|
15
|
-
key
|
|
16
|
-
context: string = "ctx"
|
|
17
|
-
): string | null => {
|
|
18
|
-
if (!key?.startsWith(`@{${context}/`)) return null;
|
|
11
|
+
const parseContextKey = (key: string): string | null => {
|
|
12
|
+
if (!key?.startsWith("@{ctx/")) return null;
|
|
19
13
|
|
|
20
|
-
return key.substring(
|
|
14
|
+
return key.substring("@{ctx/".length, key.length - 1);
|
|
21
15
|
};
|
|
22
16
|
|
|
23
17
|
const getDataSourceProvider = (
|
|
24
|
-
behavior: Behavior
|
|
25
|
-
screenRoute: string
|
|
18
|
+
behavior: Behavior
|
|
26
19
|
): BehaviorSubject<string[] | string> | null => {
|
|
27
20
|
if (!behavior) return null;
|
|
28
21
|
|
|
29
22
|
const selection = String(behavior.current_selection);
|
|
30
|
-
const screenKey = parseContextKey(selection, "screen");
|
|
31
|
-
|
|
32
|
-
if (screenKey) {
|
|
33
|
-
if (behavior.select_mode === "multi") {
|
|
34
|
-
return ScreenMultiSelectProvider.getProvider(
|
|
35
|
-
screenKey,
|
|
36
|
-
screenRoute
|
|
37
|
-
).getObservable();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (behavior.select_mode === "single") {
|
|
41
|
-
return ScreenSingleValueProvider.getProvider(
|
|
42
|
-
screenKey,
|
|
43
|
-
screenRoute
|
|
44
|
-
).getObservable();
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
23
|
const contextKey = parseContextKey(selection);
|
|
49
24
|
|
|
50
25
|
if (contextKey) {
|
|
51
|
-
// TODO: Add storage scope to behavior
|
|
52
26
|
if (behavior.select_mode === "multi") {
|
|
53
27
|
return StorageMultiSelectProvider.getProvider(contextKey).getObservable();
|
|
54
28
|
}
|
|
@@ -67,7 +41,6 @@ const getDataSourceProvider = (
|
|
|
67
41
|
|
|
68
42
|
export const useBehaviorUpdate = (behavior: Behavior) => {
|
|
69
43
|
const [lastUpdate, setLastUpdate] = React.useState<number | null>(null);
|
|
70
|
-
const screenRoute = useRoute()?.pathname || "";
|
|
71
44
|
const player = usePlayer();
|
|
72
45
|
|
|
73
46
|
const triggerUpdate = () => setLastUpdate(Date.now());
|
|
@@ -75,7 +48,7 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
|
|
|
75
48
|
useEffect(() => {
|
|
76
49
|
if (!behavior) return;
|
|
77
50
|
|
|
78
|
-
const dataSource = getDataSourceProvider(behavior
|
|
51
|
+
const dataSource = getDataSourceProvider(behavior);
|
|
79
52
|
|
|
80
53
|
if (dataSource) {
|
|
81
54
|
const subscription = dataSource.subscribe(triggerUpdate);
|
|
@@ -99,15 +72,10 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
|
|
|
99
72
|
|
|
100
73
|
// We cant use async in this function (its inside render),
|
|
101
74
|
// so we rely on useBehaviorUpdate to update current value and trigger re-render
|
|
102
|
-
export const isCellSelected = (
|
|
103
|
-
item,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}: {
|
|
107
|
-
item: ZappEntry;
|
|
108
|
-
screenRoute: string;
|
|
109
|
-
behavior?: Behavior;
|
|
110
|
-
}): boolean => {
|
|
75
|
+
export const isCellSelected = (
|
|
76
|
+
item: ZappEntry,
|
|
77
|
+
behavior?: Behavior
|
|
78
|
+
): boolean => {
|
|
111
79
|
if (!behavior) return false;
|
|
112
80
|
|
|
113
81
|
const id = behavior.selector ? get(item, behavior.selector) : item.id;
|
|
@@ -131,30 +99,7 @@ export const isCellSelected = ({
|
|
|
131
99
|
}
|
|
132
100
|
|
|
133
101
|
const selection = String(behavior.current_selection);
|
|
134
|
-
|
|
135
|
-
const screenKey = parseContextKey(selection, "screen");
|
|
136
|
-
|
|
137
|
-
if (screenKey) {
|
|
138
|
-
if (behavior.select_mode === "single") {
|
|
139
|
-
const selectedItem = ScreenSingleValueProvider.getProvider(
|
|
140
|
-
screenKey,
|
|
141
|
-
screenRoute
|
|
142
|
-
).getValue();
|
|
143
|
-
|
|
144
|
-
return selectedItem === String(id);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (behavior.select_mode === "multi") {
|
|
148
|
-
const selectedItems = ScreenMultiSelectProvider.getProvider(
|
|
149
|
-
screenKey,
|
|
150
|
-
screenRoute
|
|
151
|
-
).getSelectedItems();
|
|
152
|
-
|
|
153
|
-
return selectedItems?.includes(String(id));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const contextKey = parseContextKey(selection, "ctx");
|
|
102
|
+
const contextKey = parseContextKey(selection);
|
|
158
103
|
|
|
159
104
|
if (contextKey) {
|
|
160
105
|
if (behavior.select_mode === "single") {
|
|
@@ -8,7 +8,6 @@ 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
11
|
|
|
13
12
|
const hasElementSpecificViewType = (viewType) => (element) => {
|
|
14
13
|
if (R.isNil(element)) {
|
|
@@ -191,16 +190,8 @@ export const getFocusedButtonId = (focusable) => {
|
|
|
191
190
|
});
|
|
192
191
|
};
|
|
193
192
|
|
|
194
|
-
export const isSelected = ({
|
|
195
|
-
item,
|
|
196
|
-
screenRoute,
|
|
197
|
-
behavior,
|
|
198
|
-
}: {
|
|
199
|
-
item: ZappEntry;
|
|
200
|
-
screenRoute: string;
|
|
201
|
-
behavior?: Behavior;
|
|
202
|
-
}) => {
|
|
203
|
-
return isCellSelected({ item, screenRoute, behavior });
|
|
193
|
+
export const isSelected = (item: ZappEntry, behavior?: Behavior) => {
|
|
194
|
+
return isCellSelected(item, behavior);
|
|
204
195
|
};
|
|
205
196
|
|
|
206
197
|
export const useCellState = ({
|
|
@@ -213,10 +204,9 @@ export const useCellState = ({
|
|
|
213
204
|
focused: boolean;
|
|
214
205
|
}): CellState => {
|
|
215
206
|
const lastUpdate = useBehaviorUpdate(behavior);
|
|
216
|
-
const router = useRoute();
|
|
217
207
|
|
|
218
208
|
const _isSelected = useMemo(
|
|
219
|
-
() => isSelected(
|
|
209
|
+
() => isSelected(item, behavior),
|
|
220
210
|
[behavior, item, lastUpdate]
|
|
221
211
|
);
|
|
222
212
|
|
|
@@ -6,12 +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,
|
|
12
11
|
isAndroidTVPlatform,
|
|
12
|
+
isTvOSPlatform,
|
|
13
13
|
} from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
isAudioItem,
|
|
16
|
+
isInlineTV as isInlineTVUtil,
|
|
17
|
+
} from "@applicaster/zapp-react-native-utils/playerUtils";
|
|
15
18
|
|
|
16
19
|
import { TVEventHandlerComponent } from "@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent";
|
|
17
20
|
import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
|
|
@@ -22,7 +25,7 @@ import {
|
|
|
22
25
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
23
26
|
|
|
24
27
|
import { PlayerStateContext } from "../../Contexts/PlayerStateContext";
|
|
25
|
-
|
|
28
|
+
|
|
26
29
|
import {
|
|
27
30
|
QUICK_BRICK_EVENTS,
|
|
28
31
|
QuickBrickEvent,
|
|
@@ -97,6 +100,11 @@ const focusableBottomContainerId = "player-container-bottom";
|
|
|
97
100
|
|
|
98
101
|
const isAndroidTV = isAndroidTVPlatform();
|
|
99
102
|
|
|
103
|
+
const isTvOS = isTvOSPlatform();
|
|
104
|
+
|
|
105
|
+
// height for container with additional content below player
|
|
106
|
+
const INLINE_CONTAINER_CONTENT_HEIGHT = 400;
|
|
107
|
+
|
|
100
108
|
const withBorderHack = () => {
|
|
101
109
|
if (isAndroidTV) {
|
|
102
110
|
/* @HACK: see GH#7269 */
|
|
@@ -126,7 +134,10 @@ const webStyles = {
|
|
|
126
134
|
flex: "initial",
|
|
127
135
|
},
|
|
128
136
|
inlineRiver: {
|
|
129
|
-
height:
|
|
137
|
+
height: INLINE_CONTAINER_CONTENT_HEIGHT,
|
|
138
|
+
|
|
139
|
+
borderWidth: 4,
|
|
140
|
+
borderColor: "yellow",
|
|
130
141
|
},
|
|
131
142
|
};
|
|
132
143
|
|
|
@@ -144,7 +155,7 @@ const nativeStyles = {
|
|
|
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,
|
|
@@ -337,7 +334,7 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
337
334
|
|
|
338
335
|
setState({ error: errorObj });
|
|
339
336
|
|
|
340
|
-
if (!
|
|
337
|
+
if (!isTvOS) {
|
|
341
338
|
setTimeout(() => {
|
|
342
339
|
close();
|
|
343
340
|
}, 800);
|
|
@@ -568,10 +565,7 @@ 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
571
|
[VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(mode) ||
|
|
@@ -626,12 +620,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
626
620
|
playNextData,
|
|
627
621
|
};
|
|
628
622
|
|
|
629
|
-
const restPlayerProps = {
|
|
630
|
-
item,
|
|
631
|
-
showAudioPlayer: isAudioContent,
|
|
632
|
-
pluginConfiguration,
|
|
633
|
-
};
|
|
634
|
-
|
|
635
623
|
return (
|
|
636
624
|
<PlayerStateContext.Provider value={value}>
|
|
637
625
|
<PlayerContainerContextProvider
|
|
@@ -707,8 +695,6 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
707
695
|
</Player>
|
|
708
696
|
</PlayerFocusableWrapperView>
|
|
709
697
|
|
|
710
|
-
{renderRestPlayers(restPlayerProps)}
|
|
711
|
-
|
|
712
698
|
{state.error ? <ErrorDisplay error={state.error} /> : null}
|
|
713
699
|
</View>
|
|
714
700
|
{/* Components container */}
|
|
@@ -732,12 +718,12 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
732
718
|
screenId={screenData.id}
|
|
733
719
|
key={item.id}
|
|
734
720
|
groupId={FocusableGroupMainContainerId}
|
|
735
|
-
cellTapAction={
|
|
721
|
+
cellTapAction={onCellTap}
|
|
736
722
|
extraAnchorPointYOffset={-600}
|
|
737
723
|
isScreenWrappedInContainer={true}
|
|
738
724
|
containerHeight={styles.inlineRiver.height}
|
|
739
725
|
componentsMapExtraProps={{
|
|
740
|
-
isNestedComponentsMap:
|
|
726
|
+
isNestedComponentsMap: true,
|
|
741
727
|
}}
|
|
742
728
|
/>
|
|
743
729
|
)}
|
|
@@ -281,8 +281,8 @@ 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}
|
|
@@ -303,10 +303,6 @@ function ComponentsMapComponent(props: Props) {
|
|
|
303
303
|
onMomentumScrollEnd={_onMomentumScrollEnd}
|
|
304
304
|
onScrollEndDrag={_onScrollEndDrag}
|
|
305
305
|
scrollEventThrottle={16}
|
|
306
|
-
maintainVisibleContentPosition={{
|
|
307
|
-
minIndexForVisible: 0,
|
|
308
|
-
autoscrollToTopThreshold: 10,
|
|
309
|
-
}}
|
|
310
306
|
{...scrollViewExtraProps}
|
|
311
307
|
/>
|
|
312
308
|
</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}
|
|
@@ -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]}
|
|
@@ -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
|
};
|
|
@@ -71,6 +71,36 @@ exports[`PlayerWrapper renders inline 1`] = `
|
|
|
71
71
|
}
|
|
72
72
|
testID="test-player-container"
|
|
73
73
|
/>
|
|
74
|
+
<View
|
|
75
|
+
animationType="componentFade"
|
|
76
|
+
style={
|
|
77
|
+
{
|
|
78
|
+
"flex": 1,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
>
|
|
82
|
+
<View
|
|
83
|
+
configuration={
|
|
84
|
+
{
|
|
85
|
+
"tablet_landscape_player_container_background_color": "red",
|
|
86
|
+
"tablet_landscape_sidebar_width": "35%",
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
entry={
|
|
90
|
+
{
|
|
91
|
+
"id": "test",
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
isTablet={false}
|
|
95
|
+
isTabletLandscape={false}
|
|
96
|
+
style={
|
|
97
|
+
{
|
|
98
|
+
"flex": 1,
|
|
99
|
+
"paddingTop": 20,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/>
|
|
103
|
+
</View>
|
|
74
104
|
</View>
|
|
75
105
|
</RNCSafeAreaView>
|
|
76
106
|
</RNCSafeAreaProvider>
|
|
@@ -239,6 +269,36 @@ exports[`PlayerWrapper renders inline on tablet in landscape orientation 1`] = `
|
|
|
239
269
|
}
|
|
240
270
|
/>
|
|
241
271
|
</View>
|
|
272
|
+
<View
|
|
273
|
+
animationType="componentFade"
|
|
274
|
+
style={
|
|
275
|
+
{
|
|
276
|
+
"flex": 1,
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
>
|
|
280
|
+
<View
|
|
281
|
+
configuration={
|
|
282
|
+
{
|
|
283
|
+
"tablet_landscape_player_container_background_color": "red",
|
|
284
|
+
"tablet_landscape_sidebar_width": "35%",
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
entry={
|
|
288
|
+
{
|
|
289
|
+
"id": "test",
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
isTablet={true}
|
|
293
|
+
isTabletLandscape={true}
|
|
294
|
+
style={
|
|
295
|
+
{
|
|
296
|
+
"flex": 1,
|
|
297
|
+
"paddingTop": 20,
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/>
|
|
301
|
+
</View>
|
|
242
302
|
</View>
|
|
243
303
|
</RNCSafeAreaView>
|
|
244
304
|
</RNCSafeAreaProvider>
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
import { renderHook } from "@testing-library/react-hooks";
|
|
2
2
|
import { useDelayedPlayerDetails } from "../useDelayedPlayerDetails";
|
|
3
|
-
import { withTimeout$ } from "@applicaster/zapp-react-native-utils/idleUtils";
|
|
4
|
-
import { showDetails } from "../utils";
|
|
5
3
|
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
6
4
|
|
|
7
|
-
jest.mock("@applicaster/zapp-react-native-utils/idleUtils", () => ({
|
|
8
|
-
withTimeout$: jest.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
jest.mock("../utils", () => ({
|
|
12
|
-
showDetails: jest.fn(),
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
5
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
16
|
-
useIsTablet: jest.fn(),
|
|
6
|
+
useIsTablet: jest.fn().mockReturnValue(false),
|
|
17
7
|
}));
|
|
18
8
|
|
|
19
9
|
describe("useDelayedPlayerDetails", () => {
|
|
@@ -21,69 +11,41 @@ describe("useDelayedPlayerDetails", () => {
|
|
|
21
11
|
jest.clearAllMocks();
|
|
22
12
|
});
|
|
23
13
|
|
|
24
|
-
it("should return
|
|
25
|
-
(withTimeout$ as jest.Mock).mockReturnValue({
|
|
26
|
-
subscribe: jest.fn().mockReturnValue({ unsubscribe: jest.fn() }),
|
|
27
|
-
});
|
|
28
|
-
|
|
14
|
+
it("should return true initially", () => {
|
|
29
15
|
const { result } = renderHook(() =>
|
|
30
16
|
useDelayedPlayerDetails({ isInline: true, isDocked: false, isPip: false })
|
|
31
17
|
);
|
|
32
18
|
|
|
33
|
-
expect(result.current).toBe(
|
|
19
|
+
expect(result.current).toBe(true);
|
|
34
20
|
});
|
|
35
21
|
|
|
36
|
-
it("should return
|
|
37
|
-
(withTimeout$ as jest.Mock).mockReturnValue({
|
|
38
|
-
subscribe: (callback) => {
|
|
39
|
-
callback.next();
|
|
40
|
-
|
|
41
|
-
return { unsubscribe: jest.fn() };
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
(showDetails as jest.Mock).mockReturnValue(true);
|
|
46
|
-
(useIsTablet as jest.Mock).mockReturnValue(false);
|
|
47
|
-
|
|
22
|
+
it("should return false when isPip is true", () => {
|
|
48
23
|
const { result } = renderHook(() =>
|
|
49
|
-
useDelayedPlayerDetails({ isInline: true, isDocked:
|
|
24
|
+
useDelayedPlayerDetails({ isInline: true, isDocked: true, isPip: true })
|
|
50
25
|
);
|
|
51
26
|
|
|
52
|
-
expect(result.current).toBe(
|
|
27
|
+
expect(result.current).toBe(false);
|
|
53
28
|
});
|
|
54
29
|
|
|
55
|
-
it("should return false
|
|
56
|
-
(withTimeout$ as jest.Mock).mockReturnValue({
|
|
57
|
-
subscribe: (callback) => {
|
|
58
|
-
callback.next();
|
|
59
|
-
|
|
60
|
-
return { unsubscribe: jest.fn() };
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
(showDetails as jest.Mock).mockReturnValue(false);
|
|
65
|
-
(useIsTablet as jest.Mock).mockReturnValue(false);
|
|
66
|
-
|
|
30
|
+
it("should return false when isDocked is true", () => {
|
|
67
31
|
const { result } = renderHook(() =>
|
|
68
|
-
useDelayedPlayerDetails({ isInline: true, isDocked:
|
|
32
|
+
useDelayedPlayerDetails({ isInline: true, isDocked: true, isPip: false })
|
|
69
33
|
);
|
|
70
34
|
|
|
71
35
|
expect(result.current).toBe(false);
|
|
72
36
|
});
|
|
73
37
|
|
|
74
|
-
it("should
|
|
75
|
-
|
|
38
|
+
it("should return true for tablet regardless of other flags", () => {
|
|
39
|
+
(useIsTablet as jest.Mock).mockReturnValue(true);
|
|
76
40
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
41
|
+
const { result } = renderHook(() =>
|
|
42
|
+
useDelayedPlayerDetails({
|
|
43
|
+
isInline: false,
|
|
44
|
+
isDocked: false,
|
|
45
|
+
isPip: false,
|
|
46
|
+
})
|
|
83
47
|
);
|
|
84
48
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
expect(unsubscribeMock).toHaveBeenCalled();
|
|
49
|
+
expect(result.current).toBe(true);
|
|
88
50
|
});
|
|
89
51
|
});
|