Improve UI responsiveness during widget refresh
Problem
When a widget refreshes (either on a timer or via the r keyboard shortcut), the UI can become unresponsive while waiting for API calls to complete. This is because Refresh() runs synchronously — the widget's scheduler goroutine (or the tview event loop, in the case of keyboard-triggered refresh) is blocked on network I/O until all HTTP calls finish.
This is noticeable on widgets that make multiple or slow API calls (e.g. GitHub, NewRelic, GitLab, Jira).
Proposed approach
1. Run keyboard-triggered refresh in a background goroutine
Currently, pressing r calls widget.Refresh() directly on the tview input handler goroutine. Wrapping this in a goroutine would keep the UI responsive:
widget.InitializeRefreshKeyboardControl(func() {
go widget.Refresh()
})This could be done once in KeyboardWidget.InitializeRefreshKeyboardControl() so all widgets benefit.
2. Add a context.WithTimeout at the scheduler level
Individual modules shouldn't need to manage their own timeouts. app.Schedule() could wrap each Refresh() call with a deadline:
ctx, cancel := context.WithTimeout(context.Background(), refreshTimeout)
defer cancel()
widget.Refresh(ctx)This would require updating the Wtfable interface to accept a context, which is a larger change but would protect against any single module hanging the scheduler.
3. Ensure content() functions are always CPU-only
Establish as a project convention that the function passed to Redraw() should never do network I/O. All data fetching should happen in Refresh(), with results cached for content() to read. This was addressed for the GitHub widget in #2117 but other modules (e.g. NewRelic) have the same pattern.
Modules affected
A quick scan shows these modules make HTTP calls without timeouts or have network calls in their display path:
- NewRelic —
content()makes HTTP calls directly - DigitalOcean — keyboard handlers call API methods synchronously
- Transmission — keyboard handlers call API methods synchronously
- PagerDuty, AzureDevOps, DevTo, Gerrit — use
context.Background()without timeouts
Impact
This is a quality-of-life improvement. The app remains functional but feels sluggish when widgets are refreshing, especially with slow or rate-limited APIs.
Source: wtfutil/wtf