Skip to content

Open a bundle in the TUI as the threads inside it - #337

Merged
monorkin merged 6 commits into
mainfrom
bundle-open-tui
Aug 26, 2026
Merged

Open a bundle in the TUI as the threads inside it#337
monorkin merged 6 commits into
mainfrom
bundle-open-tui

Conversation

@monorkin

Copy link
Copy Markdown
Collaborator

Opening a bundle posting in the TUI failed with the resource-not-found error screen (or worse — see below). Now it opens: the bundle's unseen threads as a list, enter opens a member thread, esc steps back out.

The bug

A bundle's app_url names a topic only when the bundle holds exactly one unseen thread, so mail.TopicIDOf answers 0 for the rest. openSelected then substituted the posting id for a topic id and read /topics/{postingID} — a topic that almost never exists, hence the error screen. And since posting and topic ids share an integer space, a collision silently opened an unrelated thread, pointed in-thread reply at it, and marked the bundle seen. The same fallback sat in the list-view reply and forward handlers.

The fix

  • A bundle row with no topic opens the bundle: its unseen threads as a contentList layered over the box list the way search results are — its own grow-downward lane following the Link cursor, opened on the shared request lane so failures land on the error screen and stale answers are discarded. r/f on a bundle row open the bundle too. A single-unseen-thread bundle keeps opening its thread directly, as the web app does.
  • The posting-id fallback is gone everywhere. A non-bundle row that names no thread gets an honest error instead of a fabricated fetch.
  • Reads go through the SDK's new Postings().BundleUnseenPage (hey-sdk v0.26.0). Requires the HEY API to serve JSON for a bundle's unseen postings, which is deployed.

Closes the TUI half of #156; the CLI-command half is a follow-up now that the SDK operation exists.

Tests

New coverage for opening a bundle, growing it down its cursor, ignoring a stale append, reply-opens-bundle, and the refusal on a topicless non-bundle row. The shared posting fixtures never carried a TopicID — the old fallback was masking that — so they got real ones.

Note for CI

The nix vendorHash is stale after the SDK pin; the first CI run is expected to fail there and hand back the correct hash, which lands as a follow-up commit.

A bundle posting groups one contact's unseen mail, and its app_url names a
topic only when it holds exactly one unseen thread. For any other bundle the
TUI substituted the posting id for a topic id, which read a topic that does
not exist -- the resource-not-found error screen -- or, on an id collision,
silently opened an unrelated thread and marked the bundle seen.

Enter on a bundle row now opens the bundle: its unseen threads as a list
layered over the box list the way search results are, growing down its Link
cursor, with enter opening a member thread and esc stepping back out. Reply
and forward on a bundle row open the bundle too. The posting-id fallback is
gone everywhere; a non-bundle row that names no thread gets an honest error.

Reads go through the SDK's new Postings().BundleUnseenPage (hey-sdk v0.26.0),
which models GET /postings/{id}/bundles/unseen.
Copilot AI balanced review requested due to automatic review settings August 26, 2026 09:40
@monorkin
monorkin requested a review from a team as a code owner August 26, 2026 09:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

HEY's `bundled` means a posting is filed inside a bundle; the bundle row
itself is `kind: "bundle"`. Keying on the flag left every real bundle
refused as a row that names no thread. The TUI fixture now goes through
mail.NewPosting so the tests exercise the actual mapping.
HEY serves a bundle's unseen threads, so a bundle that has been read
through answers none. A bare (empty) list reads as a bug; say what it
means instead.
An unread bundle opens its unseen threads; a bundle that has been read
through now opens every thread with its contact -- where the web app sends
a read bundle -- instead of dead-ending on an empty unseen list. The list
is HEY's own, heading and all, read through Contacts().ThreadsPage
(hey-sdk v0.27.0), and pages down its Link cursor like every other list.
@monorkin
monorkin merged commit 116abff into main Aug 26, 2026
33 checks passed
@monorkin
monorkin deleted the bundle-open-tui branch August 26, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants