Skip to content

Commit e128d8f

Browse files
wesmclaude
andauthored
Make escape key clear filters one layer at a time (#111)
## Summary - When both hide-addressed and project filter are active, escape now clears only the project filter first - Pressing escape again clears the hide-addressed filter - Fixes visual flash where "No jobs matching filters" briefly appeared while loading after clearing a filter - now shows "Loading..." instead ## Test plan - [x] Enable hide-addressed filter (`h`) - [x] Apply a project filter (`f` → select repo) - [x] Press escape → verify only project filter clears, hide-addressed remains active - [x] Press escape again → verify hide-addressed clears - [x] Verify no "No jobs matching filters" flash during filter clearing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c552730 commit e128d8f

File tree

2 files changed

+336
-5
lines changed

2 files changed

+336
-5
lines changed

cmd/roborev/tui.go

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ type tuiModel struct {
9999
hasMore bool // true if there are more jobs to load
100100
loadingMore bool // true if currently loading more jobs (pagination)
101101
loadingJobs bool // true if currently loading jobs (full refresh)
102+
pendingRefetch bool // true if filter changed while loading, needs refetch when done
102103
heightDetected bool // true after first WindowSizeMsg (real terminal height known)
103104

104105
// Filter modal state
@@ -1588,16 +1589,34 @@ func (m tuiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
15881589
}
15891590

15901591
case "esc":
1591-
if m.currentView == tuiViewQueue && (len(m.activeRepoFilter) > 0 || m.hideAddressed) {
1592-
// Clear filters and refetch all jobs
1592+
if m.currentView == tuiViewQueue && len(m.activeRepoFilter) > 0 {
1593+
// Clear project filter first (keep hide-addressed if active)
15931594
m.activeRepoFilter = nil
1595+
m.jobs = nil
1596+
m.hasMore = false
1597+
m.selectedIdx = -1
1598+
m.selectedJobID = 0
1599+
// If already loading (full refresh or pagination), queue a refetch
1600+
// to avoid out-of-order responses mixing stale data
1601+
if m.loadingJobs || m.loadingMore {
1602+
m.pendingRefetch = true
1603+
return m, nil
1604+
}
1605+
m.loadingJobs = true
1606+
return m, m.fetchJobs()
1607+
} else if m.currentView == tuiViewQueue && m.hideAddressed {
1608+
// Clear hide-addressed filter (no project filter active)
15941609
m.hideAddressed = false
1595-
// Reset to default view (clear jobs so fetchJobs uses appropriate limit)
15961610
m.jobs = nil
15971611
m.hasMore = false
1598-
// Invalidate selection until refetch completes
15991612
m.selectedIdx = -1
16001613
m.selectedJobID = 0
1614+
// If already loading (full refresh or pagination), queue a refetch
1615+
// to avoid out-of-order responses mixing stale data
1616+
if m.loadingJobs || m.loadingMore {
1617+
m.pendingRefetch = true
1618+
return m, nil
1619+
}
16011620
m.loadingJobs = true
16021621
return m, m.fetchJobs()
16031622
} else if m.currentView == tuiViewReview {
@@ -1652,6 +1671,15 @@ func (m tuiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
16521671
if !msg.append {
16531672
m.loadingJobs = false
16541673
}
1674+
1675+
// If filter changed while this fetch was in flight, discard stale data
1676+
// and trigger a fresh fetch with the current filter state
1677+
if m.pendingRefetch {
1678+
m.pendingRefetch = false
1679+
m.loadingJobs = true
1680+
return m, m.fetchJobs()
1681+
}
1682+
16551683
m.hasMore = msg.hasMore
16561684

16571685
// Update display name cache for new jobs
@@ -1932,10 +1960,24 @@ func (m tuiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
19321960
m.err = msg.err
19331961
m.loadingJobs = false // Clear loading state so refreshes can resume
19341962

1963+
// If filter changed while loading, retry immediately with current filter state
1964+
if m.pendingRefetch {
1965+
m.pendingRefetch = false
1966+
m.loadingJobs = true
1967+
return m, m.fetchJobs()
1968+
}
1969+
19351970
case tuiPaginationErrMsg:
19361971
m.err = msg.err
19371972
m.loadingMore = false // Clear loading state so user can retry pagination
19381973

1974+
// If filter changed while pagination was in flight, trigger fresh fetch
1975+
if m.pendingRefetch {
1976+
m.pendingRefetch = false
1977+
m.loadingJobs = true
1978+
return m, m.fetchJobs()
1979+
}
1980+
19391981
case tuiErrMsg:
19401982
m.err = msg
19411983
}
@@ -2022,7 +2064,10 @@ func (m tuiModel) renderQueueView() string {
20222064
end := 0
20232065

20242066
if len(visibleJobList) == 0 {
2025-
if len(m.activeRepoFilter) > 0 || m.hideAddressed {
2067+
if m.loadingJobs || m.loadingMore || m.pendingRefetch {
2068+
b.WriteString("Loading...")
2069+
b.WriteString("\x1b[K\n")
2070+
} else if len(m.activeRepoFilter) > 0 || m.hideAddressed {
20262071
b.WriteString("No jobs matching filters")
20272072
b.WriteString("\x1b[K\n")
20282073
} else {

0 commit comments

Comments
 (0)