@adventurelabs/scout-core 1.4.24 → 1.4.27

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.
@@ -417,29 +417,31 @@ const feedCursorEq = (a, b) => {
417
417
  export const useInfiniteFeedByHerd = (herdId, options) => {
418
418
  const [pages, setPages] = useState([]);
419
419
  const [currentCursor, setCurrentCursor] = useState(null);
420
- const prevHerdIdRef = useRef();
420
+ // Match dummy: use state so hasMore/nextCursor trigger re-renders and loadMore sees latest (ref was not updating UI)
421
+ const [lastResult, setLastResult] = useState(null);
422
+ const prevHerdIdRef = useRef(undefined);
421
423
  const lastAddedCursorRef = useRef(undefined);
422
- // Store hasMore/nextCursor from the last merged response (like dummy's currentResult) so loadMore
423
- // always has a usable nextCursor even when currentQuery has switched to the next request.
424
- const lastResultRef = useRef(null);
424
+ const pagesLengthRef = useRef(0);
425
425
  const currentQuery = useGetFeedInfiniteByHerdQuery({
426
426
  herdId,
427
427
  limit: options.limit || 20,
428
428
  cursor: currentCursor,
429
429
  supabase: options.supabase,
430
430
  }, { skip: !options.enabled || !herdId });
431
+ useEffect(() => {
432
+ pagesLengthRef.current = pages.length;
433
+ }, [pages.length]);
434
+ // Clear all state whenever herd id changes (including to/from undefined)
431
435
  useEffect(() => {
432
436
  if (prevHerdIdRef.current !== undefined &&
433
- prevHerdIdRef.current !== herdId &&
434
- options.enabled &&
435
- herdId) {
437
+ prevHerdIdRef.current !== herdId) {
436
438
  setPages([]);
437
439
  setCurrentCursor(null);
438
440
  lastAddedCursorRef.current = undefined;
439
- lastResultRef.current = null;
441
+ setLastResult(null);
440
442
  }
441
443
  prevHerdIdRef.current = herdId;
442
- }, [herdId, options.enabled]);
444
+ }, [herdId]);
443
445
  // When we request a new page (cursor changed), clear ref so we merge the new response
444
446
  useEffect(() => {
445
447
  lastAddedCursorRef.current = undefined;
@@ -447,19 +449,19 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
447
449
  useEffect(() => {
448
450
  if (!currentQuery.data || currentQuery.isLoading)
449
451
  return;
450
- // Don't skip the first page after reset: only skip when we already have pages for this cursor
451
- if (pages.length > 0 &&
452
+ // Match dummy: only skip merge when we already have pages and this cursor was already added as a full page
453
+ if (pagesLengthRef.current > 0 &&
452
454
  feedCursorEq(lastAddedCursorRef.current ?? null, currentCursor))
453
455
  return;
454
456
  const items = Array.isArray(currentQuery.data?.items)
455
457
  ? currentQuery.data.items
456
458
  : [];
457
459
  const limit = options.limit || 20;
458
- // Store hasMore/nextCursor from this response so loadMore can use it (like dummy's currentResult)
459
- lastResultRef.current = {
460
+ // Match dummy: set state so UI re-renders with hasMore and loadMore sees latest nextCursor
461
+ setLastResult({
460
462
  hasMore: currentQuery.data.hasMore ?? false,
461
463
  nextCursor: currentQuery.data.nextCursor ?? null,
462
- };
464
+ });
463
465
  setPages((prev) => {
464
466
  const existingPage = prev.find((p) => feedCursorEq(p.cursor, currentCursor));
465
467
  if (!existingPage) {
@@ -475,18 +477,17 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
475
477
  });
476
478
  }, [currentQuery.data, currentQuery.isLoading, currentCursor, pages.length, options.limit]);
477
479
  const loadMore = useCallback(() => {
478
- const result = lastResultRef.current;
479
- if (result?.hasMore &&
480
- result.nextCursor != null &&
480
+ if (lastResult?.hasMore &&
481
+ lastResult.nextCursor != null &&
481
482
  !currentQuery.isLoading) {
482
- setCurrentCursor(result.nextCursor);
483
+ setCurrentCursor(lastResult.nextCursor);
483
484
  }
484
- }, [currentQuery.isLoading]);
485
+ }, [lastResult, currentQuery.isLoading]);
485
486
  const refetch = useCallback(() => {
486
487
  setPages([]);
487
488
  setCurrentCursor(null);
488
489
  lastAddedCursorRef.current = undefined;
489
- lastResultRef.current = null;
490
+ setLastResult(null);
490
491
  currentQuery.refetch();
491
492
  }, [currentQuery]);
492
493
  const allItems = useMemo(() => {
@@ -503,7 +504,7 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
503
504
  items: allItems,
504
505
  isLoading: currentQuery.isLoading && pages.length === 0,
505
506
  isLoadingMore: currentQuery.isLoading && pages.length > 0,
506
- hasMore: (lastResultRef.current?.hasMore ??
507
+ hasMore: (lastResult?.hasMore ??
507
508
  (currentCursor !== null && pages.length > 0)) ??
508
509
  false,
509
510
  loadMore,
@@ -514,27 +515,30 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
514
515
  export const useInfiniteFeedByDevice = (deviceId, options) => {
515
516
  const [pages, setPages] = useState([]);
516
517
  const [currentCursor, setCurrentCursor] = useState(null);
517
- const prevDeviceIdRef = useRef();
518
+ const [lastResult, setLastResult] = useState(null);
519
+ const prevDeviceIdRef = useRef(undefined);
518
520
  const lastAddedCursorRef = useRef(undefined);
519
- const lastResultRef = useRef(null);
521
+ const pagesLengthRef = useRef(0);
520
522
  const currentQuery = useGetFeedInfiniteByDeviceQuery({
521
523
  deviceId,
522
524
  limit: options.limit || 20,
523
525
  cursor: currentCursor,
524
526
  supabase: options.supabase,
525
527
  }, { skip: !options.enabled || !deviceId });
528
+ useEffect(() => {
529
+ pagesLengthRef.current = pages.length;
530
+ }, [pages.length]);
531
+ // Clear all state whenever device id changes (including to/from undefined)
526
532
  useEffect(() => {
527
533
  if (prevDeviceIdRef.current !== undefined &&
528
- prevDeviceIdRef.current !== deviceId &&
529
- options.enabled &&
530
- deviceId) {
534
+ prevDeviceIdRef.current !== deviceId) {
531
535
  setPages([]);
532
536
  setCurrentCursor(null);
533
537
  lastAddedCursorRef.current = undefined;
534
- lastResultRef.current = null;
538
+ setLastResult(null);
535
539
  }
536
540
  prevDeviceIdRef.current = deviceId;
537
- }, [deviceId, options.enabled]);
541
+ }, [deviceId]);
538
542
  // When we request a new page (cursor changed), clear ref so we merge the new response
539
543
  useEffect(() => {
540
544
  lastAddedCursorRef.current = undefined;
@@ -542,9 +546,13 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
542
546
  useEffect(() => {
543
547
  if (!currentQuery.data || currentQuery.isLoading)
544
548
  return;
545
- if (pages.length > 0 &&
549
+ if (pagesLengthRef.current > 0 &&
546
550
  feedCursorEq(lastAddedCursorRef.current ?? null, currentCursor))
547
551
  return;
552
+ setLastResult({
553
+ hasMore: currentQuery.data?.hasMore ?? false,
554
+ nextCursor: currentQuery.data?.nextCursor ?? null,
555
+ });
548
556
  setPages((prev) => {
549
557
  const existingPage = prev.find((p) => feedCursorEq(p.cursor, currentCursor));
550
558
  if (!existingPage) {
@@ -555,10 +563,6 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
555
563
  if (items.length >= limit) {
556
564
  lastAddedCursorRef.current = currentCursor;
557
565
  }
558
- lastResultRef.current = {
559
- hasMore: currentQuery.data?.hasMore ?? false,
560
- nextCursor: currentQuery.data?.nextCursor ?? null,
561
- };
562
566
  return [
563
567
  ...prev,
564
568
  { cursor: currentCursor, data: items },
@@ -568,18 +572,17 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
568
572
  });
569
573
  }, [currentQuery.data, currentQuery.isLoading, currentCursor, pages.length, options.limit]);
570
574
  const loadMore = useCallback(() => {
571
- const result = lastResultRef.current;
572
- if (result?.hasMore &&
573
- result.nextCursor != null &&
575
+ if (lastResult?.hasMore &&
576
+ lastResult.nextCursor != null &&
574
577
  !currentQuery.isLoading) {
575
- setCurrentCursor(result.nextCursor);
578
+ setCurrentCursor(lastResult.nextCursor);
576
579
  }
577
- }, [currentQuery.isLoading]);
580
+ }, [lastResult, currentQuery.isLoading]);
578
581
  const refetch = useCallback(() => {
579
582
  setPages([]);
580
583
  setCurrentCursor(null);
581
584
  lastAddedCursorRef.current = undefined;
582
- lastResultRef.current = null;
585
+ setLastResult(null);
583
586
  currentQuery.refetch();
584
587
  }, [currentQuery]);
585
588
  const allItems = useMemo(() => {
@@ -596,7 +599,7 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
596
599
  items: allItems,
597
600
  isLoading: currentQuery.isLoading && pages.length === 0,
598
601
  isLoadingMore: currentQuery.isLoading && pages.length > 0,
599
- hasMore: (lastResultRef.current?.hasMore ??
602
+ hasMore: (lastResult?.hasMore ??
600
603
  (currentCursor !== null && pages.length > 0)) ??
601
604
  false,
602
605
  loadMore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.4.24",
3
+ "version": "1.4.27",
4
4
  "description": "Core utilities and helpers for Adventure Labs Scout applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",