Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions app/internal-explorer/blocks/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ActiveBlockButton } from '../components/ActiveBlockButton';
import { explorerApi } from '../library/client';
import { formatInteger } from '../library/explorer-format';
import { explorerHref } from '../library/links';
import { nextShadowCursor } from '../library/shadow-pagination';
import type { BlocksResponse, ShadowBlockSummary } from '../library/types';
import { useExplorerChain } from '../library/useExplorerChain';
import { useShadowDelta } from '../library/useShadowDelta';
Expand Down Expand Up @@ -76,13 +77,13 @@ function BlocksContent() {
setShadowCandidates({});

explorerApi
.recentShadowBlocks(chain, { limit: PAGE_LIMIT }, controller.signal)
.recentShadowBlocks(chain, { limit: PAGE_LIMIT, before: cursor }, controller.signal)
.then(async (shadows) => {
if (cancelled) return;
if (shadows.length === 0) {
setData({
blocks: [],
page: { cursor: null, limit: PAGE_LIMIT, latestBlockNumber: 0, nextCursor: null, hasMore: false },
page: { cursor: cursor ?? null, limit: PAGE_LIMIT, latestBlockNumber: 0, nextCursor: null, hasMore: false },
});
return;
}
Expand All @@ -101,15 +102,17 @@ function BlocksContent() {
.map((shadow) => [shadow.canonicalHash!.toLowerCase(), [shadow]]),
);

const nextCursor = nextShadowCursor(shadows, PAGE_LIMIT);

setShadowCandidates(candidates);
setData({
blocks: orderedBlocks,
page: {
cursor: null,
cursor: cursor ?? null,
limit: PAGE_LIMIT,
latestBlockNumber: orderedBlocks[0]?.number ?? 0,
nextCursor: null,
hasMore: false,
nextCursor,
hasMore: nextCursor !== null,
},
});
})
Expand All @@ -125,7 +128,7 @@ function BlocksContent() {
cancelled = true;
controller.abort();
};
}, [chain, showShadowDelta]);
}, [chain, cursor, showShadowDelta]);

return (
<div className="animate-in flex flex-col gap-6">
Expand Down
31 changes: 31 additions & 0 deletions app/internal-explorer/library/shadow-pagination.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, expect, it } from 'vitest';

import { nextShadowCursor } from './shadow-pagination';

const PAGE_LIMIT = 25;

function shadowsEndingAt(oldest: number, count: number): { number: number }[] {
return Array.from({ length: count }, (_, index) => ({ number: oldest + count - 1 - index }));
}

describe('nextShadowCursor', () => {
it('returns the oldest block number when a full page is returned', () => {
const shadows = shadowsEndingAt(100, PAGE_LIMIT);
expect(shadows.at(-1)?.number).toBe(100);
expect(nextShadowCursor(shadows, PAGE_LIMIT)).toBe(100);
});

it('returns null on a short page (source exhausted)', () => {
const shadows = shadowsEndingAt(100, PAGE_LIMIT - 1);
expect(nextShadowCursor(shadows, PAGE_LIMIT)).toBeNull();
});

it('returns null when the oldest block is genesis (0)', () => {
const shadows = shadowsEndingAt(0, PAGE_LIMIT);
expect(nextShadowCursor(shadows, PAGE_LIMIT)).toBeNull();
});

it('returns null for an empty page', () => {
expect(nextShadowCursor([], PAGE_LIMIT)).toBeNull();
});
});
14 changes: 14 additions & 0 deletions app/internal-explorer/library/shadow-pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Pagination cursor for the shadow-delta block view. Shadow blocks come back
// newest-first; the API's `before` param is an *exclusive* block-number cutoff,
// so the next page starts at the oldest block returned (no overlap, no gap). A
// short page means the source is exhausted.
import type { ShadowBlockSummary } from './types';

export function nextShadowCursor(
shadows: Pick<ShadowBlockSummary, 'number'>[],
pageLimit: number,
): number | null {
if (shadows.length < pageLimit) return null;
const oldest = shadows.at(-1)?.number;
return oldest === undefined || oldest <= 0 ? null : oldest;
}
Loading