Skip to content

Give the thread view the HEY apps' More menu - #358

Open
jwahdatehagh wants to merge 2 commits into
basecamp:mainfrom
jwahdatehagh:tui-trash-conversation
Open

Give the thread view the HEY apps' More menu#358
jwahdatehagh wants to merge 2 commits into
basecamp:mainfrom
jwahdatehagh:tui-trash-conversation

Conversation

@jwahdatehagh

@jwahdatehagh jwahdatehagh commented Aug 28, 2026

Copy link
Copy Markdown

In the TUI there was no way to trash (or label, move, forward) a thread without first going back to the list. This adds the More menu the HEY apps have to the thread view.

What it does

  • m over an open thread swaps the help bar for the menu: f forward • b label • v move • t trash • esc/q back. The thread stays on screen, and every thread key keeps its meaning while the menu is up (j/k, attachments, r, scrolling all route through the thread's own key handling).
  • The actions aim at the posting the thread was opened from — captured when the thread is requested, so a live re-read carrying the row out of the list (opening marks it seen, and the refreshed head page may no longer reach it) cannot disarm the menu.
  • Trashing (or moving) the open thread closes the thread view once the server confirms, landing the reader back on the list with the row already gone — search results and bundles included, since those lists never re-read themselves.
  • Over account-wide search results and a contact's threads the menu keeps to forward and trash: those lists say neither which box a thread lives in nor which labels it carries, so label/move pickers built over them would offer wrong choices.

Edge cases covered (each with a test)

  • The thread only auto-closes under an idle reader. A reply form or picker opened while the trash is in flight survives, typed text intact; a reply/forward context still loading for the closed thread is cancelled so it cannot settle into a compose form over the list.
  • Leaving the thread with esc before the trash confirmation lands still removes the trashed row from search/bundle lists.
  • A failed trash reports the error and leaves the thread and its row untouched.
  • The menu re-reads the list's row when opening, so a label added moments earlier shows checked instead of being offered again.
  • Only lowercase m opens the menu: uppercase M is the global Mail-section shortcut and never reaches the thread.

make check passes.


Summary by cubic

Adds the HEY apps' More menu to the thread view, so m over an open thread swaps the help bar for Forward, Label, Move, and Trash (esc/q goes back) while the thread stays on screen.

  • The menu acts on the posting the thread was opened from, so it works even if a live refresh removes the row from the list.
  • Trashing or moving the open thread closes the thread view and removes the row from search and bundle lists once the server confirms.
  • A failed action leaves the thread and row untouched, and the error is reported.
  • Over search results and a contact's threads, only Forward and Trash are offered, since those lists don't reveal the thread's box or labels.
  • Label changes from the menu update the retained posting, so the reopened picker shows the new state checked instead of offering to file the label again.

Written for commit 6694a96. Summary will update on new commits.

Review in cubic

m over an open thread swaps the help bar for the More menu the HEY apps
have: Forward, Label, Move and Trash, each behind the key the same
action answers to on the list, q or esc to put the bar back. The thread
stays on screen and keeps scrolling underneath. The actions aim at the
posting the thread was opened from, so they work wherever the thread
came from - a box, search results, a bundle or Previously Seen.

Trashing (or moving) the open thread closes the thread view once the
server has confirmed it, landing the reader back on the list they came
from with the row already gone.
@jwahdatehagh
jwahdatehagh requested a review from a team as a code owner August 28, 2026 13:34
Copilot AI balanced review requested due to automatic review settings August 28, 2026 13:34

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.

Pull request overview

Adds a thread-level More menu to perform posting actions without returning to the mail list.

Changes:

  • Adds forward, label, move, and trash actions to open threads.
  • Preserves posting context across live refreshes and handles action completion.
  • Adds extensive More-menu tests.

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 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
internal/tui/more_menu.go Implements the More menu and key routing.
internal/tui/mail.go Integrates thread actions, posting snapshots, and completion handling.
internal/tui/mail_test.go Tests menu behavior and edge cases.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/tui/mail.go
Comment on lines +2390 to +2394
func (v *mailView) menuOrganizes() bool {
if v.searchActive {
return false
}
return !v.bundleActive || v.bundleContactID == 0
Comment thread internal/tui/mail.go
Comment on lines +2379 to +2383
if live := v.openedPosting(v.topicPosting.ID); live != nil {
posting := *live
v.topicPosting = &posting
}
v.openModal(newMoreMenu(*v.topicPosting, v.menuOrganizes()))
The More menu builds its label picker off the posting the thread was
opened from, and that snapshot is the only copy left once a live re-read
has carried the row out of the list - which is the whole reason it is
kept. A label filed or removed from the menu never reached it: the
completion re-read the box list and nothing else. Reopening the picker
then showed a label the thread already carries as unchecked, and enter
filed it a second time instead of removing it.

The completion now carries the posting and the label it moved, the way
the collection one already does, and lays that change onto every copy
still in hand - the thread's retained posting and the row on whichever
list holds it, which also covers the bundle and Previously Seen lists
that are never re-read. A created label is known only by its name until
the source reload hands back its ID, so a label without one matches on
its name.

Tests: labelling from the menu with the row gone leaves the reopened
picker offering to remove the label rather than file it again, for both
a filed and a freshly created one. Two more cover the menu over bundles,
which nothing reached before - label and move offered inside an unseen
bundle, withheld over a contact's threads.
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