From 04d2e6e429fe763dd3adbafbf3f384b9c2ae0e97 Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 6 Jul 2026 19:34:29 +0800 Subject: [PATCH] fix: correct connect/disconnect button state on connection lifecycle - Enable disconnect button immediately when connect is clicked, so the user can cancel without waiting for the StateConnecting IPC event. - Disable disconnect button on all error paths in the connect goroutine to restore the default disconnected button state. - Capture ipcClient once at eventLoop start instead of re-reading every iteration, preventing a second onConnect call from making the old event loop jump to the new client and consume events concurrently. - In eventLoop recv error recovery, only reset button states if the current ipcClient still matches the captured one, so a stale disconnection does not clobber the state of a newer session. - Disable connect button in StateReconnecting case (was missing). - Remove WriteOK from daemon startSession: the StateConnecting event already serves as the implicit acknowledgment; the OK response was being read by eventLoop as a no-op event, adding unnecessary noise. --- internal/daemon/daemon.go | 1 - internal/ui/view.go | 37 ++++++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 152e9e2..056badc 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -155,7 +155,6 @@ func (d *daemon) startSession(conn net.Conn, req ipc.Request) { d.session = nil return } - _ = ipc.WriteOK(conn) } func (d *daemon) stopSession() { diff --git a/internal/ui/view.go b/internal/ui/view.go index d287780..9b50ba3 100644 --- a/internal/ui/view.go +++ b/internal/ui/view.go @@ -75,6 +75,7 @@ func (a *App) onConnect() { } a.connectBtn.Disable() + a.disconnectBtn.Enable() a.stateLabel.SetText(i18n.T("StateConnecting")) go func() { @@ -85,6 +86,7 @@ func (a *App) onConnect() { showError(i18n.T("DlgDaemonError"), err.Error(), a.window) a.stateLabel.SetText(i18n.T("StateDisconnected")) a.connectBtn.Enable() + a.disconnectBtn.Disable() }) return } @@ -102,6 +104,7 @@ func (a *App) onConnect() { a.window) a.stateLabel.SetText(i18n.T("StateDisconnected")) a.connectBtn.Enable() + a.disconnectBtn.Disable() }) return } @@ -121,6 +124,7 @@ func (a *App) onConnect() { showError(i18n.T("DlgIPCError"), err.Error(), a.window) a.stateLabel.SetText(i18n.T("StateDisconnected")) a.connectBtn.Enable() + a.disconnectBtn.Disable() }) return } @@ -146,23 +150,29 @@ func (a *App) onDisconnect() { // eventLoop reads IPC events from the daemon and updates the UI. func (a *App) eventLoop() { + a.mu.Lock() + client := a.ipcClient + a.mu.Unlock() + if client == nil { + return + } + for { - a.mu.Lock() - client := a.ipcClient - a.mu.Unlock() - if client == nil { - return - } ev, err := client.Recv() if err != nil { fyne.Do(func() { - a.stateLabel.SetText(i18n.T("StateDisconnected")) - a.ipLabel.SetText(i18n.T("IpNone")) - a.uptimeLabel.SetText(i18n.T("UptimeNone")) - a.rxLabel.SetText(i18n.T("RxZero")) - a.txLabel.SetText(i18n.T("TxZero")) - a.connectBtn.Enable() - a.disconnectBtn.Disable() + a.mu.Lock() + current := a.ipcClient + a.mu.Unlock() + if current == client { + a.stateLabel.SetText(i18n.T("StateDisconnected")) + a.ipLabel.SetText(i18n.T("IpNone")) + a.uptimeLabel.SetText(i18n.T("UptimeNone")) + a.rxLabel.SetText(i18n.T("RxZero")) + a.txLabel.SetText(i18n.T("TxZero")) + a.connectBtn.Enable() + a.disconnectBtn.Disable() + } }) return } @@ -201,6 +211,7 @@ func (a *App) applyState(state string) { a.disconnectBtn.Enable() case stats.StateReconnecting: a.stateLabel.SetText(i18n.T("StateReconnecting")) + a.connectBtn.Disable() a.disconnectBtn.Enable() case stats.StateDisconnected: a.stateLabel.SetText(i18n.T("StateDisconnected"))