mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-09-01 16:48:19 +00:00
routes: keep paging when client-side region filtering starves the table
A multi-IATA region filters globally-paged rows client-side, and scroll
was the only trigger for the next page — an empty or too-short filtered
list could never fire it, so routes deeper in the cursor stream were
unreachable ('No routes' with routes that exist). Pull pages until the
region has a screenful or the cap calls it sparse.
This commit is contained in:
@@ -169,7 +169,7 @@ export function RouteTable() {
|
||||
|
||||
// Page the route set on demand (50 at a time, cursor = last route's lastSeen ms) — the DataTable
|
||||
// pulls the next page via loadMore() as you scroll, instead of eagerly loading the whole set.
|
||||
const { items: listRoutes, loadedCount, isPaging, isError, isLoading: listLoading, loadMore } =
|
||||
const { items: listRoutes, loadedCount, isPaging, isError, isLoading: listLoading, loadMore, hasMore } =
|
||||
useInfinitePages<KnownRoute>({
|
||||
queryKey: ["routes", serverIata ?? ""],
|
||||
queryFn: (cursor) => getKnownRoutesPage({ iata: serverIata, cursor }),
|
||||
@@ -223,6 +223,16 @@ export function RouteTable() {
|
||||
[rows, selectedKey],
|
||||
);
|
||||
|
||||
// A multi-IATA region filters globally-paged rows client-side, so the filtered list can be too
|
||||
// short to ever trigger scroll paging — or empty, with the region's routes deeper in the cursor
|
||||
// stream. Keep pulling pages until there's a screenful or the cap says the region is just sparse.
|
||||
useEffect(() => {
|
||||
if (search || serverIata || !iatas?.length) return;
|
||||
if (!hasMore || isPaging) return;
|
||||
if ((rows?.length ?? 0) >= 50 || loadedCount >= 1000) return;
|
||||
loadMore();
|
||||
}, [search, serverIata, iatas, hasMore, isPaging, rows, loadedCount, loadMore]);
|
||||
|
||||
const canSearch = !!(from.trim() && to.trim() && searchIatas.length >= 1);
|
||||
// clear any selection when the visible list changes out from under it (search submit/clear)
|
||||
const submitSearch = useCallback(() => {
|
||||
|
||||
@@ -30,11 +30,11 @@ const mockGetRegions = vi.mocked(getRegions);
|
||||
|
||||
const node = (id: string, name: string) => ({ id, name, publicKey: "deadbeef" });
|
||||
|
||||
function renderTable() {
|
||||
function renderTable(selection = ALL_REGIONS) {
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<QueryClientProvider client={client}>
|
||||
<RegionProvider defaultSelection={ALL_REGIONS}>{children}</RegionProvider>
|
||||
<RegionProvider defaultSelection={selection}>{children}</RegionProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
render(<RouteTable />, { wrapper });
|
||||
@@ -103,6 +103,25 @@ describe("RouteTable search", () => {
|
||||
expect(screen.getByText("Dst Node")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps paging for a multi-IATA region until its routes surface", async () => {
|
||||
const foreign: KnownRoute = { id: 1, iata: "CCC", hopCount: 1, hops: [], firstSeen: 1, lastSeen: 5, observationCount: 9 };
|
||||
const wanted: KnownRoute = { id: 2, iata: "AAA", hopCount: 2, hops: [], firstSeen: 1, lastSeen: 3, observationCount: 17 };
|
||||
// first global page has nothing from the region; the region's route sits on page two
|
||||
mockGetKnownRoutesPage.mockImplementation(({ cursor } = {}) =>
|
||||
Promise.resolve(
|
||||
cursor === undefined
|
||||
? { items: [foreign], nextCursor: 5, hasMore: true }
|
||||
: { items: [wanted], nextCursor: null, hasMore: false },
|
||||
),
|
||||
);
|
||||
|
||||
renderTable({ regions: [], iatas: ["AAA", "BBB"] });
|
||||
|
||||
// without fill-paging the table dead-ends on "No routes" — scroll can never fire on an empty list
|
||||
expect(await screen.findByText("17")).toBeInTheDocument();
|
||||
expect(mockGetKnownRoutesPage.mock.calls.length).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
|
||||
it("shows a route's observation count in the list", async () => {
|
||||
const route: KnownRoute = {
|
||||
id: 7,
|
||||
|
||||
Reference in New Issue
Block a user