diff --git a/backend/servermanager.go b/backend/servermanager.go index 12d7b91..c0a1e0e 100644 --- a/backend/servermanager.go +++ b/backend/servermanager.go @@ -37,15 +37,17 @@ func (s *ServerManager) ConnectToServer(conf *ServerConfig, password string) err return nil } -func (s *ServerManager) TestConnectionAndAuth(hostname, username, password string) error { +func (s *ServerManager) TestConnectionAndAuth(hostname, username, password string, timeout time.Duration) error { err := ErrUnreachable done := make(chan bool) go func() { _, err = s.testConnectionAndCreateClient(hostname, username, password) close(done) }() + t := time.NewTimer(timeout) + defer t.Stop() select { - case <-time.After(200 * time.Millisecond): + case <-t.C: return err case <-done: return err diff --git a/ui/controller/controller.go b/ui/controller/controller.go index adc7753..c226ad7 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -6,6 +6,7 @@ import ( "supersonic/backend" "supersonic/ui/dialogs" "supersonic/ui/util" + "time" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" @@ -43,16 +44,20 @@ func (m Controller) PromptForFirstServer() { d := dialogs.NewAddEditServerDialog("Connect to Server", nil) pop := widget.NewModalPopUp(d, m.MainWindow.Canvas()) d.OnSubmit = func() { - if m.testConnectionAndUpdateDialogError(d) { - // connection is good - pop.Hide() - server := m.App.Config.AddServer(d.Nickname, d.Host, d.Username) - if err := m.App.ServerManager.SetServerPassword(server, d.Password); err != nil { - log.Printf("error setting keyring credentials: %v", err) - // TODO: handle? + d.DisableSubmit() + go func() { + if m.testConnectionAndUpdateDialogText(d) { + // connection is good + pop.Hide() + server := m.App.Config.AddServer(d.Nickname, d.Host, d.Username) + if err := m.App.ServerManager.SetServerPassword(server, d.Password); err != nil { + log.Printf("error setting keyring credentials: %v", err) + // TODO: handle? + } + m.DoConnectToServerWorkflow(server) } - m.DoConnectToServerWorkflow(server) - } + d.EnableSubmit() + }() } pop.Show() } @@ -110,29 +115,38 @@ func (m Controller) PromptForLoginAndConnect() { d := dialogs.NewLoginDialog(m.App.Config.Servers) pop := widget.NewModalPopUp(d, m.MainWindow.Canvas()) d.OnSubmit = func(server *backend.ServerConfig, password string) { - err := m.App.ServerManager.TestConnectionAndAuth(server.Hostname, server.Username, password) - if err == backend.ErrUnreachable { - d.SetErrorText("Server unreachable") - } else if err != nil { - d.SetErrorText("Authentication failed") - } else { - pop.Hide() - m.trySetPasswordAndConnectToServer(server, password) - } + d.DisableSubmit() + d.SetInfoText("Testing connection...") + go func() { + err := m.App.ServerManager.TestConnectionAndAuth(server.Hostname, server.Username, password, 5*time.Second) + if err == backend.ErrUnreachable { + d.SetErrorText("Server unreachable") + } else if err != nil { + d.SetErrorText("Authentication failed") + } else { + pop.Hide() + m.trySetPasswordAndConnectToServer(server, password) + } + d.EnableSubmit() + }() } d.OnEditServer = func(server *backend.ServerConfig) { pop.Hide() editD := dialogs.NewAddEditServerDialog("Edit server", server) editPop := widget.NewModalPopUp(editD, m.MainWindow.Canvas()) editD.OnSubmit = func() { - if m.testConnectionAndUpdateDialogError(editD) { - // connection is good - editPop.Hide() - server.Hostname = editD.Host - server.Nickname = editD.Nickname - server.Username = editD.Username - m.trySetPasswordAndConnectToServer(server, editD.Password) - } + d.DisableSubmit() + go func() { + if m.testConnectionAndUpdateDialogText(editD) { + // connection is good + editPop.Hide() + server.Hostname = editD.Host + server.Nickname = editD.Nickname + server.Username = editD.Username + m.trySetPasswordAndConnectToServer(server, editD.Password) + } + d.EnableSubmit() + }() } editPop.Show() } @@ -150,7 +164,7 @@ func (c Controller) trySetPasswordAndConnectToServer(server *backend.ServerConfi } func (c Controller) tryConnectToServer(server *backend.ServerConfig, password string) error { - if err := c.App.ServerManager.TestConnectionAndAuth(server.Hostname, server.Username, password); err != nil { + if err := c.App.ServerManager.TestConnectionAndAuth(server.Hostname, server.Username, password, 10*time.Second); err != nil { return err } if err := c.App.ServerManager.ConnectToServer(server, password); err != nil { @@ -160,8 +174,9 @@ func (c Controller) tryConnectToServer(server *backend.ServerConfig, password st return nil } -func (c Controller) testConnectionAndUpdateDialogError(dlg *dialogs.AddEditServerDialog) bool { - err := c.App.ServerManager.TestConnectionAndAuth(dlg.Host, dlg.Username, dlg.Password) +func (c Controller) testConnectionAndUpdateDialogText(dlg *dialogs.AddEditServerDialog) bool { + dlg.SetInfoText("Testing connection...") + err := c.App.ServerManager.TestConnectionAndAuth(dlg.Host, dlg.Username, dlg.Password, 5*time.Second) if err == backend.ErrUnreachable { dlg.SetErrorText("Could not reach server (wrong hostname?)") return false diff --git a/ui/dialogs/addeditserverdialog.go b/ui/dialogs/addeditserverdialog.go index 4f81fa9..2417600 100644 --- a/ui/dialogs/addeditserverdialog.go +++ b/ui/dialogs/addeditserverdialog.go @@ -20,8 +20,9 @@ type AddEditServerDialog struct { Password string OnSubmit func() - errPromptText *widget.RichText - container *fyne.Container + submitBtn *widget.Button + promptText *widget.RichText + container *fyne.Container } var _ fyne.Widget = (*AddEditServerDialog)(nil) @@ -43,15 +44,14 @@ func NewAddEditServerDialog(title string, prefillServer *backend.ServerConfig) * hostField.SetPlaceHolder("http://localhost:4533") userField := widget.NewEntryWithData(binding.BindString(&a.Username)) passField := widget.NewPasswordEntry() - submit := widget.NewButton("Enter", func() { + a.submitBtn = widget.NewButton("Enter", func() { a.Password = passField.Text if a.OnSubmit != nil { a.OnSubmit() } }) - a.errPromptText = widget.NewRichTextWithText("") - a.errPromptText.Segments[0].(*widget.TextSegment).Style.ColorName = theme.ColorNameError - a.errPromptText.Hidden = true + a.promptText = widget.NewRichTextWithText("") + a.promptText.Hidden = true a.container = container.NewVBox( container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()), @@ -67,21 +67,41 @@ func NewAddEditServerDialog(title string, prefillServer *backend.ServerConfig) * ), widget.NewSeparator(), container.NewHBox( - a.errPromptText, + a.promptText, layout.NewSpacer(), - submit), + a.submitBtn), ) return a } +func (a *AddEditServerDialog) SetInfoText(text string) { + a.doSetPromptText(text, theme.ColorNameForeground) +} + func (a *AddEditServerDialog) SetErrorText(text string) { - a.errPromptText.Segments[0].(*widget.TextSegment).Text = text + a.doSetPromptText(text, theme.ColorNameError) +} + +func (a *AddEditServerDialog) EnableSubmit() { + a.submitBtn.Enable() + a.submitBtn.Refresh() +} + +func (a *AddEditServerDialog) DisableSubmit() { + a.submitBtn.Disable() + a.submitBtn.Refresh() +} + +func (a *AddEditServerDialog) doSetPromptText(text string, color fyne.ThemeColorName) { + ts := a.promptText.Segments[0].(*widget.TextSegment) + ts.Text = text + ts.Style.ColorName = color if text != "" { - a.errPromptText.Show() + a.promptText.Show() } else { - a.errPromptText.Hide() + a.promptText.Hide() } - a.errPromptText.Refresh() + a.promptText.Refresh() } func (a *AddEditServerDialog) MinSize() fyne.Size { diff --git a/ui/dialogs/logindialog.go b/ui/dialogs/logindialog.go index 08c087e..8289f54 100644 --- a/ui/dialogs/logindialog.go +++ b/ui/dialogs/logindialog.go @@ -16,10 +16,11 @@ type LoginDialog struct { OnSubmit func(server *backend.ServerConfig, password string) OnEditServer func(server *backend.ServerConfig) - servers []*backend.ServerConfig - serverSelect *widget.Select - passField *widget.Entry - errPromptText *widget.RichText + servers []*backend.ServerConfig + serverSelect *widget.Select + passField *widget.Entry + promptText *widget.RichText + submitBtn *widget.Button container *fyne.Container } @@ -39,11 +40,11 @@ func NewLoginDialog(servers []*backend.ServerConfig) *LoginDialog { l.serverSelect.SetSelectedIndex(0) editBtn := widget.NewButtonWithIcon("", theme.DocumentCreateIcon(), l.onEditServer) l.passField = widget.NewPasswordEntry() - okBtn := widget.NewButton("OK", l.onSubmit) + l.submitBtn = widget.NewButton("OK", l.onSubmit) - l.errPromptText = widget.NewRichTextWithText("") - l.errPromptText.Segments[0].(*widget.TextSegment).Style.ColorName = theme.ColorNameError - l.errPromptText.Hidden = true + l.promptText = widget.NewRichTextWithText("") + l.promptText.Segments[0].(*widget.TextSegment).Style.ColorName = theme.ColorNameError + l.promptText.Hidden = true l.container = container.NewVBox( container.NewHBox(layout.NewSpacer(), titleLabel, layout.NewSpacer()), @@ -53,19 +54,39 @@ func NewLoginDialog(servers []*backend.ServerConfig) *LoginDialog { widget.NewLabel("Password"), l.passField), widget.NewSeparator(), - container.NewHBox(l.errPromptText, layout.NewSpacer(), okBtn), + container.NewHBox(l.promptText, layout.NewSpacer(), l.submitBtn), ) return l } +func (l *LoginDialog) SetInfoText(text string) { + l.doSetPromptText(text, theme.ColorNameForeground) +} + func (l *LoginDialog) SetErrorText(text string) { - l.errPromptText.Segments[0].(*widget.TextSegment).Text = text + l.doSetPromptText(text, theme.ColorNameError) +} + +func (l *LoginDialog) EnableSubmit() { + l.submitBtn.Enable() + l.submitBtn.Refresh() +} + +func (l *LoginDialog) DisableSubmit() { + l.submitBtn.Disable() + l.submitBtn.Refresh() +} + +func (l *LoginDialog) doSetPromptText(text string, color fyne.ThemeColorName) { + ts := l.promptText.Segments[0].(*widget.TextSegment) + ts.Text = text + ts.Style.ColorName = color if text != "" { - l.errPromptText.Show() + l.promptText.Show() } else { - l.errPromptText.Hide() + l.promptText.Hide() } - l.errPromptText.Refresh() + l.promptText.Refresh() } func (l *LoginDialog) CreateRenderer() fyne.WidgetRenderer {