Trash the thread you are reading with t - #349
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds t/T support for trashing an open mail thread and returning to its originating list.
Changes:
- Tracks the posting used to open a thread.
- Adds thread-view trash handling and help.
- Tests list, search, direct-ID, and Previously Seen behavior.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
internal/tui/mail.go |
Adds open-thread trash behavior and posting tracking. |
internal/tui/mail_test.go |
Tests trash actions and help visibility. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| return nil | ||
| } | ||
| postingID := v.topicPostingID | ||
| cmd := v.doPostingAction("Thread moved to Trash", postingActionRemove, v.currentBoxID(), postingID, func() error { |
The trash key only worked on a list, so a thread you had opened could not be trashed without leaving it first, and the help bar in a thread never offered the key at all. The web app binds t directly on the topic toolbar, so it trashes from an open thread. t and T now file the open thread the way a and l already do, through the same posting action the list uses. The key acts on the posting the thread was opened from rather than wherever the list's cursor has since landed. Over search results, bundles and topics opened by id there is no row to file and the key says so, which is the rule the other filing keys follow. Trash also closes the thread, where Set Aside and Reply Later leave it open in the box it landed in. The Trash is not a box you file out of, and the web app returns to the list too. Closes basecamp#339
monorkin
force-pushed
the
trash-open-thread
branch
from
September 8, 2026 05:39
dabb874 to
8939779
Compare
Collaborator
|
Thanks for the PR @badta5te ! |
monorkin
added a commit
that referenced
this pull request
Sep 8, 2026
A thread on screen answered to a and l. The web app's topic toolbar keeps every filing key live while a topic is open, so u, i, d, p, b and v now work there too. The moves needed wiring rather than writing: postingAction has handled all four since the list gained them, and the thread's key switch simply never named them. It has no default the way the list's does, so anything unnamed reaches the viewport and scrolls. The two pickers needed more. Both read the list's selection, which is not the thread's row once the automatic mark-seen has resorted it under the cover, so they take the posting from actionPosting and answer to fileablePosting's rule: a thread with no row to file gets the notice the filing keys already give rather than a picker aimed at whatever the cursor has landed on. Marking unseen needed the snapshot kept in step. Opening an unseen thread marks it seen, and the snapshot is taken before that lands, so u measured against a stale row and said the thread it had just marked seen was already unseen. Left out, because the web leaves them out of an open thread too: spam, ignore, and collections. Mark seen is left out because opening the thread has already done it. Trash is #349. Closes #358
monorkin
added a commit
that referenced
this pull request
Sep 8, 2026
A thread on screen answered to a, l and t. The web app's topic toolbar keeps every filing key live while a topic is open, so u, i, d, p, b and v now work there too. The moves needed wiring rather than writing: postingAction has handled all four since the list gained them, and the thread's key switch simply never named them. It has no default the way the list's does, so anything unnamed reaches the viewport and scrolls. The two pickers needed more. Both read the list's selection, which is not the thread's row once the automatic mark-seen has resorted it under the cover, so they take the posting from actionPosting and answer to fileablePosting's rule: a thread with no row to file gets the notice the filing keys already give rather than a picker aimed at whatever the cursor has landed on. Marking unseen needed the snapshot kept in step. Opening an unseen thread marks it seen, and the snapshot is taken before that lands, so u measured against a stale row and said the thread it had just marked seen was already unseen. Left out, because the web leaves them out of an open thread too: spam, ignore, and collections. Mark seen is left out because opening the thread has already done it. Trash landed separately in #349. Closes #358
monorkin
added a commit
that referenced
this pull request
Sep 8, 2026
A thread on screen answered to a, l and t. The web app's topic toolbar keeps every filing key live while a topic is open, so u, i, d, p, b and v now work there too. The moves needed wiring rather than writing: postingAction has handled all four since the list gained them, and the thread's key switch simply never named them. It has no default the way the list's does, so anything unnamed reaches the viewport and scrolls. The two pickers needed more. Both read the list's selection, which is not the thread's row once the automatic mark-seen has resorted it under the cover, so they take the posting from actionPosting and answer to fileablePosting's rule: a thread with no row to file gets the notice the filing keys already give rather than a picker aimed at whatever the cursor has landed on. Marking unseen needed the snapshot kept in step. Opening an unseen thread marks it seen, and the snapshot is taken before that lands, so u measured against a stale row and said the thread it had just marked seen was already unseen. Left out, because the web leaves them out of an open thread too: spam, ignore, and collections. Mark seen is left out because opening the thread has already done it. Trash landed separately in #349. Closes #358
monorkin
added a commit
that referenced
this pull request
Sep 8, 2026
The filing keys refused a thread opened from a bundle, a contact's threads or a search. That is the customer report behind the card, and the customer is right that all three file fine on the web. The web manages it because its toolbar reads the posting's own box — posting.box.kind, off the row rather than off the page you came from. HEY serves that box to every client: postings/_object.jbuilder writes box_id on every posting, and the SDK's generated type has carried BoxId all along. mail.Posting was dropping it on the way through, so the TUI had nothing to ask and fell back to asking the list. A list can draw rows from several boxes at once — a search, a label, a collection, a contact's threads — so the list's box was the wrong thing to ask. Filing measured "already in Paper Trail" against whatever box sat behind the results, and the fix for that was to refuse those lists outright rather than to ask the row. So: carry BoxID through, take the box from the posting, and the refusals go with it. Only a topic opened by its id has no row and nothing to file. The same lookup fixes filing from a label or a collection list, where the source carries no box kind at all and the already-in guard could never have fired. A filed row also leaves whichever list was drawn over the box list. Nothing re-reads a search's results or a bundle's threads — both are drawn once when they open — so a row left behind stays there offering to file a thread that has already moved. That is the second commit of #349, which I dropped while rebasing it as unreachable; this is what makes it reachable.
monorkin
added a commit
that referenced
this pull request
Sep 8, 2026
The filing keys refused a thread opened from a bundle, a contact's threads or a search. That is the customer report behind the card, and the customer is right that all three file fine on the web. The web manages it because its toolbar reads the posting's own box — posting.box.kind, off the row rather than off the page you came from. HEY serves that box to every client: postings/_object.jbuilder writes box_id on every posting, and the SDK's generated type has carried BoxId all along. mail.Posting was dropping it on the way through, so the TUI had nothing to ask and fell back to asking the list. A list can draw rows from several boxes at once — a search, a label, a collection, a contact's threads — so the list's box was the wrong thing to ask. Filing measured "already in Paper Trail" against whatever box sat behind the results, and the fix for that was to refuse those lists outright rather than to ask the row. So: carry BoxID through, take the box from the posting, and the refusals go with it. Only a topic opened by its id has no row and nothing to file. The same lookup fixes filing from a label or a collection list, where the source carries no box kind at all and the already-in guard could never have fired. A filed row also leaves whichever list was drawn over the box list. Nothing re-reads a search's results or a bundle's threads — both are drawn once when they open — so a row left behind stays there offering to file a thread that has already moved. That is the second commit of #349, which I dropped while rebasing it as unreachable; this is what makes it reachable.
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The trash key only worked on a list, so a thread you had opened could not be trashed without first leaving it, and the help bar in a thread never offered the key at all. The web app trashes from an open thread.
tandTnow trash the open thread and return to the list it came from. The posting is the one the thread was opened from rather than the list's selection: a thread opened out of search results or a bundle leaves the box list's cursor on a different email. A topic opened by its id carries no posting, and HEY trashes postings, so the key says so instead.A completed removal also drops the row from the search results or the bundle the thread was opened from. Those lists were navigate-only until this key — no action could reach a row in them — so nothing had left them stale before; without it the thread would go to the Trash while its row stayed listed, ready to be reopened or trashed again.
Fixes #339
Summary by cubic
Adds
t/Tto trash the thread you're reading, returning to the list it came from. The key uses the posting the thread was opened from (not the list's cursor), and the thread help bar now offers it; threads opened by id carry no posting, so the key declines with a notice. Trashing also removes the thread's row from the search results, bundle, or Previously Seen list it was opened from.Fixes #339.
Written for commit 8939779. Summary will update on new commits.