diff --git a/README.md b/README.md index d442ff9..23f84fe 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ disabled by default, and local-only. - Shows deterministic per-author colors and read-only reaction counts. - Folds resolved threads by default and distinguishes unread or updated local state. +- After resolving the selected thread, keeps the cursor nearby by selecting + the next thread, or the previous thread when resolving the last one. - Supports fuzzy path search; whitespace-separated terms may match separate portions of the same path. - Supports configurable status and within-status ordering. diff --git a/tui.go b/tui.go index d9d8bc5..1373b04 100644 --- a/tui.go +++ b/tui.go @@ -966,6 +966,13 @@ func (m App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.threadIndex >= 0 && m.threadIndex < len(m.details.Threads) { selected = m.details.Threads[m.threadIndex].ID } + if msg.thread.IsResolved && selected == msg.threadID { + if m.threadIndex+1 < len(m.details.Threads) { + selected = m.details.Threads[m.threadIndex+1].ID + } else if m.threadIndex > 0 { + selected = m.details.Threads[m.threadIndex-1].ID + } + } for index := range m.details.Threads { if m.details.Threads[index].ID != msg.threadID { continue diff --git a/tui_test.go b/tui_test.go index cfa6d1e..a35d76d 100644 --- a/tui_test.go +++ b/tui_test.go @@ -1470,6 +1470,45 @@ func TestResolveToggleConfirmsAndUsesCurrentThreadState(t *testing.T) { } } +func TestResolvingSelectedThreadKeepsSelectionNearItsPreviousPosition(t *testing.T) { + tests := []struct { + name string + selected int + wantID string + }{ + {name: "next thread", selected: 1, wantID: "c"}, + {name: "previous thread at end", selected: 2, wantID: "b"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + m := NewApp(nil, "o", "r", false, 50, time.Second) + m.screen, m.loading, m.focus = threadScreen, false, threadDetailPane + m.details.Threads = []ReviewThread{ + {ID: "a", Path: "a.go"}, + {ID: "b", Path: "b.go"}, + {ID: "c", Path: "c.go"}, + } + m.threadIndex = test.selected + resolvedID := m.details.Threads[test.selected].ID + + updated, _ := m.Update(threadResolvedMsg{ + threadID: resolvedID, + thread: ReviewThread{ + ID: resolvedID, IsResolved: true, ViewerCanUnresolve: true, + }, + }) + m = updated.(App) + + if got := m.details.Threads[m.threadIndex].ID; got != test.wantID { + t.Fatalf("selected thread = %q, want nearest thread %q", got, test.wantID) + } + if m.focus != threadDetailPane { + t.Fatalf("focus = %d, want detail pane", m.focus) + } + }) + } +} + func TestPollingMarksNewThreadCommentsUnread(t *testing.T) { m := NewApp(nil, "o", "r", false, 50, 10*time.Second) m.screen, m.width, m.height = threadScreen, 80, 8 diff --git a/version.go b/version.go index 5e4cd98..6e5c57b 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,3 @@ package main -const dipleVersion = "0.3.0" +const dipleVersion = "0.3.1"