This repository was archived by the owner on Sep 10, 2026. It is now read-only.
Repository navigation
Follow the server's opaque pagination cursors - #39
Merged
Merged
Conversation
The engine now returns an opaque `nextCursor` on every paged list, null exactly
when the list is exhausted. Repositories return a `GamesPage` record carrying
both, and the paging screens hand the token straight back without opening it.
This removes two pieces of server knowledge the client had no business holding:
- the sort order. history_screen reconstructed the server's ORDER BY as
`'${last.finishedAt ?? last.updatedAt}'`, a copy of a rule the server owns,
kept in sync by hand.
- where the list ends. `lastPage.length < pageSize` is a guess, and it is
wrong exactly when the final page is full: the reader gets a spinner and a
request that returns nothing.
Both screens now route every refresh through a single `_refresh()`, because the
cursor lives beside the paging controller and the two must be reset together.
Refreshing without clearing the cursor refetches page one and then continues
from wherever the last scroll had reached. history has three refresh
affordances and lobby has five (toolbar, pull-to-refresh, error retry, and on
lobby a game being joined or cancelled), so this was five copies of two lines
waiting to drift.
Adds copy for the new `invalidCursor` code. A cursor is echoed, never composed,
so a user can neither cause nor fix one; it means the list needs restarting
from the top, which is what a refresh does.
Requires eigen_api with `nextCursor` (eigen-server#60).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
seenu-k
marked this pull request as ready for review
August 12, 2026 15:23
The engine's 0.4.0 line returns `nextCursor` on every paged response and takes `cursor` as an opaque string. A shell resolving 0.3.x gets `int? cursor` and no `nextCursor`, so it cannot read this wire at all. Also records the pagination work in the changelog, which the previous commit should have done. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The changelog headings are link references and CI checks that each one has a matching definition, which a hand-written '## [Unreleased]' does not get. cider maintains those definitions, and the escaping, which is the reason the header says to use it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
activeGames, playerPublicFinishedGames and friendsGames became async when they started unwrapping a GamesPage, which changes their generated hashes. The build_runner + git-diff drift guard is what catches this; I had not re-run it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Client half of eigeninteractive/eigen-server#60. Draft: blocked until that releases.
Blocked on
pubspec.yamlpinseigen_api: ^0.3.0. The published client at that version hasint? cursorand nonextCursor, so CI cannot pass. It builds locally only becausepubspec_overrides.yaml(gitignored) repointseigen_apiat the sibling eigen-server checkout.To unblock: merge eigen-server#60 → release → bump the constraint here to the released
eigen_api→ mark ready.Changes
The engine now returns an opaque
nextCursoron every paged list, null exactly when the list is exhausted. Repositories return aGamesPagerecord carrying both, and the paging screens hand the token straight back without opening it.That removes two pieces of server knowledge the client had no business holding:
history_screenreconstructed the server'sORDER BYas'${last.finishedAt ?? last.updatedAt}'— a hand-maintained copy of a rule the server owns.lastPage.length < pageSizeis a guess, and it's wrong exactly when the final page is full: the reader gets a spinner and a request that returns nothing.A latent bug this fixes
Both screens now route every refresh through a single
_refresh(). The cursor lives beside the paging controller, so the two must be reset together — refreshing without clearing the cursor refetches page one and then continues paging from wherever the last scroll had reached.This matters more than it looks.
lobby_screenmerged cleanly against the web-design refactor, which had added five refresh affordances (toolbar, pull-to-refresh, error retry, and a game being joined or cancelled). None of them reset the cursor. A clean merge is exactly how that hides.Also adds copy for the new
invalidCursorcode. A cursor is echoed, never composed, so a user can neither cause nor fix one — it means the list needs restarting from the top, which is what a refresh does.Verification
280 tests pass and the analyzer is clean, against the local override. Neither is meaningful for CI until the constraint is bumped.
🤖 Generated with Claude Code