diff --git a/client/ui/frontend/src/hooks/useAutoSizeWindow.ts b/client/ui/frontend/src/hooks/useAutoSizeWindow.ts index d4f4d80b2..693f5725f 100644 --- a/client/ui/frontend/src/hooks/useAutoSizeWindow.ts +++ b/client/ui/frontend/src/hooks/useAutoSizeWindow.ts @@ -5,7 +5,11 @@ import { isLinux } from "@/lib/platform"; // Sizes the current Wails window to the measured content height (keeping `width`), // then shows it. Re-applies on content resize and language change. -export function useAutoSizeWindow(width: number, ready: boolean = true) { +export function useAutoSizeWindow( + width: number, + ready: boolean = true, + showWhenSized: boolean = true, +) { const ref = useRef(null); useLayoutEffect(() => { const el = ref.current; @@ -33,7 +37,7 @@ export function useAutoSizeWindow(width: number, ready: b await Window.SetMaxSize(width, targetH); } await Window.SetSize(width, targetH); - showOnce(); + if (showWhenSized) showOnce(); } catch { // window gone / not ready — ignore } @@ -55,6 +59,6 @@ export function useAutoSizeWindow(width: number, ready: b cancelAnimationFrame(raf2); i18next.off("languageChanged", scheduleApply); }; - }, [width, ready]); + }, [width, ready, showWhenSized]); return ref; } diff --git a/client/ui/frontend/src/modules/login/LoginWaitingForBrowserDialog.tsx b/client/ui/frontend/src/modules/login/LoginWaitingForBrowserDialog.tsx index efbd1ee84..d1a5da1d0 100644 --- a/client/ui/frontend/src/modules/login/LoginWaitingForBrowserDialog.tsx +++ b/client/ui/frontend/src/modules/login/LoginWaitingForBrowserDialog.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef } from "react"; +import { useCallback } from "react"; import { useTranslation } from "react-i18next"; import { useSearchParams } from "react-router-dom"; import { Events } from "@wailsio/runtime"; @@ -20,8 +20,7 @@ export default function LoginWaitingForBrowserDialog() { const { t } = useTranslation(); const [params] = useSearchParams(); const uri = params.get("uri") ?? ""; - const contentRef = useAutoSizeWindow(WINDOW_WIDTH); - const openedRef = useRef(false); + const contentRef = useAutoSizeWindow(WINDOW_WIDTH, true, false); const reportOpenFailure = useCallback( (e: unknown) => { @@ -33,13 +32,6 @@ export default function LoginWaitingForBrowserDialog() { [t], ); - // Open the browser only after mount, or it lands on top of the still-hidden popup. - useEffect(() => { - if (!uri || openedRef.current) return; - openedRef.current = true; - Connection.OpenURL(uri).catch(reportOpenFailure); - }, [uri, reportOpenFailure]); - const tryAgain = useCallback(() => { if (!uri) return; Connection.OpenURL(uri).catch(reportOpenFailure); diff --git a/client/ui/services/connection.go b/client/ui/services/connection.go index f78ce4c0f..f6a8eca72 100644 --- a/client/ui/services/connection.go +++ b/client/ui/services/connection.go @@ -205,19 +205,7 @@ func (s *Connection) Down(ctx context.Context) error { // window.open, so the SSO verification page can't pop inline. Honors $BROWSER // before the platform default. func (s *Connection) OpenURL(url string) error { - if browser := os.Getenv("BROWSER"); browser != "" { - return exec.Command(browser, url).Start() - } - switch runtime.GOOS { - case "windows": - return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() - case "darwin": - return exec.Command("open", url).Start() - case "linux": - return exec.Command("xdg-open", url).Start() - default: - return fmt.Errorf("unsupported platform") - } + return openURL(url) } func (s *Connection) Logout(ctx context.Context, p LogoutParams) error { @@ -288,3 +276,19 @@ func (s *Connection) waitSSOLogin(ctx context.Context, p WaitSSOParams) (string, func (s *Connection) classifyDaemonError(err error) *ClientError { return s.classifier.classify(err) } + +func openURL(url string) error { + if browser := os.Getenv("BROWSER"); browser != "" { + return exec.Command(browser, url).Start() + } + switch runtime.GOOS { + case "windows": + return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() + case "darwin": + return exec.Command("open", url).Start() + case "linux": + return exec.Command("xdg-open", url).Start() + default: + return fmt.Errorf("unsupported platform") + } +} diff --git a/client/ui/services/windowmanager.go b/client/ui/services/windowmanager.go index 94dba6038..1a707df17 100644 --- a/client/ui/services/windowmanager.go +++ b/client/ui/services/windowmanager.go @@ -245,7 +245,11 @@ func (s *WindowManager) OpenBrowserLogin(uri string) { s.app.Event.Emit(EventBrowserLoginCancel) } }) - s.centerOnCursorScreen(s.browserLogin) + s.centerOnCursorScreen(bl) + bl.Show() + bl.Focus() + log.Debugf("browser-login popup created and shown") + s.openBrowser(uri) return } if uri != "" { @@ -254,6 +258,20 @@ func (s *WindowManager) OpenBrowserLogin(uri string) { s.centerOnCursorScreen(s.browserLogin) s.browserLogin.Show() s.browserLogin.Focus() + log.Debugf("browser-login popup reused") + s.openBrowser(uri) +} + +func (s *WindowManager) openBrowser(uri string) { + if uri == "" { + return + } + go func() { + if err := openURL(uri); err != nil { + log.Errorf("open browser for SSO login: %v", err) + s.OpenError(s.title("browserLogin.openFailedTitle"), err.Error(), "") + } + }() } // BrowserLoginWindow returns the live SSO popup, or nil. While non-nil it is the