@adventurelabs/scout-core 1.4.22 → 1.4.24

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.
@@ -419,6 +419,9 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
419
419
  const [currentCursor, setCurrentCursor] = useState(null);
420
420
  const prevHerdIdRef = useRef();
421
421
  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);
422
425
  const currentQuery = useGetFeedInfiniteByHerdQuery({
423
426
  herdId,
424
427
  limit: options.limit || 20,
@@ -433,6 +436,7 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
433
436
  setPages([]);
434
437
  setCurrentCursor(null);
435
438
  lastAddedCursorRef.current = undefined;
439
+ lastResultRef.current = null;
436
440
  }
437
441
  prevHerdIdRef.current = herdId;
438
442
  }, [herdId, options.enabled]);
@@ -443,15 +447,25 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
443
447
  useEffect(() => {
444
448
  if (!currentQuery.data || currentQuery.isLoading)
445
449
  return;
446
- if (feedCursorEq(lastAddedCursorRef.current ?? null, currentCursor))
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
+ feedCursorEq(lastAddedCursorRef.current ?? null, currentCursor))
447
453
  return;
454
+ const items = Array.isArray(currentQuery.data?.items)
455
+ ? currentQuery.data.items
456
+ : [];
457
+ 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
+ hasMore: currentQuery.data.hasMore ?? false,
461
+ nextCursor: currentQuery.data.nextCursor ?? null,
462
+ };
448
463
  setPages((prev) => {
449
464
  const existingPage = prev.find((p) => feedCursorEq(p.cursor, currentCursor));
450
465
  if (!existingPage) {
451
- const items = Array.isArray(currentQuery.data?.items)
452
- ? currentQuery.data.items
453
- : [];
454
- lastAddedCursorRef.current = currentCursor;
466
+ if (items.length >= limit) {
467
+ lastAddedCursorRef.current = currentCursor;
468
+ }
455
469
  return [
456
470
  ...prev,
457
471
  { cursor: currentCursor, data: items },
@@ -459,18 +473,20 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
459
473
  }
460
474
  return prev;
461
475
  });
462
- }, [currentQuery.data, currentQuery.isLoading, currentCursor]);
476
+ }, [currentQuery.data, currentQuery.isLoading, currentCursor, pages.length, options.limit]);
463
477
  const loadMore = useCallback(() => {
464
- if (currentQuery.data?.hasMore &&
465
- currentQuery.data.nextCursor &&
478
+ const result = lastResultRef.current;
479
+ if (result?.hasMore &&
480
+ result.nextCursor != null &&
466
481
  !currentQuery.isLoading) {
467
- setCurrentCursor(currentQuery.data.nextCursor);
482
+ setCurrentCursor(result.nextCursor);
468
483
  }
469
- }, [currentQuery.data, currentQuery.isLoading]);
484
+ }, [currentQuery.isLoading]);
470
485
  const refetch = useCallback(() => {
471
486
  setPages([]);
472
487
  setCurrentCursor(null);
473
488
  lastAddedCursorRef.current = undefined;
489
+ lastResultRef.current = null;
474
490
  currentQuery.refetch();
475
491
  }, [currentQuery]);
476
492
  const allItems = useMemo(() => {
@@ -487,7 +503,7 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
487
503
  items: allItems,
488
504
  isLoading: currentQuery.isLoading && pages.length === 0,
489
505
  isLoadingMore: currentQuery.isLoading && pages.length > 0,
490
- hasMore: (currentQuery.data?.hasMore ??
506
+ hasMore: (lastResultRef.current?.hasMore ??
491
507
  (currentCursor !== null && pages.length > 0)) ??
492
508
  false,
493
509
  loadMore,
@@ -500,6 +516,7 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
500
516
  const [currentCursor, setCurrentCursor] = useState(null);
501
517
  const prevDeviceIdRef = useRef();
502
518
  const lastAddedCursorRef = useRef(undefined);
519
+ const lastResultRef = useRef(null);
503
520
  const currentQuery = useGetFeedInfiniteByDeviceQuery({
504
521
  deviceId,
505
522
  limit: options.limit || 20,
@@ -514,6 +531,7 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
514
531
  setPages([]);
515
532
  setCurrentCursor(null);
516
533
  lastAddedCursorRef.current = undefined;
534
+ lastResultRef.current = null;
517
535
  }
518
536
  prevDeviceIdRef.current = deviceId;
519
537
  }, [deviceId, options.enabled]);
@@ -524,7 +542,8 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
524
542
  useEffect(() => {
525
543
  if (!currentQuery.data || currentQuery.isLoading)
526
544
  return;
527
- if (feedCursorEq(lastAddedCursorRef.current ?? null, currentCursor))
545
+ if (pages.length > 0 &&
546
+ feedCursorEq(lastAddedCursorRef.current ?? null, currentCursor))
528
547
  return;
529
548
  setPages((prev) => {
530
549
  const existingPage = prev.find((p) => feedCursorEq(p.cursor, currentCursor));
@@ -532,7 +551,14 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
532
551
  const items = Array.isArray(currentQuery.data?.items)
533
552
  ? currentQuery.data.items
534
553
  : [];
535
- lastAddedCursorRef.current = currentCursor;
554
+ const limit = options.limit || 20;
555
+ if (items.length >= limit) {
556
+ lastAddedCursorRef.current = currentCursor;
557
+ }
558
+ lastResultRef.current = {
559
+ hasMore: currentQuery.data?.hasMore ?? false,
560
+ nextCursor: currentQuery.data?.nextCursor ?? null,
561
+ };
536
562
  return [
537
563
  ...prev,
538
564
  { cursor: currentCursor, data: items },
@@ -540,18 +566,20 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
540
566
  }
541
567
  return prev;
542
568
  });
543
- }, [currentQuery.data, currentQuery.isLoading, currentCursor]);
569
+ }, [currentQuery.data, currentQuery.isLoading, currentCursor, pages.length, options.limit]);
544
570
  const loadMore = useCallback(() => {
545
- if (currentQuery.data?.hasMore &&
546
- currentQuery.data.nextCursor &&
571
+ const result = lastResultRef.current;
572
+ if (result?.hasMore &&
573
+ result.nextCursor != null &&
547
574
  !currentQuery.isLoading) {
548
- setCurrentCursor(currentQuery.data.nextCursor);
575
+ setCurrentCursor(result.nextCursor);
549
576
  }
550
- }, [currentQuery.data, currentQuery.isLoading]);
577
+ }, [currentQuery.isLoading]);
551
578
  const refetch = useCallback(() => {
552
579
  setPages([]);
553
580
  setCurrentCursor(null);
554
581
  lastAddedCursorRef.current = undefined;
582
+ lastResultRef.current = null;
555
583
  currentQuery.refetch();
556
584
  }, [currentQuery]);
557
585
  const allItems = useMemo(() => {
@@ -568,7 +596,7 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
568
596
  items: allItems,
569
597
  isLoading: currentQuery.isLoading && pages.length === 0,
570
598
  isLoadingMore: currentQuery.isLoading && pages.length > 0,
571
- hasMore: (currentQuery.data?.hasMore ??
599
+ hasMore: (lastResultRef.current?.hasMore ??
572
600
  (currentCursor !== null && pages.length > 0)) ??
573
601
  false,
574
602
  loadMore,
package/dist/store/api.js CHANGED
@@ -455,7 +455,8 @@ export const scoutApi = createApi({
455
455
  };
456
456
  }
457
457
  const rows = data || [];
458
- const hasMore = rows.length > limit;
458
+ // Full page (rows.length >= limit) means there might be more; only signal hasMore when we have a nextCursor
459
+ const hasMore = rows.length >= limit;
459
460
  const resultRows = hasMore ? rows.slice(0, limit) : rows;
460
461
  const uniqueFilePaths = Array.from(new Set(resultRows.flatMap((row) => {
461
462
  const paths = [];
@@ -498,7 +499,7 @@ export const scoutApi = createApi({
498
499
  : null,
499
500
  }));
500
501
  const last = resultRows[resultRows.length - 1];
501
- const nextCursor = hasMore && last
502
+ const nextCursor = resultRows.length > 0
502
503
  ? {
503
504
  timestamp: last.sort_ts ?? "",
504
505
  id: last.sort_id ?? 0,
@@ -506,7 +507,11 @@ export const scoutApi = createApi({
506
507
  }
507
508
  : null;
508
509
  return {
509
- data: { items, nextCursor, hasMore },
510
+ data: {
511
+ items,
512
+ nextCursor,
513
+ hasMore: nextCursor != null && hasMore,
514
+ },
510
515
  };
511
516
  }
512
517
  catch (err) {
@@ -537,7 +542,8 @@ export const scoutApi = createApi({
537
542
  };
538
543
  }
539
544
  const rows = data || [];
540
- const hasMore = rows.length > limit;
545
+ // Full page (rows.length >= limit) means there might be more; only signal hasMore when we have a nextCursor
546
+ const hasMore = rows.length >= limit;
541
547
  const resultRows = hasMore ? rows.slice(0, limit) : rows;
542
548
  const uniqueFilePaths = Array.from(new Set(resultRows.flatMap((row) => {
543
549
  const paths = [];
@@ -580,7 +586,7 @@ export const scoutApi = createApi({
580
586
  : null,
581
587
  }));
582
588
  const last = resultRows[resultRows.length - 1];
583
- const nextCursor = hasMore && last
589
+ const nextCursor = resultRows.length > 0
584
590
  ? {
585
591
  timestamp: last.sort_ts ?? "",
586
592
  id: last.sort_id ?? 0,
@@ -588,7 +594,11 @@ export const scoutApi = createApi({
588
594
  }
589
595
  : null;
590
596
  return {
591
- data: { items, nextCursor, hasMore },
597
+ data: {
598
+ items,
599
+ nextCursor,
600
+ hasMore: nextCursor != null && hasMore,
601
+ },
592
602
  };
593
603
  }
594
604
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.4.22",
3
+ "version": "1.4.24",
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",