mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-19 05:09:06 +02:00
fix(ui): keep main window closable after a native dialog on Windows (#6319)
A native Windows MessageBox attached to a parent window disables that window (WS_DISABLED) for its lifetime and re-enables it on dismissal. When the parent is the main window — whose WindowClosing hook hides instead of closes — the enable/hide sequence races and leaves the window unable to process its close (X) button afterwards, so e.g. a rejected login error dialog left the main window stuck open. Route all native dialogs through src/lib/dialogs.ts, which forces Detached: true on Windows (NULL owner, no window ever disabled) and is a no-op on macOS/Linux (keeps the attached sheet-style presentation).
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { Dialogs, Events } from "@wailsio/runtime";
|
||||
import { Events } from "@wailsio/runtime";
|
||||
import { errorDialog } from "@/lib/dialogs.ts";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { Connection } from "@bindings/services";
|
||||
import { Button } from "@/components/buttons/Button";
|
||||
@@ -25,7 +26,7 @@ export default function LoginWaitingForBrowserDialog() {
|
||||
|
||||
const reportOpenFailure = useCallback(
|
||||
(e: unknown) => {
|
||||
void Dialogs.Error({
|
||||
void errorDialog({
|
||||
Title: t("browserLogin.openFailedTitle"),
|
||||
Message: formatErrorMessage(e),
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Dialogs, Events } from "@wailsio/runtime";
|
||||
import { Events } from "@wailsio/runtime";
|
||||
import { Connection, WindowManager } from "@bindings/services";
|
||||
import i18next from "@/lib/i18n";
|
||||
import { errorDialog } from "@/lib/dialogs.ts";
|
||||
import { ToggleSwitch } from "@/components/switches/ToggleSwitch.tsx";
|
||||
import { useStatus } from "@/contexts/StatusContext.tsx";
|
||||
import { useProfile } from "@/contexts/ProfileContext.tsx";
|
||||
@@ -105,7 +106,7 @@ async function startLogin(): Promise<void> {
|
||||
} catch (e) {
|
||||
WindowManager.CloseBrowserLogin().catch(console.error);
|
||||
if (cancelled) return;
|
||||
await Dialogs.Error({
|
||||
await errorDialog({
|
||||
Title: i18next.t("connect.error.loginTitle"),
|
||||
Message: errorMessage(e),
|
||||
});
|
||||
@@ -192,7 +193,7 @@ export const MainConnectionStatusSwitch = () => {
|
||||
} catch (e) {
|
||||
setAction(null);
|
||||
await refresh();
|
||||
await Dialogs.Error({
|
||||
await errorDialog({
|
||||
Title: t("connect.error.connectTitle"),
|
||||
Message: errorMessage(e),
|
||||
});
|
||||
@@ -212,7 +213,7 @@ export const MainConnectionStatusSwitch = () => {
|
||||
} catch (e) {
|
||||
setAction(null);
|
||||
await refresh();
|
||||
await Dialogs.Error({
|
||||
await errorDialog({
|
||||
Title: t("connect.error.disconnectTitle"),
|
||||
Message: errorMessage(e),
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { forwardRef, useLayoutEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Dialogs } from "@wailsio/runtime";
|
||||
import { errorDialog } from "@/lib/dialogs.ts";
|
||||
import * as Popover from "@radix-ui/react-popover";
|
||||
import * as ScrollArea from "@radix-ui/react-scroll-area";
|
||||
import { Command } from "cmdk";
|
||||
@@ -36,7 +36,7 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
|
||||
try {
|
||||
await fn();
|
||||
} catch (e) {
|
||||
await Dialogs.Error({
|
||||
await errorDialog({
|
||||
Title: title,
|
||||
Message: formatErrorMessage(e),
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useLayoutEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Dialogs } from "@wailsio/runtime";
|
||||
import { errorDialog, warningDialog } from "@/lib/dialogs.ts";
|
||||
import { CircleMinus, PlusCircle, Trash2, UserCircle } from "lucide-react";
|
||||
import type { Profile } from "@bindings/services/models.js";
|
||||
import { Badge } from "@/components/Badge";
|
||||
@@ -44,7 +44,7 @@ export function ProfilesTab() {
|
||||
try {
|
||||
await fn();
|
||||
} catch (e) {
|
||||
await Dialogs.Error({
|
||||
await errorDialog({
|
||||
Title: title,
|
||||
Message: formatErrorMessage(e),
|
||||
});
|
||||
@@ -56,7 +56,7 @@ export function ProfilesTab() {
|
||||
const handleDeregister = async (name: string) => {
|
||||
const cancelLabel = i18next.t("common.cancel");
|
||||
const confirmLabel = i18next.t("profile.deregister.confirm");
|
||||
const result = await Dialogs.Warning({
|
||||
const result = await warningDialog({
|
||||
Title: i18next.t("profile.deregister.title"),
|
||||
Message: i18next.t("profile.deregister.message", { name }),
|
||||
Buttons: [
|
||||
@@ -72,7 +72,7 @@ export function ProfilesTab() {
|
||||
if (name === DEFAULT_PROFILE) return;
|
||||
const cancelLabel = i18next.t("common.cancel");
|
||||
const confirmLabel = i18next.t("common.delete");
|
||||
const result = await Dialogs.Warning({
|
||||
const result = await warningDialog({
|
||||
Title: i18next.t("profile.delete.title"),
|
||||
Message: i18next.t("profile.delete.message", { name }),
|
||||
Buttons: [
|
||||
@@ -89,7 +89,7 @@ export function ProfilesTab() {
|
||||
await addProfile(name);
|
||||
await switchProfile(name);
|
||||
} catch (e) {
|
||||
await Dialogs.Error({
|
||||
await errorDialog({
|
||||
Title: i18next.t("profile.error.createTitle"),
|
||||
Message: formatErrorMessage(e),
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { Dialogs } from "@wailsio/runtime";
|
||||
import { errorDialog } from "@/lib/dialogs.ts";
|
||||
import { ClockIcon } from "lucide-react";
|
||||
import { Button } from "@/components/buttons/Button";
|
||||
import { ConfirmDialog } from "@/components/dialog/ConfirmDialog";
|
||||
@@ -89,7 +89,7 @@ export default function SessionAboutToExpireDialog() {
|
||||
}
|
||||
WindowManager.CloseSessionAboutToExpire().catch(console.error);
|
||||
} catch (e) {
|
||||
await Dialogs.Error({
|
||||
await errorDialog({
|
||||
Title: t("sessionAboutToExpire.extendFailedTitle"),
|
||||
Message: formatErrorMessage(e),
|
||||
});
|
||||
@@ -110,7 +110,7 @@ export default function SessionAboutToExpireDialog() {
|
||||
});
|
||||
WindowManager.CloseSessionAboutToExpire().catch(console.error);
|
||||
} catch (e) {
|
||||
await Dialogs.Error({
|
||||
await errorDialog({
|
||||
Title: t("sessionAboutToExpire.logoutFailedTitle"),
|
||||
Message: formatErrorMessage(e),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user