@applicaster/zapp-react-native-ui-components 16.0.0-alpha.1740103407 → 16.0.0-alpha.1803625751
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/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +39 -0
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
- package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +124 -0
- package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
- package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
- package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
- package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +72 -0
- package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
- package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
- package/Components/GeneralContentScreen/index.ts +2 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
- package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +4 -3
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +3 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +38 -2
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +30 -16
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +2 -1
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
- package/Components/River/ComponentsMap/ComponentsMap.tsx +103 -186
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +22 -1
- package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
- package/Helpers/index.js +0 -39
- package/package.json +5 -5
- package/Components/Layout/FullWidthRow.tsx +0 -38
- package/Helpers/Analytics/index.js +0 -95
|
@@ -3,18 +3,20 @@ import { insertButtonsBetweenLabels } from "..";
|
|
|
3
3
|
describe("insertButtonsBetweenLabels - depthlevel 1", () => {
|
|
4
4
|
const buttons = "buttons";
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
{ name: "label_1" },
|
|
8
|
-
{ name: "label_2" },
|
|
9
|
-
{ name: "label_3" },
|
|
6
|
+
const slots = [
|
|
7
|
+
{ name: "label_1", element: { id: "label_1" } },
|
|
8
|
+
{ name: "label_2", element: { id: "label_2" } },
|
|
9
|
+
{ name: "label_3", element: { id: "label_3" } },
|
|
10
10
|
];
|
|
11
11
|
|
|
12
|
+
const labels = slots.map((slot) => slot.element);
|
|
13
|
+
|
|
12
14
|
it("put buttons on top", () => {
|
|
13
15
|
const configuration = {
|
|
14
16
|
tv_buttons_container_position: "on_top",
|
|
15
17
|
};
|
|
16
18
|
|
|
17
|
-
const result = insertButtonsBetweenLabels(configuration, buttons,
|
|
19
|
+
const result = insertButtonsBetweenLabels(configuration, buttons, slots);
|
|
18
20
|
|
|
19
21
|
expect(result).toEqual([buttons, ...labels]);
|
|
20
22
|
});
|
|
@@ -24,7 +26,7 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
|
|
|
24
26
|
tv_buttons_container_position: "unknown_position",
|
|
25
27
|
};
|
|
26
28
|
|
|
27
|
-
const result = insertButtonsBetweenLabels(configuration, buttons,
|
|
29
|
+
const result = insertButtonsBetweenLabels(configuration, buttons, slots);
|
|
28
30
|
|
|
29
31
|
expect(result).toEqual([...labels, buttons]);
|
|
30
32
|
});
|
|
@@ -34,7 +36,7 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
|
|
|
34
36
|
tv_buttons_container_position: "label_1",
|
|
35
37
|
};
|
|
36
38
|
|
|
37
|
-
const result = insertButtonsBetweenLabels(configuration, buttons,
|
|
39
|
+
const result = insertButtonsBetweenLabels(configuration, buttons, slots);
|
|
38
40
|
|
|
39
41
|
expect(result).toEqual([labels[0], buttons, labels[1], labels[2]]);
|
|
40
42
|
});
|
|
@@ -44,7 +46,7 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
|
|
|
44
46
|
tv_buttons_container_position: "label_2",
|
|
45
47
|
};
|
|
46
48
|
|
|
47
|
-
const result = insertButtonsBetweenLabels(configuration, buttons,
|
|
49
|
+
const result = insertButtonsBetweenLabels(configuration, buttons, slots);
|
|
48
50
|
|
|
49
51
|
expect(result).toEqual([labels[0], labels[1], buttons, labels[2]]);
|
|
50
52
|
});
|
|
@@ -54,22 +56,52 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
|
|
|
54
56
|
tv_buttons_container_position: "label_3",
|
|
55
57
|
};
|
|
56
58
|
|
|
57
|
-
const result = insertButtonsBetweenLabels(configuration, buttons,
|
|
59
|
+
const result = insertButtonsBetweenLabels(configuration, buttons, slots);
|
|
58
60
|
|
|
59
61
|
expect(result).toEqual([labels[0], labels[1], labels[2], buttons]);
|
|
60
62
|
});
|
|
61
63
|
|
|
62
|
-
it("put
|
|
64
|
+
it("put above label_2", () => {
|
|
65
|
+
const configuration = {
|
|
66
|
+
tv_buttons_container_position: "above_label_2",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const result = insertButtonsBetweenLabels(configuration, buttons, slots);
|
|
70
|
+
|
|
71
|
+
expect(result).toEqual([labels[0], buttons, labels[1], labels[2]]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("keeps the slot when its label is empty (null element)", () => {
|
|
75
|
+
const slotsWithEmpty = [
|
|
76
|
+
{ name: "label_1", element: { id: "label_1" } },
|
|
77
|
+
{ name: "label_2", element: null },
|
|
78
|
+
{ name: "label_3", element: { id: "label_3" } },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
const configuration = {
|
|
82
|
+
tv_buttons_container_position: "above_label_2",
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const result = insertButtonsBetweenLabels(
|
|
86
|
+
configuration,
|
|
87
|
+
buttons,
|
|
88
|
+
slotsWithEmpty
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
expect(result).toEqual([{ id: "label_1" }, buttons, { id: "label_3" }]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("appends buttons when the target is unknown", () => {
|
|
63
95
|
const configuration = {
|
|
64
96
|
tv_buttons_container_position: "label_X",
|
|
65
97
|
};
|
|
66
98
|
|
|
67
|
-
const result = insertButtonsBetweenLabels(configuration, buttons,
|
|
99
|
+
const result = insertButtonsBetweenLabels(configuration, buttons, slots);
|
|
68
100
|
|
|
69
101
|
expect(result).toEqual([labels[0], labels[1], labels[2], buttons]);
|
|
70
102
|
});
|
|
71
103
|
|
|
72
|
-
it("
|
|
104
|
+
it("appends buttons when there are no slots", () => {
|
|
73
105
|
const configuration = {
|
|
74
106
|
tv_buttons_container_position: "label_X",
|
|
75
107
|
};
|
|
@@ -88,7 +120,7 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
|
|
|
88
120
|
tv_buttons_container_position: "label_X",
|
|
89
121
|
};
|
|
90
122
|
|
|
91
|
-
const result = insertButtonsBetweenLabels(configuration, null,
|
|
123
|
+
const result = insertButtonsBetweenLabels(configuration, null, slots);
|
|
92
124
|
|
|
93
125
|
expect(result).toEqual(labels);
|
|
94
126
|
});
|
|
@@ -34,7 +34,7 @@ export const memoizedGetPluginIdentifier = memoizee(getPluginIdentifier);
|
|
|
34
34
|
export const insertButtonsBetweenLabels = (
|
|
35
35
|
configuration: Record<string, unknown>,
|
|
36
36
|
buttons,
|
|
37
|
-
|
|
37
|
+
slots = [] // { name, element } per canonical text label slot
|
|
38
38
|
) =>
|
|
39
39
|
insertBetweenLabels(
|
|
40
40
|
{
|
|
@@ -45,7 +45,7 @@ export const insertButtonsBetweenLabels = (
|
|
|
45
45
|
appendWhenMissing: true,
|
|
46
46
|
},
|
|
47
47
|
buttons,
|
|
48
|
-
|
|
48
|
+
slots
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
export const insertButtonsBetweenLabelContainers = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
-
import { FlatList,
|
|
3
|
+
import { FlatList, StyleSheet, View } 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";
|
|
@@ -36,16 +36,18 @@ type Props = {
|
|
|
36
36
|
groupId?: string;
|
|
37
37
|
isScreenWrappedInContainer?: boolean;
|
|
38
38
|
riverComponents: ZappUIComponent[];
|
|
39
|
-
scrollViewExtraProps?:
|
|
39
|
+
scrollViewExtraProps?: {};
|
|
40
40
|
riverId?: string;
|
|
41
|
-
getStaticComponentFeed:
|
|
42
|
-
component: ZappUIComponent;
|
|
43
|
-
index: number;
|
|
44
|
-
}) => ZappFeed | Promise<ZappFeed>;
|
|
41
|
+
getStaticComponentFeed: any;
|
|
45
42
|
stickyHeaderIndices?: number[];
|
|
46
|
-
useScrollView?: boolean;
|
|
47
43
|
};
|
|
48
44
|
|
|
45
|
+
const styles = StyleSheet.create({
|
|
46
|
+
container: {
|
|
47
|
+
flex: 1,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
49
51
|
const getNearestValue = (value, optionA, optionB) =>
|
|
50
52
|
Math.abs(value - optionA) < Math.abs(value - optionB) ? optionA : optionB;
|
|
51
53
|
|
|
@@ -62,14 +64,11 @@ function ComponentsMapComponent(props: Props) {
|
|
|
62
64
|
riverId,
|
|
63
65
|
getStaticComponentFeed,
|
|
64
66
|
stickyHeaderIndices,
|
|
65
|
-
useScrollView,
|
|
66
67
|
} = props;
|
|
67
68
|
|
|
68
69
|
const flatListRef = React.useRef<FlatList | null>(null);
|
|
69
|
-
const scrollViewRef = React.useRef<ScrollView | null>(null);
|
|
70
70
|
const flatListWrapperRef = React.useRef<View | null>(null);
|
|
71
71
|
const hasUserScrolledRef = React.useRef(false);
|
|
72
|
-
const scrollEndReachedRef = React.useRef(false);
|
|
73
72
|
const screenConfig = useScreenConfiguration(riverId);
|
|
74
73
|
const screenData = useScreenData(riverId);
|
|
75
74
|
const pullToRefreshEnabled = screenData?.rules?.pull_to_refresh_enabled;
|
|
@@ -88,7 +87,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
88
87
|
|
|
89
88
|
const onLoadDone = React.useCallback(() => {
|
|
90
89
|
logTimestamp(riverId?.toString());
|
|
91
|
-
}, [logTimestamp
|
|
90
|
+
}, [logTimestamp]);
|
|
92
91
|
|
|
93
92
|
const { loadingState, onLoadFinished, onLoadFailed, shouldShowLoadingError } =
|
|
94
93
|
useLoadingState(riverComponents.length, onLoadDone);
|
|
@@ -117,17 +116,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
117
116
|
</ScreenLoadingMeasurementsListItemWrapper>
|
|
118
117
|
);
|
|
119
118
|
},
|
|
120
|
-
[
|
|
121
|
-
feed,
|
|
122
|
-
getStaticComponentFeed,
|
|
123
|
-
groupId,
|
|
124
|
-
isScreenWrappedInContainer,
|
|
125
|
-
loadingState,
|
|
126
|
-
onLoadFailed,
|
|
127
|
-
onLoadFinished,
|
|
128
|
-
riverComponents.length,
|
|
129
|
-
riverId,
|
|
130
|
-
]
|
|
119
|
+
[feed, getStaticComponentFeed, onLoadFailed, onLoadFinished]
|
|
131
120
|
);
|
|
132
121
|
|
|
133
122
|
const screenStyle = React.useMemo(
|
|
@@ -149,13 +138,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
149
138
|
R.prop("screen_padding_right")(theme)
|
|
150
139
|
),
|
|
151
140
|
}),
|
|
152
|
-
[
|
|
153
|
-
screenConfig.paddingTop,
|
|
154
|
-
screenConfig.paddingBottom,
|
|
155
|
-
screenConfig.paddingLeft,
|
|
156
|
-
screenConfig.paddingRight,
|
|
157
|
-
theme,
|
|
158
|
-
]
|
|
141
|
+
[riverId]
|
|
159
142
|
);
|
|
160
143
|
|
|
161
144
|
const handleOnLayout = React.useCallback(
|
|
@@ -190,50 +173,55 @@ function ComponentsMapComponent(props: Props) {
|
|
|
190
173
|
canMomentum.current = true;
|
|
191
174
|
}, []);
|
|
192
175
|
|
|
193
|
-
/*
|
|
194
|
-
const
|
|
195
|
-
(
|
|
196
|
-
|
|
176
|
+
/* onMomentumScrollEnd: Handling snap to expanded/collapsed state ( for navbar ) */
|
|
177
|
+
const _onMomentumScrollEnd = React.useCallback(({ nativeEvent }) => {
|
|
178
|
+
if (!canMomentum.current) return;
|
|
179
|
+
canMomentum.current = false;
|
|
197
180
|
|
|
198
|
-
|
|
199
|
-
if (scrollState === 0 || scrollState === -headerHeight) return;
|
|
200
|
-
if (!flatListRef.current) return;
|
|
181
|
+
const { height: headerHeight, scrollState } = navBarStore.getState();
|
|
201
182
|
|
|
202
|
-
|
|
183
|
+
const offsetY = Math.max(0, nativeEvent.contentOffset.y);
|
|
203
184
|
|
|
204
|
-
|
|
185
|
+
if (
|
|
186
|
+
!(scrollState === 0 || scrollState === -headerHeight) &&
|
|
187
|
+
flatListRef.current
|
|
188
|
+
) {
|
|
189
|
+
const offset =
|
|
205
190
|
getNearestValue(Math.round(scrollState), -headerHeight, 0) ===
|
|
206
|
-
-headerHeight
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
191
|
+
-headerHeight
|
|
192
|
+
? Math.round(offsetY + scrollState + headerHeight)
|
|
193
|
+
: Math.round(offsetY + scrollState);
|
|
194
|
+
|
|
195
|
+
flatListRef.current.scrollToOffset({
|
|
196
|
+
animated: true,
|
|
197
|
+
offset,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}, []);
|
|
211
201
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
);
|
|
202
|
+
/* onScrollEnd: Handling snap to expanded/collapsed state ( for navbar ) */
|
|
203
|
+
const _onScrollEndDrag = React.useCallback(({ nativeEvent }) => {
|
|
204
|
+
const { height: headerHeight, scrollState } = navBarStore.getState();
|
|
216
205
|
|
|
217
|
-
|
|
218
|
-
const _onMomentumScrollEnd = React.useCallback(
|
|
219
|
-
({ nativeEvent }) => {
|
|
220
|
-
if (!canMomentum.current) return;
|
|
221
|
-
canMomentum.current = false;
|
|
222
|
-
snapToHeaderState(nativeEvent.contentOffset.y);
|
|
223
|
-
},
|
|
224
|
-
[snapToHeaderState]
|
|
225
|
-
);
|
|
206
|
+
const offsetY = Math.max(0, nativeEvent.contentOffset.y);
|
|
226
207
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
208
|
+
if (
|
|
209
|
+
!(scrollState === 0 || scrollState === -headerHeight) &&
|
|
210
|
+
flatListRef.current &&
|
|
211
|
+
nativeEvent.velocity.y === 0
|
|
212
|
+
) {
|
|
213
|
+
const offset =
|
|
214
|
+
getNearestValue(Math.round(scrollState), -headerHeight, 0) ===
|
|
215
|
+
-headerHeight
|
|
216
|
+
? Math.round(offsetY + scrollState + headerHeight)
|
|
217
|
+
: Math.round(offsetY + scrollState);
|
|
218
|
+
|
|
219
|
+
flatListRef.current.scrollToOffset({
|
|
220
|
+
animated: true,
|
|
221
|
+
offset,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}, []);
|
|
237
225
|
|
|
238
226
|
const onScroll = React.useCallback((event) => {
|
|
239
227
|
hasUserScrolledRef.current = true;
|
|
@@ -257,42 +245,8 @@ function ComponentsMapComponent(props: Props) {
|
|
|
257
245
|
}
|
|
258
246
|
}, []);
|
|
259
247
|
|
|
260
|
-
const onScrollWithEndReached = React.useCallback(
|
|
261
|
-
(event) => {
|
|
262
|
-
onScroll(event);
|
|
263
|
-
|
|
264
|
-
if (!hasUserScrolledRef.current) return;
|
|
265
|
-
if (isScreenWrappedInContainer) return;
|
|
266
|
-
|
|
267
|
-
const {
|
|
268
|
-
nativeEvent: {
|
|
269
|
-
contentOffset: { y },
|
|
270
|
-
layoutMeasurement: { height },
|
|
271
|
-
contentSize: { height: contentHeight },
|
|
272
|
-
},
|
|
273
|
-
} = event;
|
|
274
|
-
|
|
275
|
-
const distanceFromEnd = contentHeight - y - height;
|
|
276
|
-
|
|
277
|
-
if (distanceFromEnd <= height * 0.5) {
|
|
278
|
-
if (!scrollEndReachedRef.current) {
|
|
279
|
-
scrollEndReachedRef.current = true;
|
|
280
|
-
emitScrollEndReached();
|
|
281
|
-
}
|
|
282
|
-
} else {
|
|
283
|
-
scrollEndReachedRef.current = false;
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
[onScroll, isScreenWrappedInContainer]
|
|
287
|
-
);
|
|
288
|
-
|
|
289
248
|
const contentContainerStyle = React.useMemo(
|
|
290
|
-
() =>
|
|
291
|
-
isScreenWrappedInContainer
|
|
292
|
-
? {
|
|
293
|
-
flexGrow: 1,
|
|
294
|
-
}
|
|
295
|
-
: screenStyle,
|
|
249
|
+
() => (isScreenWrappedInContainer ? {} : screenStyle),
|
|
296
250
|
[isScreenWrappedInContainer, screenStyle]
|
|
297
251
|
);
|
|
298
252
|
|
|
@@ -303,98 +257,61 @@ function ComponentsMapComponent(props: Props) {
|
|
|
303
257
|
// TODO: support both "isScreenWrappedInContainer" and hasScreenPicker().
|
|
304
258
|
// The Screen Picker in Mobile is completly different than the TV
|
|
305
259
|
// so the various offsets / margins in TV do not apply here.
|
|
306
|
-
// Fix for WebView rerender crashes on Android API 28+
|
|
307
|
-
// https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
|
|
308
|
-
const sharedScrollProps = {
|
|
309
|
-
overScrollMode: isAndroid ? ("never" as const) : ("auto" as const),
|
|
310
|
-
scrollIndicatorInsets,
|
|
311
|
-
onLayout: handleOnLayout,
|
|
312
|
-
contentContainerStyle,
|
|
313
|
-
refreshControl,
|
|
314
|
-
onScrollBeginDrag,
|
|
315
|
-
onMomentumScrollEnd: _onMomentumScrollEnd,
|
|
316
|
-
onScrollEndDrag: _onScrollEndDrag,
|
|
317
|
-
scrollEventThrottle: 16,
|
|
318
|
-
...scrollViewExtraProps,
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
const listContent = useScrollView ? (
|
|
322
|
-
<ScrollView
|
|
323
|
-
testID="components-map-scroll-view"
|
|
324
|
-
// ViewportTracker clones this child and calls its `ref` as a function
|
|
325
|
-
// (childrenRef.current.ref(node)), so a callback ref is required here
|
|
326
|
-
// — an object ref would throw "ref is not a function".
|
|
327
|
-
ref={(ref) => {
|
|
328
|
-
scrollViewRef.current = ref;
|
|
329
|
-
}}
|
|
330
|
-
onScroll={onScrollWithEndReached}
|
|
331
|
-
{...sharedScrollProps}
|
|
332
|
-
>
|
|
333
|
-
{riverComponents.map((item, index) => (
|
|
334
|
-
<React.Fragment key={keyExtractor(item)}>
|
|
335
|
-
{renderRiverItem({ item, index })}
|
|
336
|
-
</React.Fragment>
|
|
337
|
-
))}
|
|
338
|
-
<RiverFooter
|
|
339
|
-
flatListHeight={flatListHeight}
|
|
340
|
-
loadingState={loadingState}
|
|
341
|
-
/>
|
|
342
|
-
</ScrollView>
|
|
343
|
-
) : (
|
|
344
|
-
<FlatList
|
|
345
|
-
testID="components-map-flat-list"
|
|
346
|
-
// ViewportTracker clones this child and calls its `ref` as a function
|
|
347
|
-
// (childrenRef.current.ref(node)), so a callback ref is required here
|
|
348
|
-
// — an object ref would throw "ref is not a function".
|
|
349
|
-
ref={(ref) => {
|
|
350
|
-
flatListRef.current = ref;
|
|
351
|
-
}}
|
|
352
|
-
extraData={feed}
|
|
353
|
-
stickyHeaderIndices={stickyHeaderIndices}
|
|
354
|
-
removeClippedSubviews={isAndroid}
|
|
355
|
-
initialNumToRender={3}
|
|
356
|
-
maxToRenderPerBatch={10}
|
|
357
|
-
windowSize={12}
|
|
358
|
-
keyExtractor={keyExtractor}
|
|
359
|
-
renderItem={renderRiverItem}
|
|
360
|
-
data={riverComponents}
|
|
361
|
-
ListFooterComponent={
|
|
362
|
-
<RiverFooter
|
|
363
|
-
flatListHeight={flatListHeight}
|
|
364
|
-
loadingState={loadingState}
|
|
365
|
-
/>
|
|
366
|
-
}
|
|
367
|
-
onScroll={onScroll}
|
|
368
|
-
onEndReached={
|
|
369
|
-
/* TODO: end-reached detection is inconsistent between the two paths:
|
|
370
|
-
the ScrollView branch uses manual pixel-offset math (onScrollWithEndReached)
|
|
371
|
-
with a scrollEndReachedRef debounce, while this FlatList relies on the
|
|
372
|
-
native onEndReached. Consider unifying the threshold/debounce behavior. */
|
|
373
|
-
/* When wrapped in a parent ScrollView (e.g. tabs),
|
|
374
|
-
this FlatList doesn't scroll so onEndReached can fire repeatedly;
|
|
375
|
-
skip it here and let the parent ScrollView emit scroll-end instead. */
|
|
376
|
-
isScreenWrappedInContainer
|
|
377
|
-
? undefined
|
|
378
|
-
: () => {
|
|
379
|
-
if (!hasUserScrolledRef.current) return;
|
|
380
|
-
emitScrollEndReached();
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
{...sharedScrollProps}
|
|
384
|
-
/>
|
|
385
|
-
);
|
|
386
|
-
|
|
387
260
|
return (
|
|
388
|
-
|
|
389
|
-
// for measuring the list's position/height; it intentionally has no style.
|
|
390
|
-
<View ref={flatListWrapperRef}>
|
|
261
|
+
<View style={styles.container} ref={flatListWrapperRef}>
|
|
391
262
|
<ComponentsMapHeightContext.Provider value={flatListHeight}>
|
|
392
263
|
<ComponentsMapRefContext.Provider value={flatListWrapperRef}>
|
|
393
264
|
<ScreenLoadingMeasurements
|
|
394
265
|
riverId={riverId}
|
|
395
266
|
numberOfComponents={riverComponents.length}
|
|
396
267
|
>
|
|
397
|
-
<ViewportTracker>
|
|
268
|
+
<ViewportTracker>
|
|
269
|
+
<FlatList
|
|
270
|
+
testID="components-map-flat-list"
|
|
271
|
+
ref={(ref) => {
|
|
272
|
+
flatListRef.current = ref;
|
|
273
|
+
}}
|
|
274
|
+
// Fix for WebView rerender crashes on Android API 28+
|
|
275
|
+
// https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
|
|
276
|
+
overScrollMode={isAndroid ? "never" : "auto"}
|
|
277
|
+
scrollIndicatorInsets={scrollIndicatorInsets}
|
|
278
|
+
extraData={feed}
|
|
279
|
+
stickyHeaderIndices={stickyHeaderIndices}
|
|
280
|
+
removeClippedSubviews={isAndroid}
|
|
281
|
+
onLayout={handleOnLayout}
|
|
282
|
+
initialNumToRender={3}
|
|
283
|
+
maxToRenderPerBatch={10}
|
|
284
|
+
windowSize={12}
|
|
285
|
+
keyExtractor={keyExtractor}
|
|
286
|
+
renderItem={renderRiverItem}
|
|
287
|
+
data={riverComponents}
|
|
288
|
+
contentContainerStyle={contentContainerStyle}
|
|
289
|
+
ListFooterComponent={
|
|
290
|
+
<RiverFooter
|
|
291
|
+
flatListHeight={flatListHeight}
|
|
292
|
+
loadingState={loadingState}
|
|
293
|
+
/>
|
|
294
|
+
}
|
|
295
|
+
refreshControl={refreshControl}
|
|
296
|
+
onScrollBeginDrag={onScrollBeginDrag}
|
|
297
|
+
onScroll={onScroll}
|
|
298
|
+
onMomentumScrollEnd={_onMomentumScrollEnd}
|
|
299
|
+
onScrollEndDrag={_onScrollEndDrag}
|
|
300
|
+
scrollEventThrottle={16}
|
|
301
|
+
{...scrollViewExtraProps}
|
|
302
|
+
onEndReached={
|
|
303
|
+
/* When wrapped in a parent ScrollView (e.g. tabs),
|
|
304
|
+
this FlatList doesn't scroll so onEndReached can fire repeatedly;
|
|
305
|
+
skip it here and let the parent ScrollView emit scroll-end instead. */
|
|
306
|
+
isScreenWrappedInContainer
|
|
307
|
+
? undefined
|
|
308
|
+
: () => {
|
|
309
|
+
if (!hasUserScrolledRef.current) return;
|
|
310
|
+
emitScrollEndReached();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/>
|
|
314
|
+
</ViewportTracker>
|
|
398
315
|
</ScreenLoadingMeasurements>
|
|
399
316
|
</ComponentsMapRefContext.Provider>
|
|
400
317
|
</ComponentsMapHeightContext.Provider>
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`componentsMap renders renders components map correctly 1`] = `
|
|
4
|
-
<View
|
|
4
|
+
<View
|
|
5
|
+
style={
|
|
6
|
+
{
|
|
7
|
+
"flex": 1,
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
>
|
|
5
11
|
<View
|
|
6
12
|
onLayout={[Function]}
|
|
13
|
+
style={
|
|
14
|
+
{
|
|
15
|
+
"flex": 1,
|
|
16
|
+
}
|
|
17
|
+
}
|
|
7
18
|
>
|
|
8
19
|
<RCTScrollView
|
|
9
20
|
ListFooterComponent={
|
|
@@ -156,6 +167,11 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
156
167
|
>
|
|
157
168
|
<View
|
|
158
169
|
onLayout={[Function]}
|
|
170
|
+
style={
|
|
171
|
+
{
|
|
172
|
+
"flex": 1,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
159
175
|
>
|
|
160
176
|
<View />
|
|
161
177
|
</View>
|
|
@@ -167,6 +183,11 @@ exports[`componentsMap renders renders components map correctly 1`] = `
|
|
|
167
183
|
>
|
|
168
184
|
<View
|
|
169
185
|
onLayout={[Function]}
|
|
186
|
+
style={
|
|
187
|
+
{
|
|
188
|
+
"flex": 1,
|
|
189
|
+
}
|
|
190
|
+
}
|
|
170
191
|
/>
|
|
171
192
|
</View>
|
|
172
193
|
<View
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { sendTapCellEvent, sendTapMenuItem } from "../Analytics";
|
|
2
1
|
import { getItemType } from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
3
2
|
import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
|
|
4
3
|
|
|
@@ -12,7 +11,6 @@ export function onCellPress(
|
|
|
12
11
|
itemIndex,
|
|
13
12
|
layoutVersion = "v1"
|
|
14
13
|
) {
|
|
15
|
-
sendTapCellEvent(entry, component, headerTitle, itemIndex);
|
|
16
14
|
const componentScreenType = screenTypeFromComponent(component);
|
|
17
15
|
const entryScreenType = screenTypeFromEntry(entry);
|
|
18
16
|
const itemType = getItemType(entry, layoutVersion);
|
|
@@ -37,7 +35,3 @@ function screenTypeFromComponent(component) {
|
|
|
37
35
|
function screenTypeFromEntry(entry) {
|
|
38
36
|
return entry && entry.screen_type;
|
|
39
37
|
}
|
|
40
|
-
|
|
41
|
-
export function onMenuItemFocus({ item, index, isHome }) {
|
|
42
|
-
sendTapMenuItem({ item, index, isHome });
|
|
43
|
-
}
|
package/Helpers/index.js
CHANGED
|
@@ -7,42 +7,3 @@ export const ASSETS = {
|
|
|
7
7
|
amazon: "tv_app_background",
|
|
8
8
|
}),
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
export const ANALYTICS_CORE_EVENTS = {
|
|
12
|
-
TAP_CELL: "Tap Cell",
|
|
13
|
-
PLAY_VOD_ITEM: "Play VOD Item",
|
|
14
|
-
VOD_ITEM_PLAY_WAS_TRIGGERED: "VOD Item: Play Was Triggered",
|
|
15
|
-
CHANNEL_ITEM_PLAY_WAS_TRIGGERED: "Channel Item: Play Was Triggered",
|
|
16
|
-
PROGRAM_ITEM_PLAY_WAS_TRIGGERED: "Program Item: Play Was Triggered",
|
|
17
|
-
TAP_MENU_ICON: "Tap Menu Item",
|
|
18
|
-
HOME_SCREEN_VIEWED: "Home Screen: Viewed",
|
|
19
|
-
ITEM_NOT_AVAILABLE: "N/A",
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const ANALYTICS_ENTRY_EVENTS = {
|
|
23
|
-
ITEM_TYPE: "Item Type",
|
|
24
|
-
ITEM_TITLE: "Item Name",
|
|
25
|
-
ITEM_ID: "Item ID",
|
|
26
|
-
ITEM_LINK: "Item Link",
|
|
27
|
-
ITEM_INDEX: "Item Number",
|
|
28
|
-
ITEM_TIME_PLAYED: "Item Total Time Played",
|
|
29
|
-
ITEM_TOTAL_TIME_PLAYED: "Item Total Time Played",
|
|
30
|
-
ITEM_PLAY_COMPLETED: "Completed",
|
|
31
|
-
ITEM_CUSTOM_PROPERTY: "Custom Property",
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export const ANALYTICS_COMPONENT_EVENTS = {
|
|
35
|
-
COMPONENT_ID: "Component ID",
|
|
36
|
-
COMPONENT_TYPE: "Component Type",
|
|
37
|
-
CELL_STYLE: "Cell Style",
|
|
38
|
-
HEADER_TITLE: "Header Name",
|
|
39
|
-
COMPONENT_SOURCE: "Component Source",
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export const ANALYTICS_MENU_ITEM_EVENTS = {
|
|
43
|
-
ITEM_TYPE: "Item Type",
|
|
44
|
-
ITEM_TITLE: "Item Name",
|
|
45
|
-
ITEM_ID: "Item ID",
|
|
46
|
-
ITEM_HOME_SCREEN: "Home Screen",
|
|
47
|
-
ITEM_INDEX: "Item Number",
|
|
48
|
-
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "16.0.0-alpha.
|
|
3
|
+
"version": "16.0.0-alpha.1803625751",
|
|
4
4
|
"description": "Applicaster Zapp React Native ui components for the Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "16.0.0-alpha.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-alpha.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-alpha.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-alpha.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-alpha.1803625751",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-alpha.1803625751",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-alpha.1803625751",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-alpha.1803625751",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"url": "^0.11.0",
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { StyleSheet, View } from "react-native";
|
|
3
|
-
|
|
4
|
-
const styles = StyleSheet.create({
|
|
5
|
-
row: { flexDirection: "row" },
|
|
6
|
-
basis: {
|
|
7
|
-
maxWidth: "100%",
|
|
8
|
-
flexBasis: "100%",
|
|
9
|
-
},
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
type Props = {
|
|
13
|
-
children: React.ReactNode;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Constrains its child to exactly the available parent width.
|
|
18
|
-
*
|
|
19
|
-
* UI components such as Hero and List render their items inside a parent
|
|
20
|
-
* `ScrollView` (e.g. when wrapped by tabs). A child placed directly in a
|
|
21
|
-
* scroll container can size itself to its own content rather than to the
|
|
22
|
-
* viewport, which causes items to be measured at a stale width and resize
|
|
23
|
-
* unexpectedly after layout changes such as device rotation.
|
|
24
|
-
*
|
|
25
|
-
* Wrapping the content in a `flexDirection: "row"` parent with a
|
|
26
|
-
* `flexBasis: "100%"` / `maxWidth: "100%"` child forces the child to
|
|
27
|
-
* re-derive its width from the parent on every layout pass instead of
|
|
28
|
-
* caching a measured pixel width, keeping it full-width and stable.
|
|
29
|
-
*
|
|
30
|
-
* @param children - The content to render at full parent width.
|
|
31
|
-
*/
|
|
32
|
-
export function FullWidthRow({ children }: Props) {
|
|
33
|
-
return (
|
|
34
|
-
<View style={styles.row}>
|
|
35
|
-
<View style={styles.basis}>{children}</View>
|
|
36
|
-
</View>
|
|
37
|
-
);
|
|
38
|
-
}
|