From d939c7f85f4c1d251868c4a1ff4b5c15c87acc74 Mon Sep 17 00:00:00 2001 From: natilou Date: Sat, 18 May 2024 16:03:21 -0300 Subject: [PATCH] add 'Skip duplicates' option --- ui/controller/controller.go | 26 +++++++++++++++++-- ui/dialogs/searchdialog.go | 48 +++++++++++++++++++++++++----------- ui/dialogs/selectplaylist.go | 11 ++++++--- 3 files changed, 65 insertions(+), 20 deletions(-) diff --git a/ui/controller/controller.go b/ui/controller/controller.go index b79a2e2..02e7e97 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -12,6 +12,7 @@ import ( "os" "path/filepath" "time" + "slices" "github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend/mediaprovider" @@ -338,7 +339,27 @@ func (m *Controller) DoAddTracksToPlaylistWorkflow(trackIDs []string) { go m.App.ServerManager.Server.CreatePlaylist(query, trackIDs) } else { m.App.Config.Application.DefaultPlaylistID = id - go m.App.ServerManager.Server.AddPlaylistTracks(id, trackIDs) + if sp.SkipDuplicates { + var filterTrackIDs []string + go func() { + if selectedPlaylist, err := m.App.ServerManager.Server.GetPlaylist(id); err != nil { + log.Printf("error getting playlist: %s", err.Error()) + } else { + var trackIDsInPaylist []string + + for _, track := range selectedPlaylist.Tracks{ + trackIDsInPaylist = append(trackIDsInPaylist, track.ID) + } + filterTrackIDs = sharedutil.FilterSlice(trackIDs, func(trackID string) bool { + return !slices.Contains(trackIDsInPaylist, trackID) + }) + + } + m.App.ServerManager.Server.AddPlaylistTracks(id, filterTrackIDs) + }() + } else { + go m.App.ServerManager.Server.AddPlaylistTracks(id, trackIDs) + } } }) @@ -346,8 +367,8 @@ func (m *Controller) DoAddTracksToPlaylistWorkflow(trackIDs []string) { m.haveModal = true min := sp.MinSize() height := fyne.Max(min.Height, fyne.Min(min.Height*1.5, m.MainWindow.Canvas().Size().Height*0.7)) - pop.Resize(fyne.NewSize(min.Width, height)) sp.SearchDialog.Show() + pop.Resize(fyne.NewSize(min.Width, height)) pop.Show() m.MainWindow.Canvas().Focus(sp.GetSearchEntry()) } @@ -627,6 +648,7 @@ func (c *Controller) ShowQuickSearch() { c.haveModal = true min := qs.MinSize() height := fyne.Max(min.Height, fyne.Min(min.Height*1.5, c.MainWindow.Canvas().Size().Height*0.7)) + qs.SearchDialog.Show() pop.Resize(fyne.NewSize(min.Width, height)) pop.Show() c.MainWindow.Canvas().Focus(qs.GetSearchEntry()) diff --git a/ui/dialogs/searchdialog.go b/ui/dialogs/searchdialog.go index 4477929..0272cae 100644 --- a/ui/dialogs/searchdialog.go +++ b/ui/dialogs/searchdialog.go @@ -33,22 +33,24 @@ type SearchDialog struct { list *widget.List selectedIndex int + placeholderTitle string content *fyne.Container OnDismiss func() OnNavigateTo func(mediaprovider.ContentType, string, string) OnSearched func(string) []*mediaprovider.SearchResult OnUpdateSearchResults func(*searchResult, *mediaprovider.SearchResult) - OnInit func() []*mediaprovider.SearchResult + OnInit func() ([]*mediaprovider.SearchResult, *widget.Check) } -func NewSearchDialog(im util.ImageFetcher, placeholderTitle string, onSearched func(string) []*mediaprovider.SearchResult, onUpdateSearchResult func(*searchResult, *mediaprovider.SearchResult), onInit func() []*mediaprovider.SearchResult) *SearchDialog { +func NewSearchDialog(im util.ImageFetcher, placeholderTitle string, onSearched func(string) []*mediaprovider.SearchResult, onUpdateSearchResult func(*searchResult, *mediaprovider.SearchResult), onInit func() ([]*mediaprovider.SearchResult, *widget.Check)) *SearchDialog { sd := &SearchDialog{ imgSource: im, loadingDots: widgets.NewLoadingDots(), OnSearched: onSearched, OnUpdateSearchResults: onUpdateSearchResult, OnInit: onInit, + placeholderTitle: placeholderTitle, } sd.ExtendBaseWidget(sd) @@ -80,17 +82,6 @@ func NewSearchDialog(im util.ImageFetcher, placeholderTitle string, onSearched f sd.update(sr, result) }, ) - - dismissBtn := widget.NewButton("Close", sd.onDismiss) - title := widget.NewRichText(&widget.TextSegment{Text: placeholderTitle, Style: util.BoldRichTextStyle}) - title.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignCenter - sd.content = container.NewStack( - container.NewBorder( - container.NewVBox(title, se), - container.NewVBox(widget.NewSeparator(), container.NewHBox(layout.NewSpacer(), dismissBtn)), - nil, nil, sd.list), - container.NewCenter(sd.loadingDots), - ) return sd } @@ -149,20 +140,47 @@ func (sd *SearchDialog) setResults(results []*mediaprovider.SearchResult) { sd.list.Select(0) } +func (sd *SearchDialog) SetContent(checkBox *widget.Check) { + dismissBtn := widget.NewButton("Close", sd.onDismiss) + title := widget.NewRichText(&widget.TextSegment{Text:sd.placeholderTitle, Style: util.BoldRichTextStyle}) + title.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignCenter + se := sd.SearchEntry.(fyne.CanvasObject) + if checkBox != nil { + sd.content = container.NewStack( + container.NewBorder( + container.NewVBox(title, se), + container.NewVBox(widget.NewSeparator(), container.NewHBox(checkBox, layout.NewSpacer(), dismissBtn)), + nil, nil, sd.list), + container.NewCenter(sd.loadingDots), + ) + } else { + sd.content = container.NewStack( + container.NewBorder( + container.NewVBox(title, se), + container.NewVBox(widget.NewSeparator(), container.NewHBox(layout.NewSpacer(), dismissBtn)), + nil, nil, sd.list), + container.NewCenter(sd.loadingDots), + ) + } +} + + func (sd *SearchDialog) onInit() { if sd.OnInit == nil { + sd.SetContent(nil) return } sd.loadingDots.Start() var results []*mediaprovider.SearchResult - res := sd.OnInit() + res, checkBox := sd.OnInit() if len(res) == 0 { log.Println("No results") } else { results = res } + sd.SetContent(checkBox) sd.loadingDots.Stop() - sd.setResults(results) + sd.setResults(results) } func (sd *SearchDialog) onSearched(query string) { diff --git a/ui/dialogs/selectplaylist.go b/ui/dialogs/selectplaylist.go index b4f358c..abf5838 100644 --- a/ui/dialogs/selectplaylist.go +++ b/ui/dialogs/selectplaylist.go @@ -20,6 +20,7 @@ type SelectPlaylist struct { mp mediaprovider.MediaProvider loggedInUser string allPlaylists []*mediaprovider.Playlist + SkipDuplicates bool } func NewSelectPlaylistDialog(mp mediaprovider.MediaProvider, im util.ImageFetcher, loggedInUser string) *SelectPlaylist { @@ -27,6 +28,7 @@ func NewSelectPlaylistDialog(mp mediaprovider.MediaProvider, im util.ImageFetche sp := &SelectPlaylist{ mp: mp, loggedInUser: loggedInUser, + SkipDuplicates: false, } sd := NewSearchDialog( im, @@ -39,13 +41,13 @@ func NewSelectPlaylistDialog(mp mediaprovider.MediaProvider, im util.ImageFetche return sp } -func (sp *SelectPlaylist) onInit() []*mediaprovider.SearchResult { +func (sp *SelectPlaylist) onInit() ([]*mediaprovider.SearchResult, *widget.Check) { var results []*mediaprovider.SearchResult playlists, err := sp.mp.GetPlaylists() if err != nil { // TODO: surface this error to user log.Printf("error getting playlists: %s", err.Error()) - return results + return results, nil } sp.allPlaylists = sharedutil.FilterSlice(playlists, func(playlist *mediaprovider.Playlist) bool { return playlist.Owner == sp.loggedInUser @@ -60,7 +62,10 @@ func (sp *SelectPlaylist) onInit() []*mediaprovider.SearchResult { ArtistName: playlist.Name, }) } - return results + skipDuplicatesCheck := widget.NewCheck("Skip duplicates", func(checked bool) { + sp.SkipDuplicates = checked + }) + return results, skipDuplicatesCheck } func (sp *SelectPlaylist) onSearched(query string) []*mediaprovider.SearchResult {