@assistant-ui/react-native 0.1.44 → 0.1.46

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.
Files changed (89) hide show
  1. package/LICENSE +1 -1
  2. package/dist/index.d.ts +4 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -3
  5. package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
  6. package/dist/primitives/actionBar/ActionBarEdit.js.map +1 -1
  7. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -1
  8. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -1
  9. package/dist/primitives/actionBar/ActionBarReload.js.map +1 -1
  10. package/dist/primitives/actionBar/ActionBarSpeak.js.map +1 -1
  11. package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
  12. package/dist/primitives/attachment/AttachmentName.js.map +1 -1
  13. package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
  14. package/dist/primitives/attachment/AttachmentRoot.js.map +1 -1
  15. package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
  16. package/dist/primitives/branchPicker/BranchPickerCount.js.map +1 -1
  17. package/dist/primitives/branchPicker/BranchPickerNext.js.map +1 -1
  18. package/dist/primitives/branchPicker/BranchPickerNumber.js.map +1 -1
  19. package/dist/primitives/branchPicker/BranchPickerPrevious.js.map +1 -1
  20. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
  21. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js.map +1 -1
  22. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
  23. package/dist/primitives/composer/ComposerCancel.js.map +1 -1
  24. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  25. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  26. package/dist/primitives/composer/ComposerQuoteDismiss.js.map +1 -1
  27. package/dist/primitives/composer/ComposerQuoteText.js.map +1 -1
  28. package/dist/primitives/composer/ComposerRoot.js.map +1 -1
  29. package/dist/primitives/composer/ComposerSend.js.map +1 -1
  30. package/dist/primitives/error/ErrorMessage.js.map +1 -1
  31. package/dist/primitives/error/ErrorRoot.js.map +1 -1
  32. package/dist/primitives/message/MessageContent.d.ts.map +1 -1
  33. package/dist/primitives/message/MessageContent.js +43 -33
  34. package/dist/primitives/message/MessageContent.js.map +1 -1
  35. package/dist/primitives/message/MessageIf.js.map +1 -1
  36. package/dist/primitives/message/MessageParts.js.map +1 -1
  37. package/dist/primitives/message/MessageRoot.js.map +1 -1
  38. package/dist/primitives/message.d.ts +1 -0
  39. package/dist/primitives/message.d.ts.map +1 -1
  40. package/dist/primitives/message.js +3 -2
  41. package/dist/primitives/message.js.map +1 -1
  42. package/dist/primitives/messagePart/MessagePartImage.js.map +1 -1
  43. package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
  44. package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
  45. package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
  46. package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
  47. package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
  48. package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
  49. package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
  50. package/dist/primitives/thread/ThreadEmpty.js.map +1 -1
  51. package/dist/primitives/thread/ThreadIf.js.map +1 -1
  52. package/dist/primitives/thread/ThreadMessages.d.ts +7 -5
  53. package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -1
  54. package/dist/primitives/thread/ThreadMessages.js +109 -376
  55. package/dist/primitives/thread/ThreadMessages.js.map +1 -1
  56. package/dist/primitives/thread/ThreadRoot.js.map +1 -1
  57. package/dist/primitives/thread/ThreadRowsFlatList.d.ts +29 -0
  58. package/dist/primitives/thread/ThreadRowsFlatList.d.ts.map +1 -0
  59. package/dist/primitives/thread/ThreadRowsFlatList.js +226 -0
  60. package/dist/primitives/thread/ThreadRowsFlatList.js.map +1 -0
  61. package/dist/primitives/thread/ThreadSuggestion.js.map +1 -1
  62. package/dist/primitives/thread/flatListScroll.d.ts +52 -0
  63. package/dist/primitives/thread/flatListScroll.d.ts.map +1 -0
  64. package/dist/primitives/thread/flatListScroll.js +380 -0
  65. package/dist/primitives/thread/flatListScroll.js.map +1 -0
  66. package/dist/primitives/thread.d.ts +2 -0
  67. package/dist/primitives/thread.d.ts.map +1 -1
  68. package/dist/primitives/thread.js +5 -2
  69. package/dist/primitives/thread.js.map +1 -1
  70. package/dist/primitives/threadList/ThreadListItems.js.map +1 -1
  71. package/dist/primitives/threadList/ThreadListNew.js.map +1 -1
  72. package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
  73. package/dist/primitives/threadListItem/ThreadListItemArchive.js.map +1 -1
  74. package/dist/primitives/threadListItem/ThreadListItemDelete.js.map +1 -1
  75. package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
  76. package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
  77. package/dist/primitives/threadListItem/ThreadListItemUnarchive.js.map +1 -1
  78. package/package.json +13 -9
  79. package/src/index.ts +18 -1
  80. package/src/primitives/message/MessageContent.test.tsx +46 -1
  81. package/src/primitives/message/MessageContent.tsx +3 -1
  82. package/src/primitives/message.test.tsx +74 -0
  83. package/src/primitives/message.ts +1 -0
  84. package/src/primitives/thread/ThreadMessages.test.tsx +28 -1
  85. package/src/primitives/thread/ThreadMessages.tsx +49 -343
  86. package/src/primitives/thread/ThreadRowsFlatList.test.tsx +253 -0
  87. package/src/primitives/thread/ThreadRowsFlatList.tsx +159 -0
  88. package/src/primitives/thread/flatListScroll.ts +467 -0
  89. package/src/primitives/thread.ts +5 -0
@@ -1,30 +1,25 @@
1
1
  import {
2
2
  type ComponentType,
3
3
  type FC,
4
- type ForwardedRef,
5
4
  type ReactNode,
6
- type RefObject,
7
5
  forwardRef,
8
6
  memo,
9
7
  useCallback,
10
- useEffect,
11
- useRef,
12
8
  } from "react";
13
- import {
14
- FlatList,
15
- type FlatListProps,
16
- type LayoutChangeEvent,
17
- type NativeScrollEvent,
18
- type NativeSyntheticEvent,
19
- } from "react-native";
9
+ import { FlatList, type FlatListProps } from "react-native";
20
10
  import type { ThreadMessage } from "@assistant-ui/core";
21
11
  import type { MessageState } from "@assistant-ui/core/store";
22
- import {
23
- RenderChildrenWithAccessor,
24
- useAuiEvent,
25
- useAuiState,
26
- } from "@assistant-ui/store";
12
+ import { RenderChildrenWithAccessor, useAuiState } from "@assistant-ui/store";
27
13
  import { MessageByIndexProvider } from "@assistant-ui/core/react";
14
+ import {
15
+ type FlatListHistory,
16
+ getFlatListPagingProps,
17
+ useComposedFlatListRef,
18
+ useFlatListAutoScroll,
19
+ useFlatListScrollProps,
20
+ useHistoryLoad,
21
+ useThreadHistory,
22
+ } from "./flatListScroll";
28
23
 
29
24
  type MessageComponents =
30
25
  | {
@@ -68,56 +63,18 @@ export type ThreadMessagesFlatListProps = Omit<
68
63
  scrollToBottomOnRunStart?: boolean | undefined;
69
64
  scrollToBottomOnInitialize?: boolean | undefined;
70
65
  scrollToBottomOnThreadSwitch?: boolean | undefined;
71
- history?:
72
- | {
73
- hasMore: boolean;
74
- isLoadingMore: boolean;
75
- loadMore: () => void;
76
- }
77
- | undefined;
66
+ /**
67
+ * Pages older messages in when the list nears its start. Defaults to the
68
+ * runtime's `thread.hasEarlier` / `isLoadingEarlier` / `loadEarlier()`;
69
+ * pass it to drive paging from app state instead.
70
+ */
71
+ history?: FlatListHistory | undefined;
78
72
  };
79
73
 
80
74
  /** @deprecated Use ThreadMessagesFlatListProps instead. */
81
75
  export type ThreadMessagesProps = ThreadMessagesFlatListProps;
82
76
 
83
77
  const DEFAULT_SYSTEM_MESSAGE = () => null;
84
- const AT_BOTTOM_THRESHOLD = 4;
85
-
86
- const useHistoryLoad = (
87
- history: ThreadMessagesFlatListProps["history"],
88
- onStartReached: FlatListProps<ThreadMessage>["onStartReached"],
89
- ) => {
90
- const loadRequestedRef = useRef(false);
91
- const hasMore = history?.hasMore ?? false;
92
- const isLoadingMore = history?.isLoadingMore ?? false;
93
-
94
- // The latch is scoped to one commit so a no-op load cannot disable paging.
95
- useEffect(() => {
96
- loadRequestedRef.current = isLoadingMore;
97
- });
98
-
99
- const handleStartReached = useCallback<
100
- NonNullable<FlatListProps<ThreadMessage>["onStartReached"]>
101
- >(
102
- (info) => {
103
- onStartReached?.(info);
104
- if (loadRequestedRef.current) return;
105
- loadRequestedRef.current = true;
106
- try {
107
- history?.loadMore();
108
- } catch (error) {
109
- loadRequestedRef.current = false;
110
- throw error;
111
- }
112
- },
113
- [history, onStartReached],
114
- );
115
-
116
- return {
117
- canLoadMore: hasMore && !isLoadingMore,
118
- handleStartReached,
119
- };
120
- };
121
78
 
122
79
  const getComponent = (
123
80
  components: MessageComponents,
@@ -223,230 +180,6 @@ const ThreadMessageByChildren = memo(
223
180
  );
224
181
  ThreadMessageByChildren.displayName = "ThreadPrimitive.MessageByChildren";
225
182
 
226
- const setForwardedRef = <T,>(ref: ForwardedRef<T>, value: T | null) => {
227
- if (typeof ref === "function") {
228
- ref(value);
229
- } else if (ref) {
230
- ref.current = value;
231
- }
232
- };
233
-
234
- const useComposedFlatListRef = (
235
- forwardedRef: ForwardedRef<FlatList<ThreadMessage>>,
236
- ) => {
237
- const flatListRef = useRef<FlatList<ThreadMessage> | null>(null);
238
-
239
- const setFlatListRef = useCallback(
240
- (node: FlatList<ThreadMessage> | null) => {
241
- flatListRef.current = node;
242
- setForwardedRef(forwardedRef, node);
243
- },
244
- [forwardedRef],
245
- );
246
-
247
- return [flatListRef, setFlatListRef] as const;
248
- };
249
-
250
- const useThreadMessagesFlatListAutoScroll = ({
251
- flatListRef,
252
- hasMessages,
253
- horizontal = false,
254
- autoScroll = true,
255
- scrollToBottomOnRunStart = true,
256
- scrollToBottomOnInitialize = true,
257
- scrollToBottomOnThreadSwitch = true,
258
- }: {
259
- flatListRef: RefObject<FlatList<ThreadMessage> | null>;
260
- hasMessages: boolean;
261
- horizontal?: boolean | null | undefined;
262
- autoScroll?: boolean | undefined;
263
- scrollToBottomOnRunStart?: boolean | undefined;
264
- scrollToBottomOnInitialize?: boolean | undefined;
265
- scrollToBottomOnThreadSwitch?: boolean | undefined;
266
- }) => {
267
- const metricsRef = useRef({
268
- contentHeight: 0,
269
- viewportHeight: 0,
270
- scrollY: 0,
271
- });
272
- const isAtBottomRef = useRef(true);
273
- const lastScrollEventOffsetRef = useRef(0);
274
- const initializeScrollRequestedRef = useRef(false);
275
- const contentSizeVersionRef = useRef(0);
276
- const pendingScrollToBottomRef = useRef<
277
- | false
278
- | {
279
- animated: boolean;
280
- minimumContentSizeVersion: number;
281
- }
282
- >(false);
283
-
284
- const updateIsAtBottom = useCallback(() => {
285
- const { contentHeight, scrollY, viewportHeight } = metricsRef.current;
286
- isAtBottomRef.current =
287
- contentHeight <= viewportHeight ||
288
- contentHeight - scrollY - viewportHeight <= AT_BOTTOM_THRESHOLD;
289
- }, []);
290
-
291
- // Commanding a scroll records the intended position immediately; the
292
- // native scroll echo is bridged and throttled, so waiting for it lets a
293
- // fast stream observe stale metrics and drop out of following.
294
- const scrollToBottom = useCallback(
295
- (animated: boolean) => {
296
- const { contentHeight, viewportHeight } = metricsRef.current;
297
- const offset = Math.max(0, contentHeight - viewportHeight);
298
- metricsRef.current.scrollY = offset;
299
- isAtBottomRef.current = true;
300
- flatListRef.current?.scrollToOffset({ offset, animated });
301
- },
302
- [flatListRef],
303
- );
304
-
305
- const handleLayout = useCallback(
306
- (event: LayoutChangeEvent) => {
307
- const wasAtBottom = isAtBottomRef.current;
308
- const previousViewportHeight = metricsRef.current.viewportHeight;
309
- const viewportHeight = horizontal
310
- ? event.nativeEvent.layout.width
311
- : event.nativeEvent.layout.height;
312
- metricsRef.current.viewportHeight = viewportHeight;
313
- updateIsAtBottom();
314
- const pending = pendingScrollToBottomRef.current;
315
- if (
316
- pending &&
317
- contentSizeVersionRef.current >= pending.minimumContentSizeVersion &&
318
- viewportHeight > 0
319
- ) {
320
- pendingScrollToBottomRef.current = false;
321
- scrollToBottom(pending.animated);
322
- return;
323
- }
324
- if (!wasAtBottom) return;
325
- // Layout changes are never user gestures, so they must not unpin. Past
326
- // the first measurement, a viewport change while pinned re-commands the
327
- // bottom position, since no content-size event follows a bare keyboard
328
- // open or close.
329
- if (
330
- autoScroll &&
331
- previousViewportHeight !== 0 &&
332
- viewportHeight !== previousViewportHeight
333
- ) {
334
- scrollToBottom(pending ? pending.animated : false);
335
- } else {
336
- isAtBottomRef.current = true;
337
- }
338
- },
339
- [autoScroll, horizontal, scrollToBottom, updateIsAtBottom],
340
- );
341
-
342
- const handleScroll = useCallback(
343
- (event: NativeSyntheticEvent<NativeScrollEvent>) => {
344
- const { contentOffset, contentSize, layoutMeasurement } =
345
- event.nativeEvent;
346
- const scrollOffset = horizontal ? contentOffset.x : contentOffset.y;
347
- const previousEventOffset = lastScrollEventOffsetRef.current;
348
- const wasPinnedToBottom = isAtBottomRef.current;
349
- lastScrollEventOffsetRef.current = scrollOffset;
350
- metricsRef.current = {
351
- contentHeight: horizontal ? contentSize.width : contentSize.height,
352
- viewportHeight: horizontal
353
- ? layoutMeasurement.width
354
- : layoutMeasurement.height,
355
- scrollY: scrollOffset,
356
- };
357
- updateIsAtBottom();
358
- const upwardMove = scrollOffset < previousEventOffset;
359
- // Only a deliberate upward move unpins or cancels a pending scroll.
360
- // Gestures are detected echo-to-echo because a commanded scroll
361
- // optimistically moves the tracked position ahead of its ascending
362
- // animation echoes, and those echoes must not unpin mid-flight.
363
- if (wasPinnedToBottom && !upwardMove) {
364
- isAtBottomRef.current = true;
365
- }
366
- if (!isAtBottomRef.current && upwardMove) {
367
- pendingScrollToBottomRef.current = false;
368
- }
369
- },
370
- [horizontal, updateIsAtBottom],
371
- );
372
-
373
- const handleContentSizeChange = useCallback(
374
- (width: number, height: number) => {
375
- const metrics = metricsRef.current;
376
- const contentHeight = horizontal ? width : height;
377
- const previousContentHeight = metrics.contentHeight;
378
- const wasAtBottom = isAtBottomRef.current;
379
- if (contentHeight > 0) contentSizeVersionRef.current += 1;
380
- metrics.contentHeight = contentHeight;
381
- updateIsAtBottom();
382
-
383
- // Initialize and thread-switch requests are repeated after the list has
384
- // measured so the explicit bottom offset uses real content metrics.
385
- const pendingScroll = pendingScrollToBottomRef.current;
386
- if (
387
- pendingScroll &&
388
- contentSizeVersionRef.current >=
389
- pendingScroll.minimumContentSizeVersion &&
390
- metrics.viewportHeight > 0
391
- ) {
392
- pendingScrollToBottomRef.current = false;
393
- scrollToBottom(pendingScroll.animated);
394
- return;
395
- }
396
-
397
- if (!autoScroll) return;
398
- if (!wasAtBottom) return;
399
- if (previousContentHeight === 0) return;
400
- if (contentHeight <= previousContentHeight) return;
401
-
402
- scrollToBottom(false);
403
- },
404
- [autoScroll, horizontal, scrollToBottom, updateIsAtBottom],
405
- );
406
-
407
- useEffect(() => {
408
- if (!scrollToBottomOnInitialize) return;
409
- if (!hasMessages) {
410
- initializeScrollRequestedRef.current = false;
411
- return;
412
- }
413
- if (initializeScrollRequestedRef.current) return;
414
-
415
- initializeScrollRequestedRef.current = true;
416
- pendingScrollToBottomRef.current = {
417
- animated: false,
418
- minimumContentSizeVersion: Math.max(1, contentSizeVersionRef.current),
419
- };
420
- scrollToBottom(false);
421
- }, [hasMessages, scrollToBottom, scrollToBottomOnInitialize]);
422
-
423
- useAuiEvent("thread.runStart", () => {
424
- if (!scrollToBottomOnRunStart) return;
425
- pendingScrollToBottomRef.current = {
426
- animated: true,
427
- minimumContentSizeVersion: contentSizeVersionRef.current + 1,
428
- };
429
- scrollToBottom(true);
430
- });
431
-
432
- useAuiEvent("threads.selectionChanged", () => {
433
- if (!scrollToBottomOnThreadSwitch) return;
434
- initializeScrollRequestedRef.current = false;
435
- lastScrollEventOffsetRef.current = 0;
436
- pendingScrollToBottomRef.current = {
437
- animated: false,
438
- minimumContentSizeVersion: contentSizeVersionRef.current + 1,
439
- };
440
- scrollToBottom(false);
441
- });
442
-
443
- return {
444
- handleLayout,
445
- handleScroll,
446
- handleContentSizeChange,
447
- };
448
- };
449
-
450
183
  export const ThreadMessagesFlatList = forwardRef<
451
184
  FlatList<ThreadMessage>,
452
185
  ThreadMessagesFlatListProps
@@ -461,6 +194,8 @@ export const ThreadMessagesFlatList = forwardRef<
461
194
  onScroll,
462
195
  onStartReached,
463
196
  onStartReachedThreshold,
197
+ onEndReached,
198
+ onEndReachedThreshold,
464
199
  scrollEventThrottle,
465
200
  scrollToBottomOnInitialize,
466
201
  scrollToBottomOnRunStart,
@@ -471,12 +206,13 @@ export const ThreadMessagesFlatList = forwardRef<
471
206
  forwardedRef,
472
207
  ) => {
473
208
  const messages = useAuiState((s) => s.thread.messages);
209
+ const effectiveHistory = useThreadHistory(history);
474
210
  const [flatListRef, setFlatListRef] = useComposedFlatListRef(forwardedRef);
475
211
  const {
476
212
  handleContentSizeChange: handleAutoScrollContentSizeChange,
477
213
  handleLayout: handleAutoScrollLayout,
478
214
  handleScroll: handleAutoScrollScroll,
479
- } = useThreadMessagesFlatListAutoScroll({
215
+ } = useFlatListAutoScroll({
480
216
  flatListRef,
481
217
  hasMessages: messages.length > 0,
482
218
  horizontal: flatListProps.horizontal,
@@ -502,39 +238,35 @@ export const ThreadMessagesFlatList = forwardRef<
502
238
 
503
239
  const keyExtractor = useCallback((item: ThreadMessage) => item.id, []);
504
240
 
505
- const scrollTracking =
506
- (autoScroll ?? true) ||
507
- (scrollToBottomOnInitialize ?? true) ||
508
- (scrollToBottomOnRunStart ?? true) ||
509
- (scrollToBottomOnThreadSwitch ?? true);
510
-
511
- const handleLayout = useCallback(
512
- (event: LayoutChangeEvent) => {
513
- handleAutoScrollLayout(event);
514
- onLayout?.(event);
515
- },
516
- [handleAutoScrollLayout, onLayout],
517
- );
241
+ const scrollProps = useFlatListScrollProps({
242
+ autoScroll,
243
+ scrollToBottomOnInitialize,
244
+ scrollToBottomOnRunStart,
245
+ scrollToBottomOnThreadSwitch,
246
+ onContentSizeChange,
247
+ onLayout,
248
+ onScroll,
249
+ scrollEventThrottle,
250
+ handleAutoScrollContentSizeChange,
251
+ handleAutoScrollLayout,
252
+ handleAutoScrollScroll,
253
+ });
518
254
 
519
- const handleScroll = useCallback(
520
- (event: NativeSyntheticEvent<NativeScrollEvent>) => {
521
- handleAutoScrollScroll(event);
522
- onScroll?.(event);
523
- },
524
- [handleAutoScrollScroll, onScroll],
255
+ const { canLoadMore, handleReached } = useHistoryLoad(
256
+ effectiveHistory,
257
+ onStartReached,
525
258
  );
526
-
527
- const handleContentSizeChange = useCallback(
528
- (width: number, height: number) => {
529
- handleAutoScrollContentSizeChange(width, height);
530
- onContentSizeChange?.(width, height);
259
+ const pagingProps = getFlatListPagingProps(
260
+ "start",
261
+ effectiveHistory,
262
+ canLoadMore,
263
+ handleReached,
264
+ {
265
+ onStartReached,
266
+ onStartReachedThreshold,
267
+ onEndReached,
268
+ onEndReachedThreshold,
531
269
  },
532
- [handleAutoScrollContentSizeChange, onContentSizeChange],
533
- );
534
-
535
- const { canLoadMore, handleStartReached } = useHistoryLoad(
536
- history,
537
- onStartReached,
538
270
  );
539
271
 
540
272
  return (
@@ -544,34 +276,8 @@ export const ThreadMessagesFlatList = forwardRef<
544
276
  renderItem={renderItem}
545
277
  keyExtractor={keyExtractor}
546
278
  maintainVisibleContentPosition={{ minIndexForVisible: 0 }}
547
- {...(scrollTracking
548
- ? {
549
- onContentSizeChange: handleContentSizeChange,
550
- onLayout: handleLayout,
551
- onScroll: handleScroll,
552
- scrollEventThrottle: scrollEventThrottle ?? 16,
553
- }
554
- : {
555
- ...(onContentSizeChange && { onContentSizeChange }),
556
- ...(onLayout && { onLayout }),
557
- ...(onScroll && { onScroll }),
558
- ...(scrollEventThrottle !== undefined && { scrollEventThrottle }),
559
- })}
560
- {...(history
561
- ? {
562
- ...(canLoadMore
563
- ? { onStartReached: handleStartReached }
564
- : onStartReached
565
- ? { onStartReached }
566
- : {}),
567
- onStartReachedThreshold: onStartReachedThreshold ?? 1,
568
- }
569
- : {
570
- ...(onStartReached && { onStartReached }),
571
- ...(onStartReachedThreshold !== undefined && {
572
- onStartReachedThreshold,
573
- }),
574
- })}
279
+ {...scrollProps}
280
+ {...pagingProps}
575
281
  {...flatListProps}
576
282
  />
577
283
  );