add Favorites page and toggle favorite button to album page

This commit is contained in:
Drew Weymouth
2023-01-21 17:31:52 -08:00
parent 6bae838f90
commit 96f0748767
5 changed files with 184 additions and 9 deletions
+29 -8
View File
@@ -20,6 +20,7 @@ type AlbumPage struct {
widget.BaseWidget widget.BaseWidget
albumID string albumID string
sm *backend.ServerManager
im *backend.ImageManager im *backend.ImageManager
lm *backend.LibraryManager lm *backend.LibraryManager
nav func(Route) nav func(Route)
@@ -31,8 +32,8 @@ type AlbumPage struct {
OnPlayAlbum func(string, int) OnPlayAlbum func(string, int)
} }
func NewAlbumPage(albumID string, lm *backend.LibraryManager, im *backend.ImageManager, nav func(Route)) *AlbumPage { func NewAlbumPage(albumID string, sm *backend.ServerManager, lm *backend.LibraryManager, im *backend.ImageManager, nav func(Route)) *AlbumPage {
a := &AlbumPage{albumID: albumID, lm: lm, im: im, nav: nav} a := &AlbumPage{albumID: albumID, sm: sm, lm: lm, im: im, nav: nav}
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
a.header = NewAlbumPageHeader(a) a.header = NewAlbumPageHeader(a)
a.tracklist = widgets.NewTracklist(nil) a.tracklist = widgets.NewTracklist(nil)
@@ -104,19 +105,22 @@ type AlbumPageHeader struct {
artistID string artistID string
genre string genre string
page *AlbumPage
cover *canvas.Image cover *canvas.Image
titleLabel *widget.RichText titleLabel *widget.RichText
artistLabel *widgets.CustomHyperlink artistLabel *widgets.CustomHyperlink
genreLabel *widgets.CustomHyperlink genreLabel *widgets.CustomHyperlink
miscLabel *widget.Label miscLabel *widget.Label
playButton *widget.Button toggleFavButton *widgets.FavoriteButton
playButton *widget.Button
container *fyne.Container container *fyne.Container
} }
func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader { func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader {
a := &AlbumPageHeader{} a := &AlbumPageHeader{page: page}
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
a.cover = &canvas.Image{FillMode: canvas.ImageFillContain} a.cover = &canvas.Image{FillMode: canvas.ImageFillContain}
a.cover.SetMinSize(fyne.NewSize(225, 225)) a.cover.SetMinSize(fyne.NewSize(225, 225))
@@ -142,12 +146,19 @@ func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader {
a.playButton = widget.NewButtonWithIcon("Play", theme.MediaPlayIcon(), func() { a.playButton = widget.NewButtonWithIcon("Play", theme.MediaPlayIcon(), func() {
page.onPlayTrackAt(0) page.onPlayTrackAt(0)
}) })
a.toggleFavButton = widgets.NewFavoriteButton(a.toggleFavorited)
// Todo: there's got to be a way to make this less convoluted. Custom layout?
a.container = container.NewBorder(nil, nil, a.cover, nil, a.container = container.NewBorder(nil, nil, a.cover, nil,
container.NewVBox( container.New(&layouts.VboxCustomPadding{ExtraPad: -10},
a.titleLabel, a.titleLabel,
container.New(&layouts.VboxCustomPadding{ExtraPad: -10}, a.artistLabel, a.genreLabel, a.miscLabel), container.NewVBox(
container.NewHBox(a.playButton), container.New(&layouts.VboxCustomPadding{ExtraPad: -12}, a.artistLabel, a.genreLabel, a.miscLabel),
container.NewVBox(
container.NewHBox(widgets.NewHSpace(2), a.playButton),
container.NewHBox(widgets.NewHSpace(2), a.toggleFavButton),
),
),
), ),
) )
return a return a
@@ -165,6 +176,7 @@ func (a *AlbumPageHeader) Update(album *subsonic.AlbumID3, im *backend.ImageMana
a.genre = album.Genre a.genre = album.Genre
a.genreLabel.SetText(album.Genre) a.genreLabel.SetText(album.Genre)
a.miscLabel.SetText(formatMiscLabelStr(album)) a.miscLabel.SetText(formatMiscLabelStr(album))
a.toggleFavButton.IsFavorited = !album.Starred.IsZero()
a.Refresh() a.Refresh()
// cover image was already loaded from cache in consructor // cover image was already loaded from cache in consructor
@@ -181,6 +193,14 @@ func (a *AlbumPageHeader) Update(album *subsonic.AlbumID3, im *backend.ImageMana
}() }()
} }
func (a *AlbumPageHeader) toggleFavorited() {
if a.toggleFavButton.IsFavorited {
a.page.sm.Server.Star(subsonic.StarParameters{AlbumIDs: []string{a.albumID}})
} else {
a.page.sm.Server.Unstar(subsonic.StarParameters{AlbumIDs: []string{a.albumID}})
}
}
func formatMiscLabelStr(a *subsonic.AlbumID3) string { func formatMiscLabelStr(a *subsonic.AlbumID3) string {
return fmt.Sprintf("%d · %d tracks · %s", a.Year, a.SongCount, util.SecondsToTimeString(float64(a.Duration))) return fmt.Sprintf("%d · %d tracks · %s", a.Year, a.SongCount, util.SecondsToTimeString(float64(a.Duration)))
} }
@@ -189,9 +209,10 @@ type savedAlbumPage struct {
albumID string albumID string
lm *backend.LibraryManager lm *backend.LibraryManager
im *backend.ImageManager im *backend.ImageManager
sm *backend.ServerManager
nav func(Route) nav func(Route)
} }
func (s *savedAlbumPage) Restore() Page { func (s *savedAlbumPage) Restore() Page {
return NewAlbumPage(s.albumID, s.lm, s.im, s.nav) return NewAlbumPage(s.albumID, s.sm, s.lm, s.im, s.nav)
} }
+104
View File
@@ -0,0 +1,104 @@
package browsing
import (
"log"
"supersonic/backend"
"supersonic/ui/widgets"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type FavoritesPage struct {
widget.BaseWidget
im *backend.ImageManager
sm *backend.ServerManager
nav func(Route)
grid *widgets.AlbumGrid
titleDisp *widget.RichText
container *fyne.Container
OnPlayAlbum func(string, int)
}
func NewFavoritesPage(sm *backend.ServerManager, im *backend.ImageManager, nav func(Route)) *FavoritesPage {
a := &FavoritesPage{
sm: sm,
im: im,
nav: nav,
}
a.ExtendBaseWidget(a)
a.titleDisp = widget.NewRichTextWithText("Favorites")
a.titleDisp.Segments[0].(*widget.TextSegment).Style = widget.RichTextStyle{
SizeName: theme.SizeNameHeadingText,
}
a.container = container.NewBorder(
container.NewHBox(widgets.NewHSpace(9), a.titleDisp), nil, nil, nil, layout.NewSpacer())
a.loadAsync()
return a
}
func (a *FavoritesPage) Route() Route {
return FavoritesRoute()
}
func (a *FavoritesPage) SetPlayAlbumCallback(cb func(string, int)) {
a.OnPlayAlbum = cb
}
func (a *FavoritesPage) Reload() {
a.loadAsync()
}
func (a *FavoritesPage) Save() SavedPage {
return &savedFavoritesPage{
sm: a.sm,
im: a.im,
nav: a.nav,
}
}
func (a *FavoritesPage) onPlayAlbum(albumID string) {
if a.OnPlayAlbum != nil {
a.OnPlayAlbum(albumID, 0)
}
}
func (a *FavoritesPage) onShowAlbumPage(albumID string) {
a.nav(AlbumRoute(albumID))
}
func (a *FavoritesPage) loadAsync() {
go func() {
starred, err := a.sm.Server.GetStarred2(nil)
if err != nil {
log.Printf("Failed to get favorites: %s", err.Error())
return
}
ag := widgets.NewFixedAlbumGrid(starred.Album, a.im, true /*showYear*/)
ag.OnPlayAlbum = a.onPlayAlbum
ag.OnShowAlbumPage = a.onShowAlbumPage
a.container.Objects[0] = ag
a.container.Refresh()
}()
}
func (a *FavoritesPage) CreateRenderer() fyne.WidgetRenderer {
a.ExtendBaseWidget(a)
return widget.NewSimpleRenderer(a.container)
}
type savedFavoritesPage struct {
artistID string
sm *backend.ServerManager
im *backend.ImageManager
nav func(Route)
}
func (s *savedFavoritesPage) Restore() Page {
return NewFavoritesPage(s.sm, s.im, s.nav)
}
+8 -1
View File
@@ -14,6 +14,7 @@ const (
Artists Artists
Genre Genre
Genres Genres
Favorites
Playlist Playlist
Playlists Playlists
) )
@@ -35,6 +36,10 @@ func AlbumRoute(albumID string) Route {
return Route{Page: Album, Arg: albumID} return Route{Page: Album, Arg: albumID}
} }
func FavoritesRoute() Route {
return Route{Page: Favorites}
}
func GenreRoute(genre string) Route { func GenreRoute(genre string) Route {
return Route{Page: Genre, Arg: genre} return Route{Page: Genre, Arg: genre}
} }
@@ -66,13 +71,15 @@ func NewRouter(app *backend.App, nav NavigationHandler) Router {
func (r Router) CreatePage(rte Route) Page { func (r Router) CreatePage(rte Route) Page {
switch rte.Page { switch rte.Page {
case Album: case Album:
return NewAlbumPage(rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) return NewAlbumPage(rte.Arg, r.App.ServerManager, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute)
case Albums: case Albums:
return NewAlbumsPage("Albums", rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) return NewAlbumsPage("Albums", rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute)
case Artist: case Artist:
return NewArtistPage(rte.Arg, r.App.ServerManager, r.App.ImageManager, r.OpenRoute) return NewArtistPage(rte.Arg, r.App.ServerManager, r.App.ImageManager, r.OpenRoute)
case Artists: case Artists:
return NewArtistsGenresPage(false, r.App.ServerManager, r.OpenRoute) return NewArtistsGenresPage(false, r.App.ServerManager, r.OpenRoute)
case Favorites:
return NewFavoritesPage(r.App.ServerManager, r.App.ImageManager, r.OpenRoute)
case Genre: case Genre:
return NewGenrePage(rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) return NewGenrePage(rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute)
case Genres: case Genres:
+3
View File
@@ -74,6 +74,9 @@ func (m *MainWindow) PromptForFirstServer(cb func(string, string, string, string
} }
func (m *MainWindow) addNavigationButtons() { func (m *MainWindow) addNavigationButtons() {
m.BrowsingPane.AddNavigationButton(res.ResHeartFilledInvertPng, func() {
m.Router.OpenRoute(browsing.FavoritesRoute())
})
m.BrowsingPane.AddNavigationButton(res.ResDiscInvertPng, func() { m.BrowsingPane.AddNavigationButton(res.ResDiscInvertPng, func() {
m.Router.OpenRoute(browsing.AlbumsRoute(backend.AlbumSortRecentlyAdded)) m.Router.OpenRoute(browsing.AlbumsRoute(backend.AlbumSortRecentlyAdded))
}) })
+40
View File
@@ -0,0 +1,40 @@
package widgets
import (
"supersonic/res"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
)
type FavoriteButton struct {
widget.Button
IsFavorited bool
}
func NewFavoriteButton(onTapped func()) *FavoriteButton {
f := &FavoriteButton{
Button: widget.Button{
OnTapped: onTapped,
Icon: res.ResHeartOutlineInvertPng,
},
}
f.ExtendBaseWidget(f)
return f
}
func (f *FavoriteButton) Tapped(e *fyne.PointEvent) {
f.IsFavorited = !f.IsFavorited
f.Button.Tapped(e)
f.Refresh()
}
func (f *FavoriteButton) Refresh() {
if f.IsFavorited {
f.Icon = res.ResHeartFilledInvertPng
} else {
f.Icon = res.ResHeartOutlineInvertPng
}
f.Button.Refresh()
}