From 67988c240747bb0bf18bcf6f713f5bb31341cafd Mon Sep 17 00:00:00 2001 From: Zoltan Papp Date: Wed, 13 May 2026 15:54:33 +0200 Subject: [PATCH] [client/ui] Make profile Switch sync, Down+Up async in ProfileSwitcher Switch RPC errors are now returned synchronously to the caller so the tray can show a toast immediately on invalid-profile or other early failures. Down and Up run in a background goroutine so the caller returns fast; Up still uses async=true so the goroutine is short-lived. --- client/ui/services/profileswitcher.go | 30 +++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/client/ui/services/profileswitcher.go b/client/ui/services/profileswitcher.go index 12d92101f..12bea0181 100644 --- a/client/ui/services/profileswitcher.go +++ b/client/ui/services/profileswitcher.go @@ -62,21 +62,25 @@ func (s *ProfileSwitcher) SwitchActive(ctx context.Context, p ProfileRef) error return fmt.Errorf("switch profile %q: %w", p.ProfileName, err) } - if needsDown { - if err := s.connection.Down(ctx); err != nil { - log.Errorf("profileswitcher: Down: %v", err) + // Down and Up run in a goroutine so the caller returns immediately after + // the Switch RPC. Up uses async mode so the goroutine itself is short-lived. + go func() { + bgCtx := context.Background() + if needsDown { + if err := s.connection.Down(bgCtx); err != nil { + log.Errorf("profileswitcher: Down: %v", err) + } } - } - - if wasActive { - if err := s.connection.Up(ctx, UpParams{ - ProfileName: p.ProfileName, - Username: p.Username, - Async: true, - }); err != nil { - return fmt.Errorf("reconnect %q: %w", p.ProfileName, err) + if wasActive { + if err := s.connection.Up(bgCtx, UpParams{ + ProfileName: p.ProfileName, + Username: p.Username, + Async: true, + }); err != nil { + log.Errorf("profileswitcher: Up %s: %v", p.ProfileName, err) + } } - } + }() return nil }