feat: async loading UX with spinners - #11
Conversation
- Startup: STS GetCallerIdentity runs in background, service list shows immediately - ResourceBrowser: spinner animation during loading states - DetailView: async refresh for extended details, immediate navigation
This comment was marked as resolved.
This comment was marked as resolved.
- Log error on DetailView refresh failure - Add spinner.Tick to tab/shift+tab resource type switching
This comment was marked as resolved.
This comment was marked as resolved.
- Add Supports(OpGet) guard before calling dao.Get() in DetailView - Extract IsEscKey() helper to eliminate ESC detection duplication - Add ui.NewSpinner() helper for consistent spinner styling - Fix incorrect comment on buildTable() function - Show 'AWS initializing...' in status bar during init - Remove unnecessary t.Logf in tests
This comment was marked as resolved.
This comment was marked as resolved.
- Add refreshErr field to DetailView, display in status line - Verify ServiceBrowser handles empty accountID (already ok) - Add tests for IsEscKey, NewSpinner, DetailView refresh/init
This comment was marked as resolved.
This comment was marked as resolved.
- AWS InitContext now has 5s timeout (awsInitTimeout const) to avoid hang - Add defensive nil check for d.resource in refreshResource()
00d1176 to
a56a58c
Compare
This comment was marked as duplicate.
This comment was marked as duplicate.
This comment was marked as resolved.
This comment was marked as resolved.
- Add debug log when AWS init fails - Add tests for awsContextReadyMsg success/timeout - Add comment explaining rune 27 (raw ESC byte) check
Code Review: Async Loading UX with SpinnersSummaryThis PR successfully improves perceived performance by making AWS API calls non-blocking and adding visual loading indicators. The implementation is well-structured with good test coverage. ✅ StrengthsArchitecture & Design
Code Quality
🔍 Issues & Recommendations1. Race Condition in DetailView Initialization (Medium Priority)Location: internal/view/detail_view.go:81-86 func (d *DetailView) Init() tea.Cmd {
if d.dao != nil && d.dao.Supports(dao.OpGet) {
d.refreshing = true
return tea.Batch(d.spinner.Tick, d.refreshResource)
}
return nil
}Issue: Recommendation: This is likely safe in practice since Bubbletea processes messages sequentially, but consider documenting this assumption or ensuring 2. Spinner Continues After Error (Low Priority)Location: internal/view/detail_view.go:97-111 case detailRefreshMsg:
d.refreshing = false
if msg.err != nil {
log.Warn("failed to refresh resource details", "error", msg.err)
d.refreshErr = msg.err
} else {
d.refreshErr = nil
d.resource = msg.resource
// Re-render content with refreshed data
if d.ready {
content := d.renderContent()
d.viewport.SetContent(content)
}
}
return d, nilIssue: When refresh fails, spinner stops but no visual indication except "⚠ refresh failed" in status line. Users might miss this. Recommendation: Consider adding a transient error message or more prominent error indication. 3. Context Timeout Not Propagated Consistently (Medium Priority)Location: internal/app/app.go:106-110 initAWSCmd := func() tea.Msg {
ctx, cancel := context.WithTimeout(a.ctx, awsInitTimeout)
defer cancel()
err := aws.InitContext(ctx)
return awsContextReadyMsg{err: err}
}Good: Timeout added for AWS init. Issue: The timeout is only applied to AWS initialization. What about resource loading in Recommendation: Consider adding timeouts to other AWS operations or document why they're not needed (e.g., AWS SDK has built-in timeouts). 4. Missing Nil Check in DetailView (Low Priority)Location: internal/view/detail_view.go:91-96 func (d *DetailView) refreshResource() tea.Msg {
if d.dao == nil || d.resource == nil {
return detailRefreshMsg{resource: d.resource}
}
refreshed, err := d.dao.Get(d.ctx, d.resource.GetID())
if err != nil {
return detailRefreshMsg{resource: d.resource, err: err}
}
return detailRefreshMsg{resource: refreshed}
}Issue: Check for Recommendation: Return an error or handle the nil case more explicitly. 5. Test Coverage Gap (Low Priority)Location: internal/view/view_test.go:621-638 Observation: Tests cover DAO with/without
Recommendation: Add integration tests for these edge cases. 6. Comment Cleanup (Cosmetic)Location: internal/view/resource_browser.go:537 -// handleNavigation checks if a key matches a navigation shortcut
+// buildTable rebuilds the table with current filtered resources
func (r *ResourceBrowser) buildTable() {Issue: Comment update doesn't match function. Looks like incorrect comment replacement. Recommendation: Remove or correct the comment. 7. Potential Memory Leak (Low Priority)Location: Multiple spinner.Tick commands Observation: Spinners start ticking in Recommendation: Ensure ticker stops on all error paths. Current implementation appears correct but worth verifying. 🎯 Performance ConsiderationsPositive
Potential Concerns
Recommendation: Monitor performance with slow networks or rate-limited AWS accounts. 🔒 Security Considerations✅ No security issues identified
📋 Test Coverage AssessmentExcellent coverage with 38 new test cases: ✅ app_test.go: Tests AWS init success/failure paths
Missing: Integration tests for concurrent operations, but unit test coverage is solid. 🎨 Code Style & Conventions✅ Follows repository patterns:
💡 Suggestions for Future Enhancement
✅ RecommendationAPPROVE with minor suggestions This PR delivers meaningful UX improvements with solid engineering. The identified issues are minor and don't block merging. Consider addressing the race condition comment and context timeout propagation in a follow-up if needed. Great work on the comprehensive test coverage and maintaining backward compatibility! 🎉 Review completed by Claude Code |
Summary
Changes
⠋ Loading...) during list loadingCloses #7