From 9a587eee98c03c028bd94929bfff336fb111dd79 Mon Sep 17 00:00:00 2001 From: natilou Date: Thu, 2 May 2024 20:18:15 -0300 Subject: [PATCH 1/4] chore: Improve 'Add to playlists' dialog --- backend/mediaprovider/model.go | 1 + ui/controller/controller.go | 62 ++++++---------- ui/dialogs/quicksearch.go | 2 +- ui/dialogs/searchdialog.go | 7 +- ui/dialogs/selectplaylist.go | 127 +++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 43 deletions(-) create mode 100644 ui/dialogs/selectplaylist.go diff --git a/backend/mediaprovider/model.go b/backend/mediaprovider/model.go index c865bce..315d3c9 100644 --- a/backend/mediaprovider/model.go +++ b/backend/mediaprovider/model.go @@ -179,4 +179,5 @@ type SearchResult struct { // Unset for ContentTypes Artist, Playlist, and Genre ArtistName string + Query string } diff --git a/ui/controller/controller.go b/ui/controller/controller.go index 84ef147..2d937b4 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -322,49 +322,33 @@ func (m *Controller) PromptForFirstServer() { pop.Show() } -// Show dialog to prompt for playlist. +// Show dialog to select playlist. // Depending on the results of that dialog, potentially create a new playlist // Add tracks to the user-specified playlist func (m *Controller) DoAddTracksToPlaylistWorkflow(trackIDs []string) { - go func() { - pls, err := m.App.ServerManager.Server.GetPlaylists() - pls = sharedutil.FilterSlice(pls, func(pl *mediaprovider.Playlist) bool { - return pl.Owner == m.App.ServerManager.LoggedInUser - }) - if err != nil { - // TODO: surface this error to user - log.Printf("error getting user-owned playlists: %s", err.Error()) - return + sp := dialogs.NewSelectPlaylistDialog(m.App.ServerManager.Server, m.App.ImageManager, m.App.ServerManager.LoggedInUser) + pop := widget.NewModalPopUp(sp.SearchDialog, m.MainWindow.Canvas()) + sp.SetOnDismiss(func() { + pop.Hide() + m.doModalClosed() + }) + sp.SetOnNavigateTo(func(contentType mediaprovider.ContentType, id string, query string) { + pop.Hide() + if id == "" { + go m.App.ServerManager.Server.CreatePlaylist(query, trackIDs) + } else { + m.App.Config.Application.DefaultPlaylistID = id + go m.App.ServerManager.Server.AddPlaylistTracks(id, trackIDs) } - selectedIdx := -1 - plNames := make([]string, 0, len(pls)) - for i, pl := range pls { - plNames = append(plNames, pl.Name) - if defId := m.App.Config.Application.DefaultPlaylistID; defId != "" && pl.ID == defId { - selectedIdx = i - } - } - - dlg := dialogs.NewAddToPlaylistDialog("Add to Playlist", plNames, selectedIdx) - pop := widget.NewModalPopUp(dlg, m.MainWindow.Canvas()) - m.ClosePopUpOnEscape(pop) - dlg.OnCanceled = pop.Hide - dlg.OnSubmit = func(playlistChoice int, newPlaylistName string) { - pop.Hide() - m.doModalClosed() - if playlistChoice < 0 { - go m.App.ServerManager.Server.CreatePlaylist(newPlaylistName, trackIDs) - } else { - playlist := pls[playlistChoice] - m.App.Config.Application.DefaultPlaylistID = playlist.ID - go m.App.ServerManager.Server.AddPlaylistTracks( - playlist.ID, trackIDs) - } - } - m.haveModal = true - pop.Show() - }() + }) + m.ClosePopUpOnEscape(pop) + 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)) + pop.Show() + m.MainWindow.Canvas().Focus(sp.GetSearchEntry()) } func (m *Controller) DoEditPlaylistWorkflow(playlist *mediaprovider.Playlist) { @@ -622,7 +606,7 @@ func (c *Controller) ShowQuickSearch() { pop.Hide() c.doModalClosed() }) - qs.SetOnNavigateTo(func(contentType mediaprovider.ContentType, id string) { + qs.SetOnNavigateTo(func(contentType mediaprovider.ContentType, id string, query string) { pop.Hide() c.doModalClosed() switch contentType { diff --git a/ui/dialogs/quicksearch.go b/ui/dialogs/quicksearch.go index 089a301..2b06d1e 100644 --- a/ui/dialogs/quicksearch.go +++ b/ui/dialogs/quicksearch.go @@ -95,7 +95,7 @@ func (q *QuickSearch) SetOnDismiss(onDismiss func()) { q.SearchDialog.OnDismiss = onDismiss } -func (q *QuickSearch) SetOnNavigateTo(onNavigateTo func(mediaprovider.ContentType, string)) { +func (q *QuickSearch) SetOnNavigateTo(onNavigateTo func(mediaprovider.ContentType, string, string)) { q.SearchDialog.OnNavigateTo = onNavigateTo } diff --git a/ui/dialogs/searchdialog.go b/ui/dialogs/searchdialog.go index 2afa2fa..8ddfd9a 100644 --- a/ui/dialogs/searchdialog.go +++ b/ui/dialogs/searchdialog.go @@ -36,7 +36,7 @@ type SearchDialog struct { content *fyne.Container OnDismiss func() - OnNavigateTo func(mediaprovider.ContentType, string) + OnNavigateTo func(mediaprovider.ContentType, string, string) OnSearched func(string) []*mediaprovider.SearchResult OnUpdateSearchResults func(*searchResult, *mediaprovider.SearchResult) } @@ -109,8 +109,9 @@ func (sd *SearchDialog) onSelected(idx int) { } id := sd.searchResults[idx].ID typ := sd.searchResults[idx].Type + query := sd.searchResults[idx].Query sd.resultsMutex.RUnlock() - sd.OnNavigateTo(typ, id) + sd.OnNavigateTo(typ, id, query) } func (sd *SearchDialog) moveSelectionDown() { @@ -164,7 +165,7 @@ func (sd *SearchDialog) update(sr *searchResult, result *mediaprovider.SearchRes if result == nil { return } - if sr.contentType == result.Type && sr.id == result.ID { + if sr.contentType == result.Type && sr.id == result.ID && result.ID != "" { return // nothing to do } sr.id = result.ID diff --git a/ui/dialogs/selectplaylist.go b/ui/dialogs/selectplaylist.go new file mode 100644 index 0000000..c54cf95 --- /dev/null +++ b/ui/dialogs/selectplaylist.go @@ -0,0 +1,127 @@ +package dialogs + +import ( + // "fmt" + "fmt" + "log" + "strings" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + "github.com/deluan/sanitize" + "github.com/dweymouth/supersonic/backend/mediaprovider" + "github.com/dweymouth/supersonic/sharedutil" + "github.com/dweymouth/supersonic/ui/util" +) + +type SelectPlaylist struct { + SearchDialog *SearchDialog + mp mediaprovider.MediaProvider + loggedInUser string +} + +func NewSelectPlaylistDialog(mp mediaprovider.MediaProvider, im util.ImageFetcher, loggedInUser string) *SelectPlaylist { + + sp := &SelectPlaylist{ + mp: mp, + loggedInUser: loggedInUser, + } + + sd := NewSearchDialog( + im, + "Select playlist", + sp.onSearched, + sp.onUpdateSearchResult, + ) + sp.SearchDialog = sd + return sp +} + +func (sp *SelectPlaylist) onSearched(query string) []*mediaprovider.SearchResult { + var results []*mediaprovider.SearchResult + if query != "" { + var filteredPlaylists []*mediaprovider.Playlist + if playlists, err := sp.mp.GetPlaylists(); err != nil { + // TODO: surface this error to user + log.Printf("error getting playlists: %s", err.Error()) + return results + } else { + filteredPlaylists = sharedutil.FilterSlice(playlists, func(playlist *mediaprovider.Playlist) bool { + return strings.Contains( + sanitize.Accents(strings.ToLower(playlist.Name)), + sanitize.Accents(strings.ToLower(query)), + ) && playlist.Owner == sp.loggedInUser + }) + } + + results = append(results, &mediaprovider.SearchResult{ + Name: fmt.Sprintf("Create new playlist: %s", query), + Type: mediaprovider.ContentTypePlaylist, + Query: query, + }) + for _, playlist := range filteredPlaylists { + results = append(results, &mediaprovider.SearchResult{ + Name: playlist.Name, + ID: playlist.ID, + CoverID: playlist.CoverArtID, + Type: mediaprovider.ContentTypePlaylist, + Size: playlist.TrackCount, + ArtistName: playlist.Name, + }) + } + + } + return results +} + +func (sp *SelectPlaylist) onUpdateSearchResult(sr *searchResult, result *mediaprovider.SearchResult) { + if result.ID == "" { + sr.secondary.Segments = []widget.RichTextSegment{} + sr.secondary.Refresh() + return + } + + maybePluralize := func(s string, size int) string { + if size != 1 { + return s + "s" + } + return s + } + secondaryText := fmt.Sprintf("%d %s", result.Size, maybePluralize("track", result.Size)) + sr.secondary.Segments = []widget.RichTextSegment{ + &widget.TextSegment{ + Text: result.Type.String(), + Style: widget.RichTextStyle{SizeName: theme.SizeNameCaptionText, TextStyle: fyne.TextStyle{Bold: true}, Inline: true}, + }, + } + if secondaryText != "" { + sr.secondary.Segments = append(sr.secondary.Segments, + &widget.TextSegment{ + Text: " ยท ", + Style: widget.RichTextStyle{SizeName: theme.SizeNameCaptionText, Inline: true}, + }, + &widget.TextSegment{ + Text: secondaryText, + Style: widget.RichTextStyle{SizeName: theme.SizeNameCaptionText, Inline: true}, + }, + ) + } + sr.secondary.Refresh() +} + +func (sp *SelectPlaylist) SetOnDismiss(onDismiss func()) { + sp.SearchDialog.OnDismiss = onDismiss +} + +func (sp *SelectPlaylist) SetOnNavigateTo(onNavigateTo func(mediaprovider.ContentType, string, string)) { + sp.SearchDialog.OnNavigateTo = onNavigateTo +} + +func (sp *SelectPlaylist) MinSize() fyne.Size { + return sp.SearchDialog.MinSize() +} + +func (sp *SelectPlaylist) GetSearchEntry() fyne.Focusable { + return sp.SearchDialog.SearchEntry +} From 1b53695589c5bf2514256af2ff3f7eb626fbf497 Mon Sep 17 00:00:00 2001 From: natilou Date: Sat, 18 May 2024 12:03:43 -0300 Subject: [PATCH 2/4] add a list with all the playlists and then filter by the search text --- ui/controller/controller.go | 1 + ui/dialogs/quicksearch.go | 1 + ui/dialogs/searchdialog.go | 51 ++++++++++++++++++------- ui/dialogs/selectplaylist.go | 72 +++++++++++++++++++++++------------- 4 files changed, 86 insertions(+), 39 deletions(-) diff --git a/ui/controller/controller.go b/ui/controller/controller.go index 2d937b4..b79a2e2 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -347,6 +347,7 @@ func (m *Controller) DoAddTracksToPlaylistWorkflow(trackIDs []string) { 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.Show() m.MainWindow.Canvas().Focus(sp.GetSearchEntry()) } diff --git a/ui/dialogs/quicksearch.go b/ui/dialogs/quicksearch.go index 2b06d1e..0c5d5c6 100644 --- a/ui/dialogs/quicksearch.go +++ b/ui/dialogs/quicksearch.go @@ -27,6 +27,7 @@ func NewQuickSearch(mp mediaprovider.MediaProvider, im util.ImageFetcher) *Quick "Quick Search", q.onSearched, q.onUpdateSearchResult, + nil, ) q.SearchDialog = sd return q diff --git a/ui/dialogs/searchdialog.go b/ui/dialogs/searchdialog.go index 8ddfd9a..4477929 100644 --- a/ui/dialogs/searchdialog.go +++ b/ui/dialogs/searchdialog.go @@ -39,14 +39,16 @@ type SearchDialog struct { OnNavigateTo func(mediaprovider.ContentType, string, string) OnSearched func(string) []*mediaprovider.SearchResult OnUpdateSearchResults func(*searchResult, *mediaprovider.SearchResult) + OnInit func() []*mediaprovider.SearchResult } -func NewSearchDialog(im util.ImageFetcher, placeholderTitle string, onSearched func(string) []*mediaprovider.SearchResult, onUpdateSearchResult func(*searchResult, *mediaprovider.SearchResult)) *SearchDialog { +func NewSearchDialog(im util.ImageFetcher, placeholderTitle string, onSearched func(string) []*mediaprovider.SearchResult, onUpdateSearchResult func(*searchResult, *mediaprovider.SearchResult), onInit func() []*mediaprovider.SearchResult) *SearchDialog { sd := &SearchDialog{ imgSource: im, loadingDots: widgets.NewLoadingDots(), OnSearched: onSearched, OnUpdateSearchResults: onUpdateSearchResult, + OnInit: onInit, } sd.ExtendBaseWidget(sd) @@ -92,6 +94,11 @@ func NewSearchDialog(im util.ImageFetcher, placeholderTitle string, onSearched f return sd } +func (sd *SearchDialog) Show() { + sd.onInit() + sd.BaseWidget.Show() +} + func (sd *SearchDialog) onDismiss() { if sd.OnDismiss != nil { sd.OnDismiss() @@ -132,18 +139,7 @@ func (sd *SearchDialog) moveSelectionUp() { sd.list.Select(sd.selectedIndex) } -func (sd *SearchDialog) onSearched(query string) { - sd.loadingDots.Start() - var results []*mediaprovider.SearchResult - if query != "" { - res := sd.OnSearched(query) - if len(res) == 0 { - log.Println("No results matched the query.") - } else { - results = res - } - } - sd.loadingDots.Stop() +func (sd *SearchDialog) setResults(results []*mediaprovider.SearchResult) { sd.resultsMutex.Lock() sd.searchResults = results sd.resultsMutex.Unlock() @@ -153,6 +149,35 @@ func (sd *SearchDialog) onSearched(query string) { sd.list.Select(0) } +func (sd *SearchDialog) onInit() { + if sd.OnInit == nil { + return + } + sd.loadingDots.Start() + var results []*mediaprovider.SearchResult + res := sd.OnInit() + if len(res) == 0 { + log.Println("No results") + } else { + results = res + } + sd.loadingDots.Stop() + sd.setResults(results) +} + +func (sd *SearchDialog) onSearched(query string) { + sd.loadingDots.Start() + var results []*mediaprovider.SearchResult + res := sd.OnSearched(query) + if len(res) == 0 { + log.Println("No results matched the query.") + } else { + results = res + } + sd.loadingDots.Stop() + sd.setResults(results) +} + func (sd *SearchDialog) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(sd.content) } diff --git a/ui/dialogs/selectplaylist.go b/ui/dialogs/selectplaylist.go index c54cf95..b4f358c 100644 --- a/ui/dialogs/selectplaylist.go +++ b/ui/dialogs/selectplaylist.go @@ -19,6 +19,7 @@ type SelectPlaylist struct { SearchDialog *SearchDialog mp mediaprovider.MediaProvider loggedInUser string + allPlaylists []*mediaprovider.Playlist } func NewSelectPlaylistDialog(mp mediaprovider.MediaProvider, im util.ImageFetcher, loggedInUser string) *SelectPlaylist { @@ -27,50 +28,69 @@ func NewSelectPlaylistDialog(mp mediaprovider.MediaProvider, im util.ImageFetche mp: mp, loggedInUser: loggedInUser, } - sd := NewSearchDialog( im, "Select playlist", sp.onSearched, sp.onUpdateSearchResult, + sp.onInit, ) sp.SearchDialog = sd return sp } +func (sp *SelectPlaylist) onInit() []*mediaprovider.SearchResult { + 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 + } + sp.allPlaylists = sharedutil.FilterSlice(playlists, func(playlist *mediaprovider.Playlist) bool { + return playlist.Owner == sp.loggedInUser + }) + for _, playlist := range sp.allPlaylists { + results = append(results, &mediaprovider.SearchResult{ + Name: playlist.Name, + ID: playlist.ID, + CoverID: playlist.CoverArtID, + Type: mediaprovider.ContentTypePlaylist, + Size: playlist.TrackCount, + ArtistName: playlist.Name, + }) + } + return results +} + func (sp *SelectPlaylist) onSearched(query string) []*mediaprovider.SearchResult { var results []*mediaprovider.SearchResult - if query != "" { - var filteredPlaylists []*mediaprovider.Playlist - if playlists, err := sp.mp.GetPlaylists(); err != nil { - // TODO: surface this error to user - log.Printf("error getting playlists: %s", err.Error()) - return results - } else { - filteredPlaylists = sharedutil.FilterSlice(playlists, func(playlist *mediaprovider.Playlist) bool { - return strings.Contains( - sanitize.Accents(strings.ToLower(playlist.Name)), - sanitize.Accents(strings.ToLower(query)), - ) && playlist.Owner == sp.loggedInUser - }) - } - + var filteredPlaylists []*mediaprovider.Playlist + if query == "" { + filteredPlaylists = sp.allPlaylists + } else { + filteredPlaylists = sharedutil.FilterSlice(sp.allPlaylists, func(playlist *mediaprovider.Playlist) bool { + return strings.Contains( + sanitize.Accents(strings.ToLower(playlist.Name)), + sanitize.Accents(strings.ToLower(query)), + ) + }) results = append(results, &mediaprovider.SearchResult{ Name: fmt.Sprintf("Create new playlist: %s", query), Type: mediaprovider.ContentTypePlaylist, Query: query, }) - for _, playlist := range filteredPlaylists { - results = append(results, &mediaprovider.SearchResult{ - Name: playlist.Name, - ID: playlist.ID, - CoverID: playlist.CoverArtID, - Type: mediaprovider.ContentTypePlaylist, - Size: playlist.TrackCount, - ArtistName: playlist.Name, - }) - } + } + for _, playlist := range filteredPlaylists { + results = append(results, &mediaprovider.SearchResult{ + Name: playlist.Name, + ID: playlist.ID, + CoverID: playlist.CoverArtID, + Type: mediaprovider.ContentTypePlaylist, + Size: playlist.TrackCount, + ArtistName: playlist.Name, + }) } return results } From d939c7f85f4c1d251868c4a1ff4b5c15c87acc74 Mon Sep 17 00:00:00 2001 From: natilou Date: Sat, 18 May 2024 16:03:21 -0300 Subject: [PATCH 3/4] 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 { From 090874591af7877b904c24a5a85672b6632d6964 Mon Sep 17 00:00:00 2001 From: natilou Date: Sat, 18 May 2024 16:07:50 -0300 Subject: [PATCH 4/4] go fmt --- ui/controller/controller.go | 10 +++++----- ui/dialogs/searchdialog.go | 21 ++++++++++----------- ui/dialogs/selectplaylist.go | 12 ++++++------ 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/ui/controller/controller.go b/ui/controller/controller.go index 02e7e97..a659483 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -11,8 +11,8 @@ import ( "net/url" "os" "path/filepath" - "time" "slices" + "time" "github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend/mediaprovider" @@ -346,20 +346,20 @@ func (m *Controller) DoAddTracksToPlaylistWorkflow(trackIDs []string) { log.Printf("error getting playlist: %s", err.Error()) } else { var trackIDsInPaylist []string - - for _, track := range selectedPlaylist.Tracks{ + + 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) - } + } } }) diff --git a/ui/dialogs/searchdialog.go b/ui/dialogs/searchdialog.go index 0272cae..9677268 100644 --- a/ui/dialogs/searchdialog.go +++ b/ui/dialogs/searchdialog.go @@ -33,8 +33,8 @@ type SearchDialog struct { list *widget.List selectedIndex int - placeholderTitle string - content *fyne.Container + placeholderTitle string + content *fyne.Container OnDismiss func() OnNavigateTo func(mediaprovider.ContentType, string, string) @@ -142,7 +142,7 @@ func (sd *SearchDialog) setResults(results []*mediaprovider.SearchResult) { 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 := 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 { @@ -155,15 +155,14 @@ func (sd *SearchDialog) SetContent(checkBox *widget.Check) { ) } 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), - ) + 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 { @@ -180,7 +179,7 @@ func (sd *SearchDialog) onInit() { } 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 abf5838..9136d48 100644 --- a/ui/dialogs/selectplaylist.go +++ b/ui/dialogs/selectplaylist.go @@ -16,18 +16,18 @@ import ( ) type SelectPlaylist struct { - SearchDialog *SearchDialog - mp mediaprovider.MediaProvider - loggedInUser string - allPlaylists []*mediaprovider.Playlist + SearchDialog *SearchDialog + mp mediaprovider.MediaProvider + loggedInUser string + allPlaylists []*mediaprovider.Playlist SkipDuplicates bool } func NewSelectPlaylistDialog(mp mediaprovider.MediaProvider, im util.ImageFetcher, loggedInUser string) *SelectPlaylist { sp := &SelectPlaylist{ - mp: mp, - loggedInUser: loggedInUser, + mp: mp, + loggedInUser: loggedInUser, SkipDuplicates: false, } sd := NewSearchDialog(