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:
parent
1501b15a9d
commit
da6a128882
1 changed files with 45 additions and 11 deletions
|
|
@ -83,12 +83,15 @@ var (
|
||||||
|
|
||||||
// -- history state ------------------------------------------------------------
|
// -- history state ------------------------------------------------------------
|
||||||
|
|
||||||
|
const historyRefreshInterval = 5 * time.Minute
|
||||||
|
|
||||||
type historyState struct {
|
type historyState struct {
|
||||||
entries []*parser.LogEntry
|
entries []*parser.LogEntry
|
||||||
stats *parser.Stats
|
stats *parser.Stats
|
||||||
buckets []parser.Bucket
|
buckets []parser.Bucket
|
||||||
loaded bool
|
loaded bool
|
||||||
loading bool
|
loading bool
|
||||||
|
loadedAt time.Time
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,7 +237,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case tickMsg:
|
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:
|
case newLineMsg:
|
||||||
if e := parser.ParseLine(string(msg)); e != nil {
|
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 := m.history[idx]
|
||||||
h.loading = false
|
h.loading = false
|
||||||
h.loaded = true
|
h.loaded = true
|
||||||
|
h.loadedAt = time.Now()
|
||||||
h.err = msg.err
|
h.err = msg.err
|
||||||
if msg.err == nil {
|
if msg.err == nil {
|
||||||
h.entries = msg.entries
|
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 {
|
func (m *model) ensureHistory(tab viewTab) tea.Cmd {
|
||||||
h := m.history[int(tab)-1]
|
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
|
return nil
|
||||||
}
|
}
|
||||||
h.loading = true
|
|
||||||
return loadHistory(m.client, m.logPath, tab)
|
return loadHistory(m.client, m.logPath, tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,7 +413,7 @@ func (m model) renderHistory(idx int, label string) string {
|
||||||
h := m.history[idx]
|
h := m.history[idx]
|
||||||
if h.loading {
|
if h.loading {
|
||||||
return lipgloss.NewStyle().Padding(2, 4).
|
return lipgloss.NewStyle().Padding(2, 4).
|
||||||
Render(styleWarn.Render("⏳ Loading historical data…"))
|
Render(styleWarn.Render("Loading historical data…"))
|
||||||
}
|
}
|
||||||
if !h.loaded {
|
if !h.loaded {
|
||||||
return lipgloss.NewStyle().Padding(2, 4).Render(styleDim.Render("No data 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."))
|
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)
|
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,
|
bottom := lipgloss.JoinHorizontal(lipgloss.Top,
|
||||||
m.renderPeriodStats(h.stats),
|
m.renderPeriodStats(h.stats),
|
||||||
m.renderTopHosts(h.stats),
|
m.renderTopHosts(h.stats),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue