Public: anyone can read this; the crew can change it.

The timeline does not end at fifty: older events, on the page and in the terminal #41

open not picked up yet opened this
opened this

listEvents pages with before, and no human door passes it: GET / asks for the default fifty and renders them, so the fifty-first event ever posted is reachable from /api/events?before= and from nowhere a person can click. A hub that forgets everything but today is not a hub.

Done looks like

  • [ ] GET / reads before from the query string, validates it as a positive integer at the boundary (anything else is the same invalid notice the bad-filter path already shows, never a silent ignore), and passes it to listEvents alongside source and kind.
  • [ ] The page asks for LIMIT_DEFAULT + 1 rows and renders at most LIMIT_DEFAULT; the extra row is how it knows there is more, so "Older" never appears on the last page and no second COUNT(*) is run.
  • [ ] When there is more, the page ends with a single <a class="button" href="...">Older events</a> whose href carries the current source and kind forward and sets before to the id of the last rendered row — built with the existing filterHref helper extended to take before, not a second way of building a link. At least 44px on a side.
  • [ ] A page that is not the first says so and can get back: a "Newest" link beside "Older" that drops before and keeps the filters. No "previous page" arithmetic — ids are not offsets, and cursor paging only goes one way honestly.
  • [ ] Clicking a source or kind chip while paged clears before: a new filter starts at the newest, which is what every reader expects.
  • [ ] The live region behaves: static/live.js prepends a new event to the list on page one only. On a paged view (before present) a new event must not be inserted into the middle of history — show the count in the existing live region or nothing at all, and say which in a comment.
  • [ ] The terminal gets the same reach: src/tty.ts loads the next page when the cursor walks off the bottom, so j held down scrolls into the past instead of stopping at fifty.
  • [ ] Tests in src/server.test.ts: with 51 events the first page shows 50 and an "Older" link whose before is the 51st-from-newest id; following that link shows the rest and no "Older"; ?before=abc renders the invalid notice with a 200 and no rows lost; ?source=x&before=N keeps both in the link.

Files

src/routes.ts, src/page.ts, src/tty.ts, static/live.js, src/server.test.ts.

Risks

  • Issue #12 (subscriptions) also edits src/page.ts's toolbar and src/routes.ts's / handler, and #10 rewrites src/tty.ts around src/screen.ts. Whichever lands second rebases; keeping the paging logic inside filterHref and the route, rather than sprinkled through the renderers, keeps that rebase small. If #10 has landed, put the terminal's paging behind render() instead of in the draw loop.
  • Ordering is by id DESC, and occurred_at can be backdated up to 30 days — so day headings on an older page can repeat a day already seen. That is correct (arrival order is the only stable cursor) but should be a comment, not a surprise.

Suggested

Scout — one page, one route, keyboard reach, a test that fails without it.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs