fix: auto-refresh history tabs every 5 minutes

History tabs were loaded once at first visit and never updated,
causing 1 Day/Week/Month to show fewer requests than the live view.

- Silent background refresh every 5 min for the active history tab
- Re-check on tab switch: reload if data is older than 5 min
- Spinner only on first load; subsequent refreshes keep old data visible
- Chart title shows "updated X ago" so freshness is always visible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SW 2026-06-11 14:26:08 +02:00
parent 1501b15a9d
commit da6a128882

View file

@ -83,12 +83,15 @@ var (
// -- history state ------------------------------------------------------------
const historyRefreshInterval = 5 * time.Minute
type historyState struct {
entries []*parser.LogEntry
stats *parser.Stats
buckets []parser.Bucket
loaded bool
loading bool
loadedAt time.Time
err error
}
@ -234,7 +237,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
case tickMsg:
return m, tea.Batch(tick(), drainLines(m.lineCh), fetchSysStats(m.client))
cmds := []tea.Cmd{tick(), drainLines(m.lineCh), fetchSysStats(m.client)}
if m.activeTab != tabLive {
if cmd := m.maybeRefreshHistory(m.activeTab); cmd != nil {
cmds = append(cmds, cmd)
}
}
return m, tea.Batch(cmds...)
case newLineMsg:
if e := parser.ParseLine(string(msg)); e != nil {
@ -254,6 +263,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
h := m.history[idx]
h.loading = false
h.loaded = true
h.loadedAt = time.Now()
h.err = msg.err
if msg.err == nil {
h.entries = msg.entries
@ -270,10 +280,30 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m *model) ensureHistory(tab viewTab) tea.Cmd {
h := m.history[int(tab)-1]
if h.loaded || h.loading {
if h.loading {
return nil
}
if !h.loaded {
h.loading = true // first load: show spinner
return loadHistory(m.client, m.logPath, tab)
}
if time.Since(h.loadedAt) > historyRefreshInterval {
// silent refresh: keep showing old data while new data loads
return loadHistory(m.client, m.logPath, tab)
}
return nil
}
// maybeRefreshHistory triggers a silent background reload for the active history
// tab if its data is older than historyRefreshInterval.
func (m *model) maybeRefreshHistory(tab viewTab) tea.Cmd {
h := m.history[int(tab)-1]
if h.loading || !h.loaded {
return nil
}
if time.Since(h.loadedAt) < historyRefreshInterval {
return nil
}
h.loading = true
return loadHistory(m.client, m.logPath, tab)
}
@ -383,7 +413,7 @@ func (m model) renderHistory(idx int, label string) string {
h := m.history[idx]
if h.loading {
return lipgloss.NewStyle().Padding(2, 4).
Render(styleWarn.Render("⏳ Loading historical data…"))
Render(styleWarn.Render("Loading historical data…"))
}
if !h.loaded {
return lipgloss.NewStyle().Padding(2, 4).Render(styleDim.Render("No data loaded."))
@ -397,8 +427,12 @@ func (m model) renderHistory(idx int, label string) string {
Render(styleDim.Render("No log entries found for this period."))
}
// Append "updated X ago" to label so the chart title reflects freshness
age := time.Since(h.loadedAt).Round(time.Second)
labelWithAge := fmt.Sprintf("%s — updated %s ago", label, age)
tab := viewTab(idx + 1)
chart := m.renderBarChart(h.buckets, tabBucketSize(tab), label, h.stats)
chart := m.renderBarChart(h.buckets, tabBucketSize(tab), labelWithAge, h.stats)
bottom := lipgloss.JoinHorizontal(lipgloss.Top,
m.renderPeriodStats(h.stats),
m.renderTopHosts(h.stats),