@applicaster/zapp-react-native-ui-components 16.0.0-rc.85 → 16.0.0-rc.87
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 +15 -0
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +1 -23
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +1 -36
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +1 -26
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +30 -25
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +7 -2
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +48 -1
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +0 -2
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +18 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +54 -0
- package/Components/ModalComponent/BottomSheetModalContent.tsx +92 -51
- package/Components/ModalComponent/SortableList.tsx +38 -23
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +17 -0
- package/Components/ModalComponent/index.tsx +7 -9
- package/Components/River/ComponentsMap/ComponentsMap.tsx +85 -59
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +138 -75
- package/Components/River/__tests__/componentsMap.test.js +8 -3
- package/Components/Screen/navigationHandler.ts +8 -8
- package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
- package/Contexts/ModalNavigationContext/index.tsx +2 -2
- package/package.json +5 -5
- package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -314
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
memo,
|
|
3
|
+
useCallback,
|
|
4
|
+
useDeferredValue,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
} from "react";
|
|
2
9
|
import {
|
|
3
10
|
ActivityIndicator,
|
|
4
11
|
ScrollView,
|
|
@@ -48,6 +55,10 @@ const styles = StyleSheet.create({
|
|
|
48
55
|
fontSize: 14,
|
|
49
56
|
textAlign: "center",
|
|
50
57
|
},
|
|
58
|
+
scrollView: {
|
|
59
|
+
flexShrink: 1,
|
|
60
|
+
},
|
|
61
|
+
someStyles: { paddingHorizontal: 20, paddingTop: 8, paddingBottom: 12 },
|
|
51
62
|
});
|
|
52
63
|
|
|
53
64
|
type ModalComponentProps = {
|
|
@@ -81,9 +92,10 @@ type ModalComponentProps = {
|
|
|
81
92
|
contentComponent?: React.ComponentType<any>;
|
|
82
93
|
contentComponentProps?: Record<string, any>;
|
|
83
94
|
styleOverrides?: ModalBlocksConfig | Record<string, any>;
|
|
95
|
+
measurementPhase?: boolean;
|
|
84
96
|
};
|
|
85
97
|
|
|
86
|
-
|
|
98
|
+
function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
87
99
|
const {
|
|
88
100
|
items,
|
|
89
101
|
currentRoute,
|
|
@@ -103,13 +115,21 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
103
115
|
contentComponent: ContentComponent,
|
|
104
116
|
contentComponentProps = {},
|
|
105
117
|
styleOverrides: propStyleOverrides,
|
|
118
|
+
measurementPhase = false,
|
|
106
119
|
} = props;
|
|
107
120
|
|
|
108
121
|
const route = useRef(currentRoute);
|
|
109
122
|
const theme = useTheme<BaseThemePropertiesMobile>();
|
|
123
|
+
const bottomInset = useSafeAreaInsets().bottom;
|
|
110
124
|
const paddingTop = Number(theme.modal_bottom_sheet_padding_top);
|
|
111
125
|
const paddingBottom = Number(theme.modal_bottom_sheet_padding_bottom);
|
|
112
126
|
|
|
127
|
+
const calculatedcontentContainerStyle = useMemo(() => {
|
|
128
|
+
return {
|
|
129
|
+
paddingBottom: paddingBottom + bottomInset,
|
|
130
|
+
};
|
|
131
|
+
}, [paddingBottom, bottomInset]);
|
|
132
|
+
|
|
113
133
|
const {
|
|
114
134
|
items: fetchedItems,
|
|
115
135
|
showCentralLoading,
|
|
@@ -159,11 +179,28 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
159
179
|
const createPlaylistTitle =
|
|
160
180
|
pluginLocalizations?.create_new_playlist_title || "Create New Playlist";
|
|
161
181
|
|
|
182
|
+
const nextUpTitle =
|
|
183
|
+
pluginLocalizations?.next_up_title ||
|
|
184
|
+
(normalizedPluginId
|
|
185
|
+
? pluginLocalizations?.[`${normalizedPluginId}_next_up_title`]
|
|
186
|
+
: undefined) ||
|
|
187
|
+
"Next Up";
|
|
188
|
+
|
|
189
|
+
const nowPlayingTitle =
|
|
190
|
+
pluginLocalizations?.now_playing_title ||
|
|
191
|
+
(normalizedPluginId
|
|
192
|
+
? pluginLocalizations?.[`${normalizedPluginId}_now_playing_title`]
|
|
193
|
+
: undefined) ||
|
|
194
|
+
"Now Playing";
|
|
195
|
+
|
|
162
196
|
const resolvedTitle = props.title || localizedHeaderTitle || title;
|
|
163
197
|
|
|
164
198
|
const itemsToRender = viewModel ? fetchedItems : items;
|
|
165
199
|
const operations = editableCollection?.operations || [];
|
|
166
|
-
|
|
200
|
+
|
|
201
|
+
const isSortable = useDeferredValue(
|
|
202
|
+
!measurementPhase && hasOperation(operations, OPERATIONS.REORDER)
|
|
203
|
+
);
|
|
167
204
|
|
|
168
205
|
const cell = getCell(
|
|
169
206
|
viewModel ? role : undefined,
|
|
@@ -264,7 +301,17 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
264
301
|
]
|
|
265
302
|
);
|
|
266
303
|
|
|
267
|
-
const
|
|
304
|
+
const sortableListRenderItem = useCallback(
|
|
305
|
+
({ item, index, renderHandle }) =>
|
|
306
|
+
cell.renderItem({
|
|
307
|
+
item,
|
|
308
|
+
index,
|
|
309
|
+
onPress: onItemPress,
|
|
310
|
+
context: renderContext,
|
|
311
|
+
renderHandle,
|
|
312
|
+
}),
|
|
313
|
+
[cell, onItemPress, renderContext]
|
|
314
|
+
);
|
|
268
315
|
|
|
269
316
|
const hasNowPlaying = hasOperation(operations, OPERATIONS.NOW_PLAYING);
|
|
270
317
|
const hasClearAll = hasOperation(operations, OPERATIONS.CLEAR_ALL);
|
|
@@ -291,6 +338,8 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
291
338
|
component: () => (
|
|
292
339
|
<NowPlayingHeaderSection
|
|
293
340
|
onClear={hasClearAll ? handleDeleteCollection : undefined}
|
|
341
|
+
nextUpTitle={nextUpTitle}
|
|
342
|
+
nowPlayingTitle={nowPlayingTitle}
|
|
294
343
|
/>
|
|
295
344
|
),
|
|
296
345
|
});
|
|
@@ -318,11 +367,32 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
318
367
|
hasNowPlaying,
|
|
319
368
|
hasClearAll,
|
|
320
369
|
handleDeleteCollection,
|
|
370
|
+
nextUpTitle,
|
|
371
|
+
nowPlayingTitle,
|
|
321
372
|
hasAdd,
|
|
322
373
|
handleCreatePlaylist,
|
|
374
|
+
createPlaylistTitle,
|
|
323
375
|
]);
|
|
324
376
|
|
|
325
|
-
const
|
|
377
|
+
const _items = useMemo(() => {
|
|
378
|
+
return (itemsToRender || []).map((item, index) =>
|
|
379
|
+
cell.renderItem({
|
|
380
|
+
item,
|
|
381
|
+
index,
|
|
382
|
+
onPress: onItemPress,
|
|
383
|
+
context: renderContext,
|
|
384
|
+
})
|
|
385
|
+
);
|
|
386
|
+
}, [itemsToRender, cell, onItemPress, renderContext]);
|
|
387
|
+
|
|
388
|
+
const _styles = useMemo(() => {
|
|
389
|
+
return {
|
|
390
|
+
maxHeight: props.maxHeight,
|
|
391
|
+
paddingTop: paddingTop,
|
|
392
|
+
};
|
|
393
|
+
}, [props.maxHeight, paddingTop]);
|
|
394
|
+
|
|
395
|
+
const renderedBody = (() => {
|
|
326
396
|
if (ContentComponent) {
|
|
327
397
|
const shouldWrapInScrollView =
|
|
328
398
|
!contentComponentProps?.disableScrollViewWrap &&
|
|
@@ -331,9 +401,8 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
331
401
|
if (shouldWrapInScrollView) {
|
|
332
402
|
return (
|
|
333
403
|
<ScrollView
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}}
|
|
404
|
+
keyboardShouldPersistTaps="handled"
|
|
405
|
+
contentContainerStyle={calculatedcontentContainerStyle}
|
|
337
406
|
>
|
|
338
407
|
<ContentComponent
|
|
339
408
|
{...contentComponentProps}
|
|
@@ -346,12 +415,7 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
346
415
|
}
|
|
347
416
|
|
|
348
417
|
return (
|
|
349
|
-
<View
|
|
350
|
-
style={{
|
|
351
|
-
flexShrink: 1,
|
|
352
|
-
paddingBottom: paddingBottom + bottomInset,
|
|
353
|
-
}}
|
|
354
|
-
>
|
|
418
|
+
<View style={[styles.scrollView, calculatedcontentContainerStyle]}>
|
|
355
419
|
<ContentComponent
|
|
356
420
|
{...contentComponentProps}
|
|
357
421
|
dismiss={dismiss}
|
|
@@ -395,43 +459,30 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
395
459
|
<SortableList
|
|
396
460
|
sortableData={itemsToRender}
|
|
397
461
|
headers={headers}
|
|
398
|
-
scrollViewStyle={
|
|
399
|
-
|
|
400
|
-
}}
|
|
401
|
-
contentContainerStyle={{
|
|
402
|
-
paddingBottom: paddingBottom + bottomInset,
|
|
403
|
-
}}
|
|
462
|
+
scrollViewStyle={styles.scrollView}
|
|
463
|
+
contentContainerStyle={calculatedcontentContainerStyle}
|
|
404
464
|
onDragEnd={handleDragEnd}
|
|
405
|
-
renderItem={
|
|
406
|
-
cell.renderItem({
|
|
407
|
-
item,
|
|
408
|
-
index,
|
|
409
|
-
onPress: onItemPress,
|
|
410
|
-
context: renderContext,
|
|
411
|
-
renderHandle,
|
|
412
|
-
})
|
|
413
|
-
}
|
|
465
|
+
renderItem={sortableListRenderItem}
|
|
414
466
|
/>
|
|
415
467
|
);
|
|
416
468
|
}
|
|
417
469
|
|
|
418
470
|
return (
|
|
419
471
|
<ScrollView
|
|
472
|
+
keyboardShouldPersistTaps="handled"
|
|
420
473
|
overScrollMode="never"
|
|
421
474
|
bounces={false}
|
|
422
|
-
contentContainerStyle={
|
|
423
|
-
paddingBottom: paddingBottom + bottomInset,
|
|
424
|
-
}}
|
|
475
|
+
contentContainerStyle={calculatedcontentContainerStyle}
|
|
425
476
|
>
|
|
426
477
|
{hasNowPlaying ? (
|
|
427
478
|
<NowPlayingHeaderSection
|
|
428
479
|
onClear={hasClearAll ? handleDeleteCollection : undefined}
|
|
480
|
+
nextUpTitle={nextUpTitle}
|
|
481
|
+
nowPlayingTitle={nowPlayingTitle}
|
|
429
482
|
/>
|
|
430
483
|
) : null}
|
|
431
484
|
{hasAdd ? (
|
|
432
|
-
<View
|
|
433
|
-
style={{ paddingHorizontal: 20, paddingTop: 8, paddingBottom: 12 }}
|
|
434
|
-
>
|
|
485
|
+
<View style={styles.someStyles}>
|
|
435
486
|
<AudioPlayerButton
|
|
436
487
|
configuration={VARIANT_WITH_ICON}
|
|
437
488
|
data={{ title: createPlaylistTitle }}
|
|
@@ -439,26 +490,14 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
439
490
|
/>
|
|
440
491
|
</View>
|
|
441
492
|
) : null}
|
|
442
|
-
{
|
|
443
|
-
cell.renderItem({
|
|
444
|
-
item,
|
|
445
|
-
index,
|
|
446
|
-
onPress: onItemPress,
|
|
447
|
-
context: renderContext,
|
|
448
|
-
})
|
|
449
|
-
)}
|
|
493
|
+
{_items}
|
|
450
494
|
</ScrollView>
|
|
451
495
|
);
|
|
452
|
-
};
|
|
496
|
+
})();
|
|
453
497
|
|
|
454
498
|
return (
|
|
455
499
|
<ModalBlocksStyleContext.Provider value={styleOverrides as any}>
|
|
456
|
-
<View
|
|
457
|
-
style={{
|
|
458
|
-
maxHeight: props.maxHeight,
|
|
459
|
-
paddingTop: paddingTop,
|
|
460
|
-
}}
|
|
461
|
-
>
|
|
500
|
+
<View style={_styles}>
|
|
462
501
|
<BottomSheetDragArea>
|
|
463
502
|
<Header
|
|
464
503
|
dismiss={dismiss}
|
|
@@ -467,8 +506,10 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
467
506
|
title={resolvedTitle}
|
|
468
507
|
/>
|
|
469
508
|
</BottomSheetDragArea>
|
|
470
|
-
{
|
|
509
|
+
{renderedBody}
|
|
471
510
|
</View>
|
|
472
511
|
</ModalBlocksStyleContext.Provider>
|
|
473
512
|
);
|
|
474
513
|
}
|
|
514
|
+
|
|
515
|
+
export const BottomSheetModalContent = memo(_BottomSheetModalContent);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useState } from "react";
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
2
|
import Sortable from "react-native-sortables";
|
|
3
3
|
import { Text, View, ViewStyle } from "react-native";
|
|
4
4
|
import Animated, { useAnimatedRef } from "react-native-reanimated";
|
|
@@ -65,6 +65,9 @@ export type SortableListProps = {
|
|
|
65
65
|
}) => void;
|
|
66
66
|
};
|
|
67
67
|
|
|
68
|
+
const defaultKeyExtractor = (item, index) =>
|
|
69
|
+
`sortable-item-${item?.id ?? item?.title ?? index}`;
|
|
70
|
+
|
|
68
71
|
export const SortableList = ({
|
|
69
72
|
scrollViewStyle,
|
|
70
73
|
contentContainerStyle,
|
|
@@ -73,8 +76,7 @@ export const SortableList = ({
|
|
|
73
76
|
columns = 1,
|
|
74
77
|
rowGap = 0,
|
|
75
78
|
columnGap = 0,
|
|
76
|
-
keyExtractor =
|
|
77
|
-
`sortable-item-${item?.id ?? item?.title ?? index}`,
|
|
79
|
+
keyExtractor = defaultKeyExtractor,
|
|
78
80
|
renderItem,
|
|
79
81
|
sortableData = DEFAULT_SORTABLE_DATA,
|
|
80
82
|
headers = DEFAULT_HEADERS,
|
|
@@ -85,7 +87,9 @@ export const SortableList = ({
|
|
|
85
87
|
const [localData, setLocalData] = useState(sortableData);
|
|
86
88
|
|
|
87
89
|
useEffect(() => {
|
|
88
|
-
setLocalData(
|
|
90
|
+
setLocalData((localData) =>
|
|
91
|
+
localData !== sortableData ? sortableData : localData
|
|
92
|
+
);
|
|
89
93
|
}, [sortableData]);
|
|
90
94
|
|
|
91
95
|
const handleDragEnd = useCallback(
|
|
@@ -101,13 +105,24 @@ export const SortableList = ({
|
|
|
101
105
|
[onDragEnd]
|
|
102
106
|
);
|
|
103
107
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
const _renderItem = useCallback(
|
|
109
|
+
({ item, index }) => (
|
|
110
|
+
<View key={keyExtractor(item, index)} style={itemStyle}>
|
|
111
|
+
{renderItem({
|
|
112
|
+
item,
|
|
113
|
+
index,
|
|
114
|
+
renderHandle: (children) => (
|
|
115
|
+
<Sortable.Handle>{children}</Sortable.Handle>
|
|
116
|
+
),
|
|
117
|
+
})}
|
|
118
|
+
</View>
|
|
119
|
+
),
|
|
120
|
+
[itemStyle, keyExtractor, renderItem]
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const renderHeader = useMemo(
|
|
124
|
+
() =>
|
|
125
|
+
(headers || []).map((header, idx) => (
|
|
111
126
|
<View key={`static-header-${idx}`} style={headerStyle}>
|
|
112
127
|
{header.title ? (
|
|
113
128
|
<SortableListHeader title={header.title} />
|
|
@@ -115,7 +130,17 @@ export const SortableList = ({
|
|
|
115
130
|
renderHeaderComponent(header.component)
|
|
116
131
|
)}
|
|
117
132
|
</View>
|
|
118
|
-
))
|
|
133
|
+
)),
|
|
134
|
+
[headers, headerStyle]
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<Animated.ScrollView
|
|
139
|
+
ref={scrollableRef}
|
|
140
|
+
style={scrollViewStyle}
|
|
141
|
+
contentContainerStyle={contentContainerStyle}
|
|
142
|
+
>
|
|
143
|
+
{renderHeader}
|
|
119
144
|
<Sortable.Grid
|
|
120
145
|
scrollableRef={scrollableRef}
|
|
121
146
|
customHandle
|
|
@@ -125,17 +150,7 @@ export const SortableList = ({
|
|
|
125
150
|
data={localData}
|
|
126
151
|
keyExtractor={keyExtractor}
|
|
127
152
|
onDragEnd={handleDragEnd}
|
|
128
|
-
renderItem={
|
|
129
|
-
<View key={keyExtractor(item, index)} style={itemStyle}>
|
|
130
|
-
{renderItem({
|
|
131
|
-
item,
|
|
132
|
-
index,
|
|
133
|
-
renderHandle: (children) => (
|
|
134
|
-
<Sortable.Handle>{children}</Sortable.Handle>
|
|
135
|
-
),
|
|
136
|
-
})}
|
|
137
|
-
</View>
|
|
138
|
-
)}
|
|
153
|
+
renderItem={_renderItem}
|
|
139
154
|
/>
|
|
140
155
|
</Animated.ScrollView>
|
|
141
156
|
);
|
|
@@ -235,4 +235,21 @@ describe("BottomSheetModalContent role/behavior cells", () => {
|
|
|
235
235
|
const { ScrollView } = require("react-native");
|
|
236
236
|
expect(() => UNSAFE_getByType(ScrollView)).toThrow();
|
|
237
237
|
});
|
|
238
|
+
|
|
239
|
+
it("sets keyboardShouldPersistTaps='handled' on ScrollView wrapping contentComponent", () => {
|
|
240
|
+
const CustomComponent = () => (
|
|
241
|
+
<React.Fragment>Custom Content</React.Fragment>
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const { UNSAFE_getByType } = render(
|
|
245
|
+
<BottomSheetModalContent
|
|
246
|
+
{...baseProps}
|
|
247
|
+
contentComponent={CustomComponent}
|
|
248
|
+
/>
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const { ScrollView } = require("react-native");
|
|
252
|
+
const scrollView = UNSAFE_getByType(ScrollView);
|
|
253
|
+
expect(scrollView.props.keyboardShouldPersistTaps).toBe("handled");
|
|
254
|
+
});
|
|
238
255
|
});
|
|
@@ -26,20 +26,18 @@ type Props = {
|
|
|
26
26
|
export function ModalComponent(props: ModalBottomSheetContentProps & Props) {
|
|
27
27
|
const [height, setHeight] = useState(0);
|
|
28
28
|
|
|
29
|
-
const onLayout = useCallback(
|
|
30
|
-
(
|
|
31
|
-
|
|
32
|
-
setHeight(nativeEvent.layout.height);
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
[height]
|
|
36
|
-
);
|
|
29
|
+
const onLayout = useCallback(({ nativeEvent }) => {
|
|
30
|
+
setHeight((value) => (value === 0 ? nativeEvent.layout.height : value));
|
|
31
|
+
}, []);
|
|
37
32
|
|
|
38
33
|
if (height === 0) {
|
|
39
34
|
return (
|
|
40
35
|
<MeasurementsPortal
|
|
41
36
|
Component={BottomSheetModalContent}
|
|
42
|
-
componentProps={
|
|
37
|
+
componentProps={{
|
|
38
|
+
...props,
|
|
39
|
+
measurementPhase: true,
|
|
40
|
+
}}
|
|
43
41
|
onLayout={onLayout}
|
|
44
42
|
/>
|
|
45
43
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as R from "ramda";
|
|
3
2
|
import { FlatList, StyleSheet, View } from "react-native";
|
|
3
|
+
import { SafeAreaView, EdgeMode, Edge } from "react-native-safe-area-context";
|
|
4
4
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
5
5
|
import { RiverItem } from "../RiverItem";
|
|
6
6
|
import { RiverFooter } from "../RiverFooter";
|
|
@@ -40,6 +40,12 @@ type Props = {
|
|
|
40
40
|
riverId?: string;
|
|
41
41
|
getStaticComponentFeed: any;
|
|
42
42
|
stickyHeaderIndices?: number[];
|
|
43
|
+
safeArea?:
|
|
44
|
+
| {
|
|
45
|
+
edges: Record<Edge, EdgeMode>;
|
|
46
|
+
mode: "padding" | "margin" | undefined;
|
|
47
|
+
}
|
|
48
|
+
| undefined;
|
|
43
49
|
};
|
|
44
50
|
|
|
45
51
|
const styles = StyleSheet.create({
|
|
@@ -52,7 +58,7 @@ const getNearestValue = (value, optionA, optionB) =>
|
|
|
52
58
|
Math.abs(value - optionA) < Math.abs(value - optionB) ? optionA : optionB;
|
|
53
59
|
|
|
54
60
|
const scrollIndicatorInsets = { right: 1 };
|
|
55
|
-
const keyExtractor =
|
|
61
|
+
const keyExtractor = (item) => item.id.toString();
|
|
56
62
|
|
|
57
63
|
function ComponentsMapComponent(props: Props) {
|
|
58
64
|
const {
|
|
@@ -64,6 +70,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
64
70
|
riverId,
|
|
65
71
|
getStaticComponentFeed,
|
|
66
72
|
stickyHeaderIndices,
|
|
73
|
+
safeArea,
|
|
67
74
|
} = props;
|
|
68
75
|
|
|
69
76
|
const flatListRef = React.useRef<FlatList | null>(null);
|
|
@@ -77,7 +84,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
77
84
|
|
|
78
85
|
const riverComponents = React.useMemo(() => {
|
|
79
86
|
if (feed?.entry?.length < components.length) {
|
|
80
|
-
return
|
|
87
|
+
return components.slice(0, feed?.entry?.length);
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
return components;
|
|
@@ -92,7 +99,7 @@ function ComponentsMapComponent(props: Props) {
|
|
|
92
99
|
const { loadingState, onLoadFinished, onLoadFailed, shouldShowLoadingError } =
|
|
93
100
|
useLoadingState(riverComponents.length, onLoadDone);
|
|
94
101
|
|
|
95
|
-
const theme = useTheme();
|
|
102
|
+
const theme = useTheme<BaseThemePropertiesMobile>();
|
|
96
103
|
|
|
97
104
|
const renderRiverItem = React.useCallback(
|
|
98
105
|
({ item, index }) => {
|
|
@@ -121,21 +128,13 @@ function ComponentsMapComponent(props: Props) {
|
|
|
121
128
|
|
|
122
129
|
const screenStyle = React.useMemo(
|
|
123
130
|
() => ({
|
|
124
|
-
paddingTop: ifEmptyUseFallback(
|
|
125
|
-
screenConfig.paddingTop,
|
|
126
|
-
R.prop("screen_padding_top")(theme)
|
|
127
|
-
),
|
|
128
|
-
paddingBottom: ifEmptyUseFallback(
|
|
129
|
-
screenConfig.paddingBottom,
|
|
130
|
-
R.prop("screen_padding_bottom")(theme)
|
|
131
|
-
),
|
|
132
131
|
paddingLeft: ifEmptyUseFallback(
|
|
133
132
|
screenConfig.paddingLeft,
|
|
134
|
-
|
|
133
|
+
theme.screen_padding_left
|
|
135
134
|
),
|
|
136
135
|
paddingRight: ifEmptyUseFallback(
|
|
137
136
|
screenConfig.paddingRight,
|
|
138
|
-
|
|
137
|
+
theme.screen_padding_right
|
|
139
138
|
),
|
|
140
139
|
}),
|
|
141
140
|
[riverId]
|
|
@@ -266,51 +265,79 @@ function ComponentsMapComponent(props: Props) {
|
|
|
266
265
|
numberOfComponents={riverComponents.length}
|
|
267
266
|
>
|
|
268
267
|
<ViewportTracker>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
268
|
+
<>
|
|
269
|
+
<FlatList
|
|
270
|
+
ListHeaderComponent={
|
|
271
|
+
<SafeAreaView
|
|
272
|
+
mode={safeArea?.mode ?? "margin"}
|
|
273
|
+
style={{
|
|
274
|
+
paddingTop: ifEmptyUseFallback(
|
|
275
|
+
screenConfig.paddingTop,
|
|
276
|
+
theme.screen_padding_top
|
|
277
|
+
),
|
|
278
|
+
height: StyleSheet.hairlineWidth,
|
|
279
|
+
}}
|
|
280
|
+
edges={{ top: safeArea?.edges?.top ?? "off" }}
|
|
281
|
+
/>
|
|
282
|
+
}
|
|
283
|
+
testID="components-map-flat-list"
|
|
284
|
+
ref={(ref) => {
|
|
285
|
+
flatListRef.current = ref;
|
|
286
|
+
}}
|
|
287
|
+
// Fix for WebView rerender crashes on Android API 28+
|
|
288
|
+
// https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
|
|
289
|
+
overScrollMode={isAndroid ? "never" : "auto"}
|
|
290
|
+
scrollIndicatorInsets={scrollIndicatorInsets}
|
|
291
|
+
extraData={feed}
|
|
292
|
+
stickyHeaderIndices={stickyHeaderIndices}
|
|
293
|
+
removeClippedSubviews={isAndroid}
|
|
294
|
+
onLayout={handleOnLayout}
|
|
295
|
+
initialNumToRender={3}
|
|
296
|
+
maxToRenderPerBatch={10}
|
|
297
|
+
windowSize={12}
|
|
298
|
+
keyExtractor={keyExtractor}
|
|
299
|
+
renderItem={renderRiverItem}
|
|
300
|
+
data={riverComponents}
|
|
301
|
+
contentContainerStyle={contentContainerStyle}
|
|
302
|
+
ListFooterComponent={
|
|
303
|
+
<>
|
|
304
|
+
<RiverFooter
|
|
305
|
+
flatListHeight={flatListHeight}
|
|
306
|
+
loadingState={loadingState}
|
|
307
|
+
/>
|
|
308
|
+
<SafeAreaView
|
|
309
|
+
mode={safeArea?.mode ?? "margin"}
|
|
310
|
+
style={{
|
|
311
|
+
paddingBottom: ifEmptyUseFallback(
|
|
312
|
+
screenConfig.paddingBottom,
|
|
313
|
+
theme.screen_padding_bottom
|
|
314
|
+
),
|
|
315
|
+
height: StyleSheet.hairlineWidth,
|
|
316
|
+
}}
|
|
317
|
+
edges={{ bottom: safeArea?.edges?.bottom ?? "off" }}
|
|
318
|
+
/>
|
|
319
|
+
</>
|
|
320
|
+
}
|
|
321
|
+
refreshControl={refreshControl}
|
|
322
|
+
onScrollBeginDrag={onScrollBeginDrag}
|
|
323
|
+
onScroll={onScroll}
|
|
324
|
+
onMomentumScrollEnd={_onMomentumScrollEnd}
|
|
325
|
+
onScrollEndDrag={_onScrollEndDrag}
|
|
326
|
+
scrollEventThrottle={16}
|
|
327
|
+
{...scrollViewExtraProps}
|
|
328
|
+
onEndReached={
|
|
329
|
+
/* When wrapped in a parent ScrollView (e.g. tabs),
|
|
304
330
|
this FlatList doesn't scroll so onEndReached can fire repeatedly;
|
|
305
331
|
skip it here and let the parent ScrollView emit scroll-end instead. */
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
332
|
+
isScreenWrappedInContainer
|
|
333
|
+
? undefined
|
|
334
|
+
: () => {
|
|
335
|
+
if (!hasUserScrolledRef.current) return;
|
|
336
|
+
emitScrollEndReached();
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/>
|
|
340
|
+
</>
|
|
314
341
|
</ViewportTracker>
|
|
315
342
|
</ScreenLoadingMeasurements>
|
|
316
343
|
</ComponentsMapRefContext.Provider>
|
|
@@ -320,6 +347,5 @@ function ComponentsMapComponent(props: Props) {
|
|
|
320
347
|
}
|
|
321
348
|
|
|
322
349
|
export const ComponentsMap = React.memo(
|
|
323
|
-
withComponentsMapProvider(ComponentsMapComponent)
|
|
324
|
-
R.equals
|
|
350
|
+
withComponentsMapProvider(ComponentsMapComponent)
|
|
325
351
|
);
|