A native feed list and item list #3

Closed
opened 2026-09-19 16:56:08 -07:00 by rays · 1 comment
Owner

The part where native actually beats the page. A real library is ten thousand items and a hundred and thirty-five feeds, and a list view with cell reuse handles that in a way a DOM table does not.

Blocked by #1 for the data and #2 for the material and palette it draws in.

The feed list is the sidebar: places first (Directory, Popular, Currently Listening, All Subscriptions), then feeds with their art, counts and unread badges, and OPML folders as groups. The item list is the table: title, feed, kind, size and date, sortable by any of them, filtered by all, unread, downloaded or pinned, and searchable. GET /api/entries takes sort, dir, filter and q and pages fifty at a time, so the sorting and searching stay the server's work.

Read and pinned are written through the flags route and should be optimistic, the way the page's readWrites map is: a list asked for before the write lands answers with the old state, which put the dot back on an item just read.

This is the step that decides whether the rest is worth it. If the native list does not feel better than the page at ten thousand rows, stop here and keep the web view.

The part where native actually beats the page. A real library is ten thousand items and a hundred and thirty-five feeds, and a list view with cell reuse handles that in a way a DOM table does not. **Blocked by #1** for the data and **#2** for the material and palette it draws in. The feed list is the sidebar: places first (Directory, Popular, Currently Listening, All Subscriptions), then feeds with their art, counts and unread badges, and OPML folders as groups. The item list is the table: title, feed, kind, size and date, sortable by any of them, filtered by all, unread, downloaded or pinned, and searchable. `GET /api/entries` takes `sort`, `dir`, `filter` and `q` and pages fifty at a time, so the sorting and searching stay the server's work. Read and pinned are written through the flags route and should be optimistic, the way the page's `readWrites` map is: a list asked for before the write lands answers with the old state, which put the dot back on an item just read. This is the step that decides whether the rest is worth it. If the native list does not feel better than the page at ten thousand rows, stop here and keep the web view.
rays added the enhancement label 2026-09-19 16:56:08 -07:00
rays added a new dependency 2026-09-19 16:58:57 -07:00
rays added a new dependency 2026-09-19 16:58:57 -07:00
rays added a new dependency 2026-09-19 16:58:58 -07:00
rays added a new dependency 2026-09-19 16:58:58 -07:00
Author
Owner

Done in 01d8bd9.

LibraryStore holds a page and asks for the next — sorting, filtering and searching stay the server's work, and eleven thousand items have no business being in memory to be sorted here. Read and pinned are set locally and sent after, as the page's readWrites map does and for the same reason.

FeedListView is the sidebar: places first, then feeds with art, counts and unread badges, OPML subscriptions as folders. ItemListView is the table with the filter tabs, sort menu, search and paging by the last row. Swipe to mark read or pin.

Tested against the real library — 135 feeds, eleven thousand items — and it scrolls the way a list view does rather than the way a DOM table does. The playback test now drives the native row instead of the page's button. Thirteen pass.

Two things worth recording:

  • The interface follows the account's light or dark, not the phone's, and defaults to dark when nobody has chosen, because that is what theme.ts does. Following the system put a light list in front of a dark page. preferredColorScheme was not enough inside a hosting controller; overrideUserInterfaceStyle on the controller is, and it carries to the page presented over it.
  • The sidebar had to be split into sub-views — the whole list in one expression was more than the type checker would work through, and it said so rather than compiling it.
Done in 01d8bd9. `LibraryStore` holds a page and asks for the next — sorting, filtering and searching stay the server's work, and eleven thousand items have no business being in memory to be sorted here. Read and pinned are set locally and sent after, as the page's `readWrites` map does and for the same reason. `FeedListView` is the sidebar: places first, then feeds with art, counts and unread badges, OPML subscriptions as folders. `ItemListView` is the table with the filter tabs, sort menu, search and paging by the last row. Swipe to mark read or pin. Tested against the real library — 135 feeds, eleven thousand items — and it scrolls the way a list view does rather than the way a DOM table does. The playback test now drives the native row instead of the page's button. Thirteen pass. Two things worth recording: - The interface follows the **account's** light or dark, not the phone's, and defaults to dark when nobody has chosen, because that is what `theme.ts` does. Following the system put a light list in front of a dark page. `preferredColorScheme` was not enough inside a hosting controller; `overrideUserInterfaceStyle` on the controller is, and it carries to the page presented over it. - The sidebar had to be split into sub-views — the whole list in one expression was more than the type checker would work through, and it said so rather than compiling it.
rays closed this issue 2026-09-20 06:27:17 -07:00
Sign in to join this conversation.