From 3a3774f5b64844984b7140ea28c298e961533cfa Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Fri, 14 Feb 2025 17:15:03 -0300 Subject: [PATCH] add loading indicator and err/timeout toast to Album and Playlist pages (todo: other pages) --- res/translations/en.json | 2 ++ ui/browsing/albumpage.go | 18 ++++++++++++++++-- ui/browsing/playlistpage.go | 17 ++++++++++++++++- ui/widgets/tracklist.go | 14 +++++++++++++- ui/widgets/tracklistloader.go | 3 +++ 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/res/translations/en.json b/res/translations/en.json index 287fe17..7c2516d 100644 --- a/res/translations/en.json +++ b/res/translations/en.json @@ -17,6 +17,7 @@ "All": "All", "All Tracks": "All Tracks", "Alt. URL": "Alt. URL", + "An error occurred": "An error occurred", "An error occurred adding tracks to the playlist": "An error occurred adding tracks to the playlist", "An error occurred updating the playlist": "An error occurred updating the playlist", "Are you sure you want to delete the server": "Are you sure you want to delete the server", @@ -196,6 +197,7 @@ "Support the project": "Support the project", "Switch Servers": "Switch Servers", "Testing connection": "Testing connection", + "The request timed out": "The request timed out", "Theme": "Theme", "Time": "Time", "Title": "Title", diff --git a/ui/browsing/albumpage.go b/ui/browsing/albumpage.go index 453128d..7573eca 100644 --- a/ui/browsing/albumpage.go +++ b/ui/browsing/albumpage.go @@ -3,6 +3,7 @@ package browsing import ( "fmt" "log" + "strings" "github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend/mediaprovider" @@ -107,6 +108,7 @@ func newAlbumPage( container.New(&layout.CustomPaddedLayout{LeftPadding: 15, RightPadding: 15, TopPadding: 15, BottomPadding: 10}, a.header), nil, nil, nil, container.New(&layout.CustomPaddedLayout{LeftPadding: 15, RightPadding: 15, BottomPadding: 15}, a.tracklist)) + a.tracklist.SetLoading(true) go a.load() return a } @@ -117,6 +119,7 @@ func (a *AlbumPage) CreateRenderer() fyne.WidgetRenderer { func (a *AlbumPage) Save() SavedPage { a.disposed = true + a.tracklist.SetLoading(false) s := a.albumPageState s.sort = a.tracklist.Sorting() a.header.page = nil @@ -139,6 +142,7 @@ func (a *AlbumPage) OnSongChange(track mediaprovider.MediaItem, lastScrobbledIfA } func (a *AlbumPage) Reload() { + a.tracklist.SetLoading(true) go a.load() } @@ -162,13 +166,23 @@ func (a *AlbumPage) Scroll(scrollAmt float32) { func (a *AlbumPage) load() { album, err := a.mp.GetAlbum(a.albumID) if err != nil { - log.Printf("Failed to get album: %s", err.Error()) + msg := err.Error() + log.Printf("Failed to get album: %s", msg) + toastMsg := "An error occurred" + if strings.Contains(msg, "deadline exceeded") { + toastMsg = "The request timed out" + } + fyne.Do(func() { + a.tracklist.SetLoading(false) + a.contr.ToastProvider.ShowErrorToast(lang.L(toastMsg)) + }) return } if a.disposed { return } fyne.Do(func() { + a.tracklist.SetLoading(false) a.header.Update(album, a.im) a.tracklist.Options.ShowDiscNumber = len(album.Tracks) > 0 && album.Tracks[0].DiscNumber != album.Tracks[len(album.Tracks)-1].DiscNumber a.tracks = album.Tracks @@ -376,7 +390,7 @@ func formatMiscLabelStr(a *mediaprovider.AlbumWithTracks) string { } yearStr := util.FormatItemDate(a.Date) if y := a.ReissueDate.Year; y != nil && *y > a.YearOrZero() { - yearStr += fmt.Sprintf(" (%s %d)", lang.L("reissued"), util.FormatItemDate(a.ReissueDate)) + yearStr += fmt.Sprintf(" (%s %s)", lang.L("reissued"), util.FormatItemDate(a.ReissueDate)) } return fmt.Sprintf("%s · %d %s · %s%s", yearStr, a.TrackCount, tracks, discs, util.SecondsToTimeString(float64(a.Duration))) } diff --git a/ui/browsing/playlistpage.go b/ui/browsing/playlistpage.go index 9df0be0..01caba2 100644 --- a/ui/browsing/playlistpage.go +++ b/ui/browsing/playlistpage.go @@ -3,6 +3,7 @@ package browsing import ( "fmt" "log" + "strings" "github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend/mediaprovider" @@ -104,6 +105,8 @@ func newPlaylistPage( a.container = container.NewBorder( container.New(&layout.CustomPaddedLayout{LeftPadding: 15, RightPadding: 15, TopPadding: 15, BottomPadding: 10}, a.header), nil, nil, nil, container.New(&layout.CustomPaddedLayout{LeftPadding: 15, RightPadding: 15, BottomPadding: 15}, a.tracklist)) + + a.tracklist.SetLoading(true) go a.load() return a } @@ -114,6 +117,7 @@ func (a *PlaylistPage) CreateRenderer() fyne.WidgetRenderer { func (a *PlaylistPage) Save() SavedPage { a.disposed = true + a.tracklist.SetLoading(false) p := a.playlistPageState p.trackSort = a.tracklist.Sorting() p.widgetPool.Release(util.WidgetTypePlaylistPageHeader, a.header) @@ -136,6 +140,7 @@ func (a *PlaylistPage) OnSongChange(item mediaprovider.MediaItem, lastScrobbledI } func (a *PlaylistPage) Reload() { + a.tracklist.SetLoading(true) go a.load() } @@ -159,7 +164,16 @@ func (a *PlaylistPage) Scroll(scrollAmt float32) { func (a *PlaylistPage) load() { playlist, err := a.sm.Server.GetPlaylist(a.playlistID) if err != nil { - log.Printf("Failed to get playlist: %s", err.Error()) + msg := err.Error() + log.Printf("Failed to get playlist: %s", msg) + toastMsg := "An error occurred" + if strings.Contains(msg, "deadline exceeded") { + toastMsg = "The request timed out" + } + fyne.Do(func() { + a.tracklist.SetLoading(false) + a.contr.ToastProvider.ShowErrorToast(lang.L(toastMsg)) + }) return } if a.disposed { @@ -167,6 +181,7 @@ func (a *PlaylistPage) load() { } renumberTracks(playlist.Tracks) fyne.Do(func() { + a.tracklist.SetLoading(false) a.tracks = playlist.Tracks a.tracklist.SetTracks(playlist.Tracks) a.tracklist.SetNowPlaying(a.nowPlayingID) diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index 1962a80..d726b78 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -102,6 +102,7 @@ type Tracklist struct { shareMenuItem *fyne.MenuItem songRadioMenuItem *fyne.MenuItem infoMenuItem *fyne.MenuItem + loadingDots *LoadingDots container *fyne.Container } @@ -199,17 +200,28 @@ func NewTracklist(tracks []*mediaprovider.Track, im *backend.ImageManager, useCo t.OnReorderTracks(t.SelectedTrackIDs(), insertPos) } } - t.container = container.NewBorder(t.hdr, nil, nil, nil, t.list) + t.loadingDots = NewLoadingDots() + t.container = container.NewBorder(t.hdr, nil, nil, nil, + container.NewStack(t.list, container.NewCenter(t.loadingDots))) return t } func (t *Tracklist) Reset() { + t.SetLoading(false) t.Clear() t.Options = TracklistOptions{} t.ctxMenu = nil t.SetSorting(TracklistSort{}) } +func (t *Tracklist) SetLoading(loading bool) { + if loading { + t.loadingDots.Start() + } else { + t.loadingDots.Stop() + } +} + func (t *Tracklist) Scroll(amount float32) { t.list.ScrollToOffset(t.list.GetScrollOffset() + amount) } diff --git a/ui/widgets/tracklistloader.go b/ui/widgets/tracklistloader.go index fddf16d..461c829 100644 --- a/ui/widgets/tracklistloader.go +++ b/ui/widgets/tracklistloader.go @@ -29,12 +29,14 @@ func NewTracklistLoader(tracklist *Tracklist, iter mediaprovider.TrackIterator) } t.tracklist.OnTrackShown = t.onTrackShown t.fetching = true + t.tracklist.SetLoading(true) go t.loadMoreTracks(25) return &t } // Cancels all asynchronous loads so that they will no longer modify the tracklist. func (t *TracklistLoader) Dispose() { + t.tracklist.SetLoading(false) t.disposed.Store(true) t.tracklist.OnTrackShown = nil } @@ -71,6 +73,7 @@ func (t *TracklistLoader) loadMoreTracks(num int) { return } fyne.Do(func() { + t.tracklist.SetLoading(false) t.tracklist.AppendTracks(t.trackBuffer) t.len += len(t.trackBuffer) })