@adventurelabs/scout-core 1.4.28 → 1.4.32
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.
|
@@ -20,6 +20,7 @@ export declare const useInfiniteEventsByHerd: (herdId: number, options: UseInfin
|
|
|
20
20
|
export declare const useInfiniteEventsByDevice: (deviceId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<IEventAndTagsPrettyLocation>;
|
|
21
21
|
export declare const useInfiniteArtifactsByHerd: (herdId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<IArtifactWithMediaUrl>;
|
|
22
22
|
export declare const useInfiniteArtifactsByDevice: (deviceId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<IArtifactWithMediaUrl>;
|
|
23
|
+
/** useInfiniteFeedByHerd: logic matches useInfiniteFeedByHerdDummy verbatim; only the fetch is via API (RTK Query) instead of supabase.rpc. */
|
|
23
24
|
export declare const useInfiniteFeedByHerd: (herdId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<IFeedItem>;
|
|
24
25
|
export declare const useInfiniteFeedByDevice: (deviceId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<IFeedItem>;
|
|
25
26
|
export declare const useIntersectionObserver: (callback: () => void, options?: IntersectionObserverInit) => import("react").Dispatch<import("react").SetStateAction<Element | null>>;
|
|
@@ -408,93 +408,131 @@ export const useInfiniteArtifactsByDevice = (deviceId, options) => {
|
|
|
408
408
|
};
|
|
409
409
|
};
|
|
410
410
|
const feedCursorEq = (a, b) => {
|
|
411
|
-
if (a ===
|
|
411
|
+
if (a === b)
|
|
412
412
|
return true;
|
|
413
|
-
if (
|
|
413
|
+
if (a == null || b == null)
|
|
414
414
|
return false;
|
|
415
|
-
return a.timestamp === b.timestamp &&
|
|
415
|
+
return (a.timestamp === b.timestamp &&
|
|
416
|
+
a.id === b.id &&
|
|
417
|
+
a.feed_type === b.feed_type);
|
|
416
418
|
};
|
|
419
|
+
/** useInfiniteFeedByHerd: logic matches useInfiniteFeedByHerdDummy verbatim; only the fetch is via API (RTK Query) instead of supabase.rpc. */
|
|
417
420
|
export const useInfiniteFeedByHerd = (herdId, options) => {
|
|
421
|
+
const limit = options.limit ?? 20;
|
|
422
|
+
const enabled = !!(options.enabled && herdId);
|
|
418
423
|
const [pages, setPages] = useState([]);
|
|
419
424
|
const [currentCursor, setCurrentCursor] = useState(null);
|
|
420
|
-
|
|
421
|
-
const [lastResult, setLastResult] = useState(null);
|
|
425
|
+
const [currentResult, setCurrentResult] = useState(null);
|
|
422
426
|
const prevHerdIdRef = useRef(undefined);
|
|
423
427
|
const lastAddedCursorRef = useRef(undefined);
|
|
424
428
|
const pagesLengthRef = useRef(0);
|
|
425
429
|
const currentQuery = useGetFeedInfiniteByHerdQuery({
|
|
426
430
|
herdId,
|
|
427
|
-
limit
|
|
431
|
+
limit,
|
|
428
432
|
cursor: currentCursor,
|
|
429
433
|
supabase: options.supabase,
|
|
430
|
-
}, { skip: !
|
|
434
|
+
}, { skip: !enabled });
|
|
435
|
+
const isLoading = currentQuery.isLoading;
|
|
431
436
|
useEffect(() => {
|
|
432
437
|
pagesLengthRef.current = pages.length;
|
|
433
438
|
}, [pages.length]);
|
|
434
|
-
//
|
|
439
|
+
// Reset when herd changes (match dummy: prev !== herdId && enabled && herdId)
|
|
435
440
|
useEffect(() => {
|
|
436
441
|
if (prevHerdIdRef.current !== undefined &&
|
|
437
|
-
prevHerdIdRef.current !== herdId
|
|
442
|
+
prevHerdIdRef.current !== herdId &&
|
|
443
|
+
enabled &&
|
|
444
|
+
herdId) {
|
|
438
445
|
setPages([]);
|
|
439
446
|
setCurrentCursor(null);
|
|
447
|
+
setCurrentResult(null);
|
|
440
448
|
lastAddedCursorRef.current = undefined;
|
|
441
|
-
setLastResult(null);
|
|
442
449
|
}
|
|
443
450
|
prevHerdIdRef.current = herdId;
|
|
444
|
-
}, [herdId]);
|
|
445
|
-
// When
|
|
451
|
+
}, [herdId, enabled]);
|
|
452
|
+
// When cursor changes, clear ref so we merge the new response
|
|
446
453
|
useEffect(() => {
|
|
447
454
|
lastAddedCursorRef.current = undefined;
|
|
448
455
|
}, [currentCursor]);
|
|
456
|
+
// Merge when we have data (mirror dummy's .then() logic; fetch is done by RTK Query)
|
|
449
457
|
useEffect(() => {
|
|
450
458
|
if (!currentQuery.data || currentQuery.isLoading)
|
|
451
459
|
return;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
feedCursorEq(lastAddedCursorRef.current ?? null, currentCursor))
|
|
455
|
-
return;
|
|
456
|
-
const items = Array.isArray(currentQuery.data?.items)
|
|
460
|
+
const cursor = currentCursor;
|
|
461
|
+
const items = Array.isArray(currentQuery.data.items)
|
|
457
462
|
? currentQuery.data.items
|
|
458
463
|
: [];
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
//
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
464
|
+
const hasMore = currentQuery.data.hasMore ?? false;
|
|
465
|
+
const nextCursor = currentQuery.data.nextCursor ?? null;
|
|
466
|
+
// Only update currentResult for successful response (match dummy)
|
|
467
|
+
if (items.length === 0 && cursor === null) {
|
|
468
|
+
// Leave currentResult unchanged on spurious empty first page
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
setCurrentResult({
|
|
472
|
+
hasMore: hasMore && nextCursor != null,
|
|
473
|
+
nextCursor,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
if (items.length === 0)
|
|
477
|
+
return;
|
|
478
|
+
// Skip merge exactly like dummy: full page already added for this cursor and we have pages
|
|
479
|
+
if (items.length >= limit &&
|
|
480
|
+
feedCursorEq(lastAddedCursorRef.current ?? null, cursor) &&
|
|
481
|
+
pagesLengthRef.current > 0) {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
467
484
|
setPages((prev) => {
|
|
468
|
-
const existingPage = prev.find((p) => feedCursorEq(p.cursor,
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
];
|
|
485
|
+
const existingPage = prev.find((p) => feedCursorEq(p.cursor, cursor));
|
|
486
|
+
const next = !existingPage
|
|
487
|
+
? [...prev, { cursor, data: items }]
|
|
488
|
+
: items.length > existingPage.data.length
|
|
489
|
+
? prev.map((p) => feedCursorEq(p.cursor, cursor) ? { cursor, data: items } : p)
|
|
490
|
+
: prev;
|
|
491
|
+
if (!existingPage && items.length >= limit) {
|
|
492
|
+
lastAddedCursorRef.current = cursor;
|
|
477
493
|
}
|
|
478
|
-
|
|
494
|
+
if (existingPage &&
|
|
495
|
+
items.length > existingPage.data.length &&
|
|
496
|
+
items.length >= limit) {
|
|
497
|
+
lastAddedCursorRef.current = cursor;
|
|
498
|
+
}
|
|
499
|
+
return next;
|
|
479
500
|
});
|
|
480
|
-
}, [
|
|
501
|
+
}, [
|
|
502
|
+
currentQuery.data,
|
|
503
|
+
currentQuery.isLoading,
|
|
504
|
+
currentCursor,
|
|
505
|
+
pages.length,
|
|
506
|
+
limit,
|
|
507
|
+
]);
|
|
481
508
|
const loadMore = useCallback(() => {
|
|
482
|
-
if (
|
|
483
|
-
|
|
484
|
-
!
|
|
485
|
-
setCurrentCursor(
|
|
509
|
+
if (currentResult?.hasMore &&
|
|
510
|
+
currentResult.nextCursor != null &&
|
|
511
|
+
!isLoading) {
|
|
512
|
+
setCurrentCursor(currentResult.nextCursor);
|
|
486
513
|
}
|
|
487
|
-
}, [
|
|
514
|
+
}, [currentResult, isLoading]);
|
|
488
515
|
const refetch = useCallback(() => {
|
|
489
516
|
setPages([]);
|
|
490
517
|
setCurrentCursor(null);
|
|
518
|
+
setCurrentResult(null);
|
|
491
519
|
lastAddedCursorRef.current = undefined;
|
|
492
|
-
setLastResult(null);
|
|
493
520
|
currentQuery.refetch();
|
|
494
521
|
}, [currentQuery]);
|
|
495
522
|
const allItems = useMemo(() => {
|
|
523
|
+
const sorted = [...pages].sort((a, b) => {
|
|
524
|
+
if (feedCursorEq(a.cursor, b.cursor))
|
|
525
|
+
return 0;
|
|
526
|
+
if (a.cursor === null)
|
|
527
|
+
return -1;
|
|
528
|
+
if (b.cursor === null)
|
|
529
|
+
return 1;
|
|
530
|
+
const ta = a.cursor.timestamp ?? '';
|
|
531
|
+
const tb = b.cursor.timestamp ?? '';
|
|
532
|
+
return tb.localeCompare(ta);
|
|
533
|
+
});
|
|
496
534
|
const seen = new Set();
|
|
497
|
-
return
|
|
535
|
+
return sorted.flatMap((p) => p.data).filter((item) => {
|
|
498
536
|
const key = `${item.sort_ts ?? ''}_${item.sort_id ?? ''}_${item.feed_type ?? ''}`;
|
|
499
537
|
if (seen.has(key))
|
|
500
538
|
return false;
|
|
@@ -504,10 +542,10 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
|
|
|
504
542
|
}, [pages]);
|
|
505
543
|
return {
|
|
506
544
|
items: allItems,
|
|
507
|
-
isLoading:
|
|
508
|
-
isLoadingMore:
|
|
509
|
-
hasMore:
|
|
510
|
-
(currentCursor !== null && pages.length > 0)
|
|
545
|
+
isLoading: isLoading && pages.length === 0,
|
|
546
|
+
isLoadingMore: isLoading && pages.length > 0,
|
|
547
|
+
hasMore: currentResult?.hasMore ??
|
|
548
|
+
(currentCursor !== null && pages.length > 0) ??
|
|
511
549
|
false,
|
|
512
550
|
loadMore,
|
|
513
551
|
refetch,
|
|
@@ -555,7 +593,7 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
|
|
|
555
593
|
hasMore: currentQuery.data?.hasMore ?? false,
|
|
556
594
|
nextCursor: currentQuery.data?.nextCursor ?? null,
|
|
557
595
|
};
|
|
558
|
-
|
|
596
|
+
setLastResult(nextResult);
|
|
559
597
|
setPages((prev) => {
|
|
560
598
|
const existingPage = prev.find((p) => feedCursorEq(p.cursor, currentCursor));
|
|
561
599
|
if (!existingPage) {
|
|
@@ -573,6 +611,9 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
|
|
|
573
611
|
}
|
|
574
612
|
return prev;
|
|
575
613
|
});
|
|
614
|
+
if (nextResult.hasMore && nextResult.nextCursor != null) {
|
|
615
|
+
setCurrentCursor(nextResult.nextCursor);
|
|
616
|
+
}
|
|
576
617
|
}, [currentQuery.data, currentQuery.isLoading, currentCursor, pages.length, options.limit]);
|
|
577
618
|
const loadMore = useCallback(() => {
|
|
578
619
|
if (lastResult?.hasMore &&
|