Add F keybinding to flush the mail queue via postqueue -f

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-03-29 19:45:40 +07:00
commit 9ba845e5f4
3 changed files with 34 additions and 2 deletions

View file

@ -48,6 +48,7 @@ go install github.com/nemunaire/mqv@latest
| `↑` / `↓` | Navigate messages | | `↑` / `↓` | Navigate messages |
| `Enter` | Open message | | `Enter` | Open message |
| `r` | Refresh queue | | `r` | Refresh queue |
| `F` | Flush queue (`postqueue -f`) |
| `q` | Quit | | `q` | Quit |
Subjects are fetched lazily in parallel via `postcat` as the list loads; a progress bar tracks completion. Subjects are fetched lazily in parallel via `postcat` as the list loads; a progress bar tracks completion.
@ -59,6 +60,7 @@ Subjects are fetched lazily in parallel via `postcat` as the list loads; a progr
| `↑↓` / `Space` / `PgUp` / `PgDn` | Scroll | | `↑↓` / `Space` / `PgUp` / `PgDn` | Scroll |
| `H` | Toggle full headers / short headers | | `H` | Toggle full headers / short headers |
| `s` | Save raw EML to `~/QUEUEID.eml` | | `s` | Save raw EML to `~/QUEUEID.eml` |
| `F` | Requeue message (`postsuper -r`) |
| `v` | Browse MIME parts | | `v` | Browse MIME parts |
| `q` | Back to queue list | | `q` | Back to queue list |

View file

@ -210,6 +210,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.list.SetItems(nil) m.list.SetItems(nil)
_ = m.progress.SetPercent(0) _ = m.progress.SetPercent(0)
return m, loadQueueCmd() return m, loadQueueCmd()
case "F":
m.entries = nil
m.entryIndex = nil
m.loadingTotal = 0
m.loadingDone = 0
m.list.SetItems(nil)
_ = m.progress.SetPercent(0)
return m, flushQueueCmd()
case "enter": case "enter":
if item, ok := m.list.SelectedItem().(queueItem); ok { if item, ok := m.list.SelectedItem().(queueItem); ok {
return m, loadMessageCmd(item.entry.ID) return m, loadMessageCmd(item.entry.ID)
@ -254,6 +262,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.refreshViewport() m.refreshViewport()
m.viewport.GotoTop() m.viewport.GotoTop()
return m, nil return m, nil
case "F":
return m, requeueMessageCmd(m.currentID)
case "v": case "v":
m.parts = extractParts(m.currentRaw) m.parts = extractParts(m.currentRaw)
m.partsCursor = 0 m.partsCursor = 0
@ -384,7 +394,7 @@ func (m Model) View() string {
headersHint = "H: short headers" headersHint = "H: short headers"
} }
status := statusBarStyle.Render( status := statusBarStyle.Render(
fmt.Sprintf(" ↑↓/SPC/PgUp/Dn: scroll │ s: save EML │ v: parts │ %s │ q: back │ %d%% ", headersHint, scrollPct), fmt.Sprintf(" ↑↓/SPC/PgUp/Dn: scroll │ s: save EML │ F: requeue │ v: parts │ %s │ q: back │ %d%% ", headersHint, scrollPct),
) )
notice := "" notice := ""
if m.messageSaving { if m.messageSaving {
@ -464,7 +474,7 @@ func (m Model) renderBottom() string {
return dimStyle.Render(label) + "\n " + m.progress.View() return dimStyle.Render(label) + "\n " + m.progress.View()
} }
status := statusBarStyle.Render( status := statusBarStyle.Render(
fmt.Sprintf(" %d message(s) │ Enter: open │ r: refresh │ q: quit ", len(m.list.Items())), fmt.Sprintf(" %d message(s) │ Enter: open │ r: refresh │ F: flush │ q: quit ", len(m.list.Items())),
) )
if item, ok := m.list.SelectedItem().(queueItem); ok { if item, ok := m.list.SelectedItem().(queueItem); ok {
if reason := item.entry.Reason; reason != "" { if reason := item.entry.Reason; reason != "" {

20
msgs.go
View file

@ -1,6 +1,8 @@
package main package main
import ( import (
"os/exec"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
) )
@ -34,6 +36,24 @@ func loadQueueCmd() tea.Cmd {
} }
} }
func flushQueueCmd() tea.Cmd {
return func() tea.Msg {
exec.Command("postqueue", "-f").Run()
entries, err := loadQueue()
if err != nil {
return queueErrMsg{err}
}
return queueParsedMsg{entries}
}
}
func requeueMessageCmd(id string) tea.Cmd {
return func() tea.Msg {
exec.Command("postsuper", "-r", id).Run()
return nil
}
}
func fetchSubjectCmd(id string) tea.Cmd { func fetchSubjectCmd(id string) tea.Cmd {
return func() tea.Msg { return func() tea.Msg {
raw, err := fetchHeaders(id) raw, err := fetchHeaders(id)