add multi-server functionality pt. 2 - delete server

This commit is contained in:
Drew Weymouth
2023-05-17 16:19:24 -07:00
parent 85eb790cd1
commit 4d4b14e4de
8 changed files with 113 additions and 18 deletions
+7
View File
@@ -12,6 +12,7 @@ import (
"github.com/dweymouth/supersonic/backend/util" "github.com/dweymouth/supersonic/backend/util"
"github.com/dweymouth/supersonic/player" "github.com/dweymouth/supersonic/player"
"github.com/dweymouth/supersonic/sharedutil" "github.com/dweymouth/supersonic/sharedutil"
"github.com/google/uuid"
"github.com/20after4/configdir" "github.com/20after4/configdir"
"github.com/zalando/go-keyring" "github.com/zalando/go-keyring"
@@ -149,6 +150,12 @@ func (a *App) LoginToDefaultServer(string) error {
return a.ServerManager.ConnectToServer(serverCfg, pass) return a.ServerManager.ConnectToServer(serverCfg, pass)
} }
func (a *App) DeleteServerCacheDir(serverID uuid.UUID) error {
path := path.Join(configdir.LocalCache(a.appName), serverID.String())
log.Printf("Deleting server cache dir: %s", path)
return os.RemoveAll(path)
}
func (a *App) Shutdown() { func (a *App) Shutdown() {
a.PlaybackManager.DisableCallbacks() a.PlaybackManager.DisableCallbacks()
a.Player.Stop() // will trigger scrobble check a.Player.Stop() // will trigger scrobble check
+10
View File
@@ -219,6 +219,16 @@ func (c *Config) AddServer(nickname string, connection ServerConnection) *Server
return s return s
} }
func (c *Config) DeleteServer(uuid uuid.UUID) {
newServers := make([]*ServerConfig, 0, len(c.Servers)-1)
for _, s := range c.Servers {
if s.ID != uuid {
newServers = append(newServers, s)
}
}
c.Servers = newServers
}
func (c *Config) WriteConfigFile(filepath string) error { func (c *Config) WriteConfigFile(filepath string) error {
b, err := toml.Marshal(c) b, err := toml.Marshal(c)
if err != nil { if err != nil {
+13
View File
@@ -3,6 +3,7 @@ package backend
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"fmt" "fmt"
"image" "image"
"image/jpeg" "image/jpeg"
@@ -14,6 +15,7 @@ import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"github.com/20after4/configdir" "github.com/20after4/configdir"
"github.com/google/uuid"
) )
const CachedImageValidTime = 24 * time.Hour const CachedImageValidTime = 24 * time.Hour
@@ -74,6 +76,9 @@ func (i *ImageManager) GetFullSizeCoverArt(coverID string) (image.Image, error)
if i.cachedFullSizeCoverID == coverID { if i.cachedFullSizeCoverID == coverID {
return i.cachedFullSizeCover, nil return i.cachedFullSizeCover, nil
} }
if i.s.Server == nil {
return nil, errors.New("logged out")
}
im, err := i.s.Server.GetCoverArt(coverID, 0) im, err := i.s.Server.GetCoverArt(coverID, 0)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -105,6 +110,11 @@ func (i *ImageManager) RefreshCachedArtistImageIfExpired(artistID string, imgURL
} }
func (i *ImageManager) ensureCoverCacheDir() string { func (i *ImageManager) ensureCoverCacheDir() string {
// if user logged out with pending fetches in progress,
// make sure we don't write to nil (00000000-*0) cache directory
if i.s.ServerID == uuid.Nil {
return ""
}
path := path.Join(i.baseCacheDir, i.s.ServerID.String(), "covers") path := path.Join(i.baseCacheDir, i.s.ServerID.String(), "covers")
configdir.MakePath(path) configdir.MakePath(path)
return path return path
@@ -145,6 +155,9 @@ func (i *ImageManager) fetchAndCacheCoverFromDiskOrServer(coverID string, ttl ti
} }
func (i *ImageManager) fetchAndCacheCoverFromServer(coverID string, ttl time.Duration) (image.Image, error) { func (i *ImageManager) fetchAndCacheCoverFromServer(coverID string, ttl time.Duration) (image.Image, error) {
if i.s.Server == nil {
return nil, errors.New("logged out")
}
img, err := i.s.Server.GetCoverArt(coverID, coverArtThumbnailSize) img, err := i.s.Server.GetCoverArt(coverID, coverArtThumbnailSize)
if err != nil { if err != nil {
return nil, err return nil, err
+31 -3
View File
@@ -1,6 +1,7 @@
package controller package controller
import ( import (
"fmt"
"image" "image"
"log" "log"
"time" "time"
@@ -140,7 +141,7 @@ func (m *Controller) ConnectAlbumGridActions(grid *widgets.GridView) {
} }
func (m *Controller) PromptForFirstServer() { func (m *Controller) PromptForFirstServer() {
d := dialogs.NewAddEditServerDialog("Connect to Server", nil, m.MainWindow.Canvas().Focus) d := dialogs.NewAddEditServerDialog("Connect to Server", false, nil, m.MainWindow.Canvas().Focus)
pop := widget.NewModalPopUp(d, m.MainWindow.Canvas()) pop := widget.NewModalPopUp(d, m.MainWindow.Canvas())
d.OnSubmit = func() { d.OnSubmit = func() {
d.DisableSubmit() d.DisableSubmit()
@@ -290,7 +291,7 @@ func (m *Controller) PromptForLoginAndConnect() {
} }
d.OnEditServer = func(server *backend.ServerConfig) { d.OnEditServer = func(server *backend.ServerConfig) {
pop.Hide() pop.Hide()
editD := dialogs.NewAddEditServerDialog("Edit server", server, m.MainWindow.Canvas().Focus) editD := dialogs.NewAddEditServerDialog("Edit server", true, server, m.MainWindow.Canvas().Focus)
editPop := widget.NewModalPopUp(editD, m.MainWindow.Canvas()) editPop := widget.NewModalPopUp(editD, m.MainWindow.Canvas())
editD.OnSubmit = func() { editD.OnSubmit = func() {
d.DisableSubmit() d.DisableSubmit()
@@ -309,16 +310,21 @@ func (m *Controller) PromptForLoginAndConnect() {
d.EnableSubmit() d.EnableSubmit()
}() }()
} }
editD.OnCancel = func() {
editPop.Hide()
pop.Show()
}
editPop.Show() editPop.Show()
} }
d.OnNewServer = func() { d.OnNewServer = func() {
pop.Hide() pop.Hide()
newD := dialogs.NewAddEditServerDialog("Add server", nil, m.MainWindow.Canvas().Focus) newD := dialogs.NewAddEditServerDialog("Add server", true, nil, m.MainWindow.Canvas().Focus)
newPop := widget.NewModalPopUp(newD, m.MainWindow.Canvas()) newPop := widget.NewModalPopUp(newD, m.MainWindow.Canvas())
newD.OnSubmit = func() { newD.OnSubmit = func() {
d.DisableSubmit() d.DisableSubmit()
go func() { go func() {
if m.testConnectionAndUpdateDialogText(newD) { if m.testConnectionAndUpdateDialogText(newD) {
// connection is good
newPop.Hide() newPop.Hide()
conn := backend.ServerConnection{ conn := backend.ServerConnection{
Hostname: newD.Host, Hostname: newD.Host,
@@ -333,8 +339,30 @@ func (m *Controller) PromptForLoginAndConnect() {
d.EnableSubmit() d.EnableSubmit()
}() }()
} }
newD.OnCancel = func() {
newPop.Hide()
pop.Show()
}
newPop.Show() newPop.Show()
} }
d.OnDeleteServer = func(server *backend.ServerConfig) {
pop.Hide()
dialog.ShowConfirm("Confirm delete server",
fmt.Sprintf("Are you sure you want to delete the server %q?", server.Nickname),
func(ok bool) {
if ok {
m.App.Config.DeleteServer(server.ID)
m.App.DeleteServerCacheDir(server.ID)
d.SetServers(m.App.Config.Servers)
}
if len(m.App.Config.Servers) == 0 {
m.PromptForFirstServer()
} else {
pop.Show()
}
}, m.MainWindow)
}
m.haveModal = true m.haveModal = true
pop.Show() pop.Show()
} }
+24 -5
View File
@@ -21,6 +21,7 @@ type AddEditServerDialog struct {
Password string Password string
LegacyAuth bool LegacyAuth bool
OnSubmit func() OnSubmit func()
OnCancel func()
passField *widget.Entry passField *widget.Entry
submitBtn *widget.Button submitBtn *widget.Button
@@ -30,7 +31,7 @@ type AddEditServerDialog struct {
var _ fyne.Widget = (*AddEditServerDialog)(nil) var _ fyne.Widget = (*AddEditServerDialog)(nil)
func NewAddEditServerDialog(title string, prefillServer *backend.ServerConfig, focusHandler func(fyne.Focusable)) *AddEditServerDialog { func NewAddEditServerDialog(title string, cancelable bool, prefillServer *backend.ServerConfig, focusHandler func(fyne.Focusable)) *AddEditServerDialog {
a := &AddEditServerDialog{} a := &AddEditServerDialog{}
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
if prefillServer != nil { if prefillServer != nil {
@@ -57,11 +58,26 @@ func NewAddEditServerDialog(title string, prefillServer *backend.ServerConfig, f
nickField.SetPlaceHolder("My Server") nickField.SetPlaceHolder("My Server")
nickField.OnSubmitted = func(_ string) { focusHandler(hostField) } nickField.OnSubmitted = func(_ string) { focusHandler(hostField) }
a.submitBtn = widget.NewButton("Enter", a.doSubmit) a.submitBtn = widget.NewButton("Enter", a.doSubmit)
a.submitBtn.Importance = widget.HighImportance
a.promptText = widget.NewRichTextWithText("") a.promptText = widget.NewRichTextWithText("")
a.promptText.Hidden = true a.promptText.Hidden = true
legacyAuthCheck := widget.NewCheckWithData("Use legacy authentication", binding.BindBool(&a.LegacyAuth)) legacyAuthCheck := widget.NewCheckWithData("Use legacy authentication", binding.BindBool(&a.LegacyAuth))
var bottomRow *fyne.Container
if cancelable {
bottomRow = container.NewHBox(
a.promptText,
layout.NewSpacer(),
widget.NewButton("Cancel", a.onCancel),
a.submitBtn)
} else {
bottomRow = container.NewHBox(
a.promptText,
layout.NewSpacer(),
a.submitBtn)
}
a.container = container.NewVBox( a.container = container.NewVBox(
container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()), container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()),
container.New(layout.NewFormLayout(), container.New(layout.NewFormLayout(),
@@ -78,10 +94,7 @@ func NewAddEditServerDialog(title string, prefillServer *backend.ServerConfig, f
), ),
container.NewHBox(layout.NewSpacer(), legacyAuthCheck), container.NewHBox(layout.NewSpacer(), legacyAuthCheck),
widget.NewSeparator(), widget.NewSeparator(),
container.NewHBox( bottomRow,
a.promptText,
layout.NewSpacer(),
a.submitBtn),
) )
return a return a
} }
@@ -111,6 +124,12 @@ func (a *AddEditServerDialog) doSubmit() {
} }
} }
func (a *AddEditServerDialog) onCancel() {
if a.OnCancel != nil {
a.OnCancel()
}
}
func (a *AddEditServerDialog) doSetPromptText(text string, color fyne.ThemeColorName) { func (a *AddEditServerDialog) doSetPromptText(text string, color fyne.ThemeColorName) {
ts := a.promptText.Segments[0].(*widget.TextSegment) ts := a.promptText.Segments[0].(*widget.TextSegment)
ts.Text = text ts.Text = text
+1
View File
@@ -48,6 +48,7 @@ func NewAddToPlaylistDialog(title string, existingPlaylistNames []string) *AddTo
a.newPlaylistLabel.Hidden = true a.newPlaylistLabel.Hidden = true
a.okBtn = widget.NewButton("OK", a.onOK) a.okBtn = widget.NewButton("OK", a.onOK)
a.okBtn.Importance = widget.HighImportance
a.okBtn.Disable() a.okBtn.Disable()
cancelBtn := widget.NewButton("Cancel", a.onCancel) cancelBtn := widget.NewButton("Cancel", a.onCancel)
+1
View File
@@ -44,6 +44,7 @@ func NewEditPlaylistDialog(playlist *mediaprovider.Playlist) *EditPlaylistDialog
e.OnUpdateMetadata() e.OnUpdateMetadata()
} }
}) })
submitBtn.Importance = widget.HighImportance
cancelBtn := widget.NewButton("Cancel", func() { cancelBtn := widget.NewButton("Cancel", func() {
if e.OnCanceled != nil { if e.OnCanceled != nil {
e.OnCanceled() e.OnCanceled()
+22 -6
View File
@@ -2,6 +2,7 @@ package dialogs
import ( import (
"github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend"
"github.com/dweymouth/supersonic/sharedutil"
"github.com/google/uuid" "github.com/google/uuid"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
@@ -18,9 +19,11 @@ type LoginDialog struct {
OnSubmit func(server *backend.ServerConfig, password string) OnSubmit func(server *backend.ServerConfig, password string)
OnEditServer func(server *backend.ServerConfig) OnEditServer func(server *backend.ServerConfig)
OnDeleteServer func(server *backend.ServerConfig)
OnNewServer func() OnNewServer func()
servers []*backend.ServerConfig servers []*backend.ServerConfig
serverSelect *widget.Select serverSelect *widget.Select
passField *widget.Entry passField *widget.Entry
promptText *widget.RichText promptText *widget.RichText
@@ -36,10 +39,7 @@ func NewLoginDialog(servers []*backend.ServerConfig, pwFetch PasswordFetchFunc)
l.ExtendBaseWidget(l) l.ExtendBaseWidget(l)
titleLabel := widget.NewLabel("Login to Server") titleLabel := widget.NewLabel("Login to Server")
titleLabel.TextStyle.Bold = true titleLabel.TextStyle.Bold = true
serverNames := make([]string, len(servers)) serverNames := sharedutil.MapSlice(servers, func(s *backend.ServerConfig) string { return s.Nickname })
for i, s := range servers {
serverNames[i] = s.Nickname
}
l.serverSelect = widget.NewSelect(serverNames, func(_ string) { l.serverSelect = widget.NewSelect(serverNames, func(_ string) {
if l.passField == nil { if l.passField == nil {
return return
@@ -54,9 +54,11 @@ func NewLoginDialog(servers []*backend.ServerConfig, pwFetch PasswordFetchFunc)
l.serverSelect.SetSelectedIndex(0) l.serverSelect.SetSelectedIndex(0)
editBtn := widget.NewButtonWithIcon("", theme.DocumentCreateIcon(), l.onEditServer) editBtn := widget.NewButtonWithIcon("", theme.DocumentCreateIcon(), l.onEditServer)
newBtn := widget.NewButtonWithIcon("", theme.ContentAddIcon(), l.onNewServer) newBtn := widget.NewButtonWithIcon("", theme.ContentAddIcon(), l.onNewServer)
deleteBtn := widget.NewButtonWithIcon("", theme.DeleteIcon(), func() { l.onDeleteServer(l.serverSelect.SelectedIndex()) })
l.passField = widget.NewPasswordEntry() l.passField = widget.NewPasswordEntry()
l.passField.OnSubmitted = func(_ string) { l.onSubmit() } l.passField.OnSubmitted = func(_ string) { l.onSubmit() }
l.submitBtn = widget.NewButton("OK", l.onSubmit) l.submitBtn = widget.NewButton("OK", l.onSubmit)
l.submitBtn.Importance = widget.HighImportance
l.promptText = widget.NewRichTextWithText("") l.promptText = widget.NewRichTextWithText("")
l.promptText.Segments[0].(*widget.TextSegment).Style.ColorName = theme.ColorNameError l.promptText.Segments[0].(*widget.TextSegment).Style.ColorName = theme.ColorNameError
@@ -66,7 +68,7 @@ func NewLoginDialog(servers []*backend.ServerConfig, pwFetch PasswordFetchFunc)
container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()), container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()),
container.New(layout.NewFormLayout(), container.New(layout.NewFormLayout(),
widget.NewLabel("Server"), widget.NewLabel("Server"),
container.NewBorder(nil, nil, nil, container.NewHBox(editBtn, newBtn), l.serverSelect), container.NewBorder(nil, nil, nil, container.NewHBox(editBtn, newBtn, deleteBtn), l.serverSelect),
widget.NewLabel("Password"), widget.NewLabel("Password"),
l.passField), l.passField),
widget.NewSeparator(), widget.NewSeparator(),
@@ -83,6 +85,14 @@ func (l *LoginDialog) SetErrorText(text string) {
l.doSetPromptText(text, theme.ColorNameError) l.doSetPromptText(text, theme.ColorNameError)
} }
func (l *LoginDialog) SetServers(servers []*backend.ServerConfig) {
l.servers = servers
l.serverSelect.Options = sharedutil.MapSlice(servers, func(s *backend.ServerConfig) string { return s.Nickname })
if len(servers) > 0 {
l.serverSelect.SetSelectedIndex(0)
}
}
func (l *LoginDialog) EnableSubmit() { func (l *LoginDialog) EnableSubmit() {
l.submitBtn.Enable() l.submitBtn.Enable()
l.submitBtn.Refresh() l.submitBtn.Refresh()
@@ -111,7 +121,7 @@ func (l *LoginDialog) CreateRenderer() fyne.WidgetRenderer {
func (l *LoginDialog) MinSize() fyne.Size { func (l *LoginDialog) MinSize() fyne.Size {
l.ExtendBaseWidget(l) l.ExtendBaseWidget(l)
return fyne.NewSize(330, l.container.MinSize().Height) return fyne.NewSize(375, l.container.MinSize().Height)
} }
func (l *LoginDialog) onSubmit() { func (l *LoginDialog) onSubmit() {
@@ -131,3 +141,9 @@ func (l *LoginDialog) onNewServer() {
l.OnNewServer() l.OnNewServer()
} }
} }
func (l *LoginDialog) onDeleteServer(idx int) {
if l.OnDeleteServer != nil {
l.OnDeleteServer(l.servers[idx])
}
}