fix(ui): keep Spotify errors within scroll budget
This commit is contained in:
@@ -38,7 +38,7 @@ func (m *Model) handleSpotSearchInputKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
m.closeSpotSearch()
|
||||
case tea.KeyEnter:
|
||||
if m.spotSearch.query == "" {
|
||||
m.spotSearch.err = "Enter a search query."
|
||||
m.setSpotSearchError("Enter a search query.")
|
||||
return nil
|
||||
}
|
||||
if !m.spotSearch.loading {
|
||||
@@ -123,7 +123,7 @@ func (m *Model) handleSpotSearchResultsKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
// The playlist picker adds one track; an album is many, and Spotify
|
||||
// has no single call to add a whole record.
|
||||
if track.IsAlbum() {
|
||||
m.spotSearch.err = "Open the album with Enter, then add tracks from the queue."
|
||||
m.setSpotSearchError("Open the album with Enter, then add tracks from the queue.")
|
||||
return nil
|
||||
}
|
||||
m.spotSearch.selTrack = track
|
||||
@@ -166,13 +166,20 @@ func (m *Model) spotSearchBusy() bool {
|
||||
return m.spotSearch.loading || m.spotSearch.albumLoading
|
||||
}
|
||||
|
||||
func (m *Model) setSpotSearchError(message string) {
|
||||
m.spotSearch.err = message
|
||||
if m.spotSearch.screen == spotSearchResults {
|
||||
m.spotSearchResultsMaybeAdjustScroll(m.spotSearchResultsVisible())
|
||||
}
|
||||
}
|
||||
|
||||
// expandSpotAlbum fetches the tracks of the selected album placeholder. The
|
||||
// overlay stays open while it runs: closing it would bump the request
|
||||
// generation and drop the response.
|
||||
func (m *Model) expandSpotAlbum(album playlist.Track, action spotAlbumAction) tea.Cmd {
|
||||
loader, ok := m.spotSearch.prov.(provider.AlbumTrackLoader)
|
||||
if !ok {
|
||||
m.spotSearch.err = "This provider cannot open albums."
|
||||
m.setSpotSearchError("This provider cannot open albums.")
|
||||
return nil
|
||||
}
|
||||
m.spotSearch.albumLoading = true
|
||||
@@ -249,7 +256,7 @@ func (m *Model) handleSpotSearchNewNameKey(msg tea.KeyPressMsg) tea.Cmd {
|
||||
m.spotSearchPlaylistMaybeAdjustScroll(m.spotSearchPlaylistVisible())
|
||||
case tea.KeyEnter:
|
||||
if strings.TrimSpace(m.spotSearch.newName) == "" {
|
||||
m.spotSearch.err = "Playlist name is required."
|
||||
m.setSpotSearchError("Playlist name is required.")
|
||||
return nil
|
||||
}
|
||||
if !m.spotSearch.loading {
|
||||
|
||||
@@ -300,7 +300,11 @@ func (m *Model) spotSearchResultsHelpLine() string {
|
||||
}
|
||||
|
||||
func (m *Model) spotSearchResultsVisible() int {
|
||||
return m.effectivePlaylistVisible()
|
||||
visible := m.effectivePlaylistVisible()
|
||||
if m.spotSearch.err != "" {
|
||||
visible--
|
||||
}
|
||||
return max(0, visible)
|
||||
}
|
||||
|
||||
func (m *Model) spotSearchPlaylistHelpLine() string {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -54,6 +55,35 @@ func TestSpotSearchErrorFitsBodyBudget(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpotSearchErrorKeepsCursorVisible(t *testing.T) {
|
||||
m := newLayoutTestModel(80, 18)
|
||||
m.spotSearch.visible = true
|
||||
m.spotSearch.screen = spotSearchResults
|
||||
m.recomputeLayout()
|
||||
visible := m.effectivePlaylistVisible()
|
||||
if visible < 3 {
|
||||
t.Fatalf("body rows = %d, want at least 3", visible)
|
||||
}
|
||||
for i := range visible - 1 {
|
||||
m.spotSearch.results = append(m.spotSearch.results, trackResult(fmt.Sprintf("Track %d", i)))
|
||||
}
|
||||
m.spotSearch.cursor = len(m.spotSearch.results) - 1
|
||||
|
||||
m.setSpotSearchError("Search failed")
|
||||
|
||||
resultRows := m.spotSearchResultsVisible()
|
||||
if resultRows != visible-1 {
|
||||
t.Fatalf("result rows = %d, want %d", resultRows, visible-1)
|
||||
}
|
||||
if rows := spotSearchRowsToCursor(m.spotSearch.results, m.spotSearch.scroll, m.spotSearch.cursor); rows > resultRows {
|
||||
t.Fatalf("cursor sits %d rows below scroll %d, result window is %d", rows, m.spotSearch.scroll, resultRows)
|
||||
}
|
||||
selected := m.spotSearch.results[m.spotSearch.cursor].Title
|
||||
if body := stripAnsi(m.renderSpotSearchBody()); !strings.Contains(body, selected) {
|
||||
t.Fatalf("body = %q, want selected result %q", body, selected)
|
||||
}
|
||||
}
|
||||
|
||||
func trackResult(name string) playlist.Track {
|
||||
return playlist.Track{Title: name, Artist: "NOFX"}
|
||||
}
|
||||
|
||||
+6
-6
@@ -723,7 +723,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.spotSearch.cursor = 0
|
||||
m.spotSearch.scroll = 0
|
||||
if msg.err != nil {
|
||||
m.spotSearch.err = msg.err.Error()
|
||||
m.setSpotSearchError(msg.err.Error())
|
||||
return m, nil
|
||||
}
|
||||
m.spotSearch.results = msg.tracks
|
||||
@@ -739,11 +739,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
m.spotSearch.albumLoading = false
|
||||
if msg.err != nil {
|
||||
m.spotSearch.err = msg.err.Error()
|
||||
m.setSpotSearchError(msg.err.Error())
|
||||
return m, nil
|
||||
}
|
||||
if len(msg.tracks) == 0 {
|
||||
m.spotSearch.err = "That album has no tracks available here."
|
||||
m.setSpotSearchError("That album has no tracks available here.")
|
||||
return m, nil
|
||||
}
|
||||
album := msg.album
|
||||
@@ -766,7 +766,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.spotSearch.cursor = 0
|
||||
m.spotSearch.scroll = 0
|
||||
if msg.err != nil {
|
||||
m.spotSearch.err = msg.err.Error()
|
||||
m.setSpotSearchError(msg.err.Error())
|
||||
return m, nil
|
||||
}
|
||||
m.spotSearch.playlists = msg.playlists
|
||||
@@ -783,7 +783,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.cancelSpotRequest()
|
||||
m.spotSearch.loading = false
|
||||
if msg.err != nil {
|
||||
m.spotSearch.err = "Add failed: " + msg.err.Error()
|
||||
m.setSpotSearchError("Add failed: " + msg.err.Error())
|
||||
return m, nil
|
||||
}
|
||||
m.status.Showf(statusTTLDefault, "Added to %q", msg.name)
|
||||
@@ -797,7 +797,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.cancelSpotRequest()
|
||||
m.spotSearch.loading = false
|
||||
if msg.err != nil {
|
||||
m.spotSearch.err = "Create failed: " + msg.err.Error()
|
||||
m.setSpotSearchError("Create failed: " + msg.err.Error())
|
||||
return m, nil
|
||||
}
|
||||
m.status.Showf(statusTTLDefault, "Created %q & added track", msg.name)
|
||||
|
||||
Reference in New Issue
Block a user