add rating to tracklist, hook up set rating action

This commit is contained in:
Drew Weymouth
2023-04-25 20:48:49 -07:00
parent 238b786a15
commit e1f742fafd
3 changed files with 71 additions and 11 deletions
+8
View File
@@ -242,6 +242,14 @@ func (p *PlaybackManager) OnTrackFavoriteStatusChanged(id string, fav bool) {
} }
} }
// Any time the user changes the rating of a track elsewhere in the app,
// this should be called to ensure the in-memory track model is updated.
func (p *PlaybackManager) OnTrackRatingChanged(id string, rating int) {
if tr := sharedutil.FindTrackByID(id, p.playQueue); tr != nil {
tr.UserRating = rating
}
}
// trackIdxs must be sorted // trackIdxs must be sorted
func (p *PlaybackManager) RemoveTracksFromQueue(trackIdxs []int) { func (p *PlaybackManager) RemoveTracksFromQueue(trackIdxs []int) {
newQueue := make([]*subsonic.Child, 0, len(p.playQueue)-len(trackIdxs)) newQueue := make([]*subsonic.Child, 0, len(p.playQueue)-len(trackIdxs))
+22
View File
@@ -9,6 +9,7 @@ import (
"supersonic/ui/dialogs" "supersonic/ui/dialogs"
"supersonic/ui/util" "supersonic/ui/util"
"supersonic/ui/widgets" "supersonic/ui/widgets"
"sync"
"time" "time"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
@@ -111,6 +112,27 @@ func (m *Controller) ConnectTracklistActions(tracklist *widgets.Tracklist) {
m.App.PlaybackManager.OnTrackFavoriteStatusChanged(id, fav) m.App.PlaybackManager.OnTrackFavoriteStatusChanged(id, fav)
} }
} }
tracklist.OnSetRating = func(trackIDs []string, rating int) {
go func() {
// Subsonic doesn't allow bulk setting ratings.
// To not overwhelm the server with requests, set rating for
// only 5 tracks at a time concurrently
for i := 0; i < len(trackIDs); i += 5 {
var wg sync.WaitGroup
for j := i; j < i+5 && j < len(trackIDs); j++ {
wg.Add(1)
go func(idx int) {
m.App.ServerManager.Server.SetRating(trackIDs[idx], rating)
wg.Done()
}(j)
}
wg.Wait()
}
}()
for _, id := range trackIDs {
m.App.PlaybackManager.OnTrackRatingChanged(id, rating)
}
}
tracklist.OnShowAlbumPage = func(albumID string) { tracklist.OnShowAlbumPage = func(albumID string) {
m.NavigateTo(AlbumRoute(albumID)) m.NavigateTo(AlbumRoute(albumID))
} }
+41 -11
View File
@@ -26,6 +26,7 @@ const (
ColumnTime = "Time" ColumnTime = "Time"
ColumnYear = "Year" ColumnYear = "Year"
ColumnFavorite = "Favorite" ColumnFavorite = "Favorite"
ColumnRating = "Rating"
ColumnPlays = "Plays" ColumnPlays = "Plays"
ColumnBitrate = "Bitrate" ColumnBitrate = "Bitrate"
ColumnSize = "Size" ColumnSize = "Size"
@@ -62,6 +63,7 @@ type Tracklist struct {
OnAddToQueue func(trackIDs []*subsonic.Child) OnAddToQueue func(trackIDs []*subsonic.Child)
OnAddToPlaylist func(trackIDs []string) OnAddToPlaylist func(trackIDs []string)
OnSetFavorite func(trackIDs []string, fav bool) OnSetFavorite func(trackIDs []string, fav bool)
OnSetRating func(trackIDs []string, rating int)
OnShowArtistPage func(artistID string) OnShowArtistPage func(artistID string)
OnShowAlbumPage func(albumID string) OnShowAlbumPage func(albumID string)
@@ -83,12 +85,12 @@ type Tracklist struct {
} }
func NewTracklist(tracks []*subsonic.Child) *Tracklist { func NewTracklist(tracks []*subsonic.Child) *Tracklist {
t := &Tracklist{Tracks: tracks, visibleColumns: make([]bool, 11)} t := &Tracklist{Tracks: tracks, visibleColumns: make([]bool, 12)}
t.ExtendBaseWidget(t) t.ExtendBaseWidget(t)
t.selectionMgr = util.NewListSelectionManager(t.lenTracks) t.selectionMgr = util.NewListSelectionManager(t.lenTracks)
// #, Title, Artist, Album, Time, Year, Favorite, Plays, Bitrate, Size, Path // #, Title, Artist, Album, Time, Year, Favorite, Rating, Plays, Bitrate, Size, Path
t.colLayout = layouts.NewColumnsLayout([]float32{40, -1, -1, -1, 60, 60, 47, 65, 75, 70, -1}) t.colLayout = layouts.NewColumnsLayout([]float32{40, -1, -1, -1, 60, 60, 47, 95, 65, 75, 70, -1})
t.buildHeader() t.buildHeader()
t.hdr.OnColumnVisibilityChanged = t.setColumnVisible t.hdr.OnColumnVisibilityChanged = t.setColumnVisible
t.hdr.OnColumnVisibilityMenuShown = func(pop *widget.PopUp) { t.hdr.OnColumnVisibilityMenuShown = func(pop *widget.PopUp) {
@@ -132,6 +134,7 @@ func (t *Tracklist) buildHeader() {
{Text: "Time", AlignTrailing: true, CanToggleVisible: true}, {Text: "Time", AlignTrailing: true, CanToggleVisible: true},
{Text: "Year", AlignTrailing: true, CanToggleVisible: true}, {Text: "Year", AlignTrailing: true, CanToggleVisible: true},
{Text: "Fav.", AlignTrailing: false, CanToggleVisible: true}, {Text: "Fav.", AlignTrailing: false, CanToggleVisible: true},
{Text: "Rating", AlignTrailing: false, CanToggleVisible: true},
{Text: "Plays", AlignTrailing: true, CanToggleVisible: true}, {Text: "Plays", AlignTrailing: true, CanToggleVisible: true},
{Text: "Bitrate", AlignTrailing: true, CanToggleVisible: true}, {Text: "Bitrate", AlignTrailing: true, CanToggleVisible: true},
{Text: "Size", AlignTrailing: true, CanToggleVisible: true}, {Text: "Size", AlignTrailing: true, CanToggleVisible: true},
@@ -303,6 +306,18 @@ func (t *Tracklist) onSetFavorite(trackID string, fav bool) {
} }
} }
func (t *Tracklist) onSetRating(trackID string, rating int) {
// update our own track model
t.tracksMutex.RLock()
tr := sharedutil.FindTrackByID(trackID, t.Tracks)
t.tracksMutex.RUnlock()
tr.UserRating = rating
// notify listener
if t.OnSetRating != nil {
t.OnSetRating([]string{trackID}, rating)
}
}
func (t *Tracklist) onArtistTapped(artistID string) { func (t *Tracklist) onArtistTapped(artistID string) {
if t.OnShowArtistPage != nil { if t.OnShowArtistPage != nil {
t.OnShowArtistPage(artistID) t.OnShowArtistPage(artistID)
@@ -360,14 +375,16 @@ func ColNumber(colName string) int {
return 5 return 5
case ColumnFavorite: case ColumnFavorite:
return 6 return 6
case ColumnPlays: case ColumnRating:
return 7 return 7
case ColumnBitrate: case ColumnPlays:
return 8 return 8
case ColumnSize: case ColumnBitrate:
return 9 return 9
case ColumnPath: case ColumnSize:
return 10 return 10
case ColumnPath:
return 11
default: default:
log.Printf("error: Tracklist: invalid column name %s", colName) log.Printf("error: Tracklist: invalid column name %s", colName)
return -100 return -100
@@ -388,12 +405,14 @@ func colName(i int) string {
case 6: case 6:
return ColumnFavorite return ColumnFavorite
case 7: case 7:
return ColumnPlays return ColumnRating
case 8: case 8:
return ColumnBitrate return ColumnPlays
case 9: case 9:
return ColumnSize return ColumnBitrate
case 10: case 10:
return ColumnSize
case 11:
return ColumnPath return ColumnPath
default: default:
return "" return ""
@@ -421,6 +440,7 @@ type TrackRow struct {
dur *widget.RichText dur *widget.RichText
year *widget.RichText year *widget.RichText
favorite *fyne.Container favorite *fyne.Container
rating *StarRating
bitrate *widget.RichText bitrate *widget.RichText
plays *widget.RichText plays *widget.RichText
size *widget.RichText size *widget.RichText
@@ -449,6 +469,9 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow
favorite := NewTappbaleIcon(res.ResHeartOutlineInvertPng) favorite := NewTappbaleIcon(res.ResHeartOutlineInvertPng)
favorite.OnTapped = t.toggleFavorited favorite.OnTapped = t.toggleFavorited
t.favorite = container.NewCenter(favorite) t.favorite = container.NewCenter(favorite)
t.rating = NewStarRating()
t.rating.StarSize = 16
t.rating.OnRatingChanged = t.setTrackRating
t.plays = widget.NewRichTextWithText("") t.plays = widget.NewRichTextWithText("")
t.plays.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing t.plays.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing
t.bitrate = widget.NewRichTextWithText("") t.bitrate = widget.NewRichTextWithText("")
@@ -459,7 +482,7 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow
t.path.Wrapping = fyne.TextTruncate t.path.Wrapping = fyne.TextTruncate
t.Content = container.New(tracklist.colLayout, t.Content = container.New(tracklist.colLayout,
t.num, t.name, t.artist, t.album, t.dur, t.year, t.favorite, t.plays, t.bitrate, t.size, t.path) t.num, t.name, t.artist, t.album, t.dur, t.year, t.favorite, t.rating, t.plays, t.bitrate, t.size, t.path)
return t return t
} }
@@ -546,12 +569,15 @@ func (t *TrackRow) Update(tr *subsonic.Child, rowNum int) {
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartFilledInvertPng t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartFilledInvertPng
} }
t.rating.Rating = tr.UserRating
// Show only columns configured to be visible // Show only columns configured to be visible
t.artist.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnArtist)] t.artist.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnArtist)]
t.album.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnAlbum)] t.album.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnAlbum)]
t.dur.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnTime)] t.dur.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnTime)]
t.year.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnYear)] t.year.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnYear)]
t.favorite.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnFavorite)] t.favorite.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnFavorite)]
t.rating.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnRating)]
t.plays.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnPlays)] t.plays.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnPlays)]
t.bitrate.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnBitrate)] t.bitrate.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnBitrate)]
t.size.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnSize)] t.size.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnSize)]
@@ -574,6 +600,10 @@ func (t *TrackRow) toggleFavorited() {
} }
} }
func (t *TrackRow) setTrackRating(rating int) {
t.tracklist.onSetRating(t.trackID, rating)
}
func (t *TrackRow) TappedSecondary(e *fyne.PointEvent) { func (t *TrackRow) TappedSecondary(e *fyne.PointEvent) {
if t.OnTappedSecondary != nil { if t.OnTappedSecondary != nil {
t.OnTappedSecondary(e, t.trackIdx) t.OnTappedSecondary(e, t.trackIdx)