update dialogs, hide main window on browser login, keep state as disconnected when needslogin

This commit is contained in:
Eduard Gert
2026-05-18 16:31:59 +02:00
parent 741ce8581d
commit 5b71a4f2ad
22 changed files with 444 additions and 197 deletions

View File

@@ -4,14 +4,17 @@ import { useSearchParams } from "react-router-dom";
import { Events } from "@wailsio/runtime";
import { ClockIcon } from "lucide-react";
import { Button } from "@/components/Button";
import {
Connection,
Profiles as ProfilesSvc,
WindowManager,
} from "@bindings/services";
import { ConfirmDialog } from "@/components/ConfirmDialog";
import { DialogActions } from "@/components/DialogActions";
import { DialogDescription } from "@/components/DialogDescription";
import { DialogHeading } from "@/components/DialogHeading";
import { SquareIcon } from "@/components/SquareIcon";
import { Connection, Profiles as ProfilesSvc, WindowManager } from "@bindings/services";
import { useAutoSizeWindow } from "@/lib/useAutoSizeWindow";
const EVENT_TRIGGER_LOGIN = "trigger-login";
const DEFAULT_SECONDS = 360;
const WINDOW_WIDTH = 360;
function formatMMSS(seconds: number): string {
const s = Math.max(0, seconds | 0);
@@ -20,8 +23,9 @@ function formatMMSS(seconds: number): string {
return `${String(m).padStart(2, "0")}:${String(r).padStart(2, "0")}`;
}
export default function SessionAboutToExpire() {
export default function SessionAboutToExpireDialog() {
const { t } = useTranslation();
const contentRef = useAutoSizeWindow<HTMLDivElement>(WINDOW_WIDTH);
const [params] = useSearchParams();
const initialSeconds = useMemo(() => {
const raw = params.get("seconds");
@@ -66,53 +70,48 @@ export default function SessionAboutToExpire() {
}, []);
return (
<div
className={
"h-screen w-full flex flex-col items-center justify-center text-center px-6 py-8 bg-nb-gray-950"
}
>
<div
className={
"h-12 w-12 rounded-full flex items-center justify-center bg-nb-gray-900 text-netbird mb-4"
}
>
<ClockIcon size={22} />
<ConfirmDialog ref={contentRef}>
<SquareIcon icon={ClockIcon} className={"mt-4"} />
<div className={"flex flex-col items-center gap-1"}>
<DialogHeading>
{expired
? t("sessionAboutToExpire.expired")
: t("sessionAboutToExpire.title")}
</DialogHeading>
<DialogDescription>
{t("sessionAboutToExpire.description")}
</DialogDescription>
</div>
<h1 className={"text-base font-semibold text-nb-gray-100"}>
{expired
? t("sessionAboutToExpire.expired")
: t("sessionAboutToExpire.title")}
</h1>
<p className={"text-xs text-nb-gray-400 mt-1.5 max-w-[20rem] leading-snug"}>
{t("sessionAboutToExpire.description")}
</p>
<div
className={
"mt-5 font-mono text-3xl tabular-nums text-nb-gray-100 tracking-wider"
"font-mono font-semibold text-2xl tabular-nums text-nb-gray-50 tracking-wider"
}
aria-live={"polite"}
>
{formatMMSS(remaining)}
</div>
<div className={"flex gap-2 mt-5 w-full max-w-[18rem]"}>
<Button
variant={"secondary"}
size={"xs"}
className={"flex-1"}
onClick={logout}
>
{t("sessionAboutToExpire.logout")}
</Button>
<DialogActions>
<Button
variant={"primary"}
size={"xs"}
className={"flex-1"}
size={"md"}
className={"w-full"}
onClick={stay}
disabled={expired}
>
{t("sessionAboutToExpire.stay")}
</Button>
</div>
</div>
<Button
variant={"secondary"}
size={"md"}
className={"w-full"}
onClick={logout}
>
{t("sessionAboutToExpire.logout")}
</Button>
</DialogActions>
</ConfirmDialog>
);
}

View File

@@ -0,0 +1,49 @@
import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { Events } from "@wailsio/runtime";
import { AlertCircleIcon } from "lucide-react";
import { Button } from "@/components/Button";
import { ConfirmDialog } from "@/components/ConfirmDialog";
import { DialogActions } from "@/components/DialogActions";
import { DialogDescription } from "@/components/DialogDescription";
import { DialogHeading } from "@/components/DialogHeading";
import { SquareIcon } from "@/components/SquareIcon";
import { WindowManager } from "@bindings/services";
import { useAutoSizeWindow } from "@/lib/useAutoSizeWindow";
const EVENT_TRIGGER_LOGIN = "trigger-login";
const WINDOW_WIDTH = 360;
export default function SessionExpiredDialog() {
const { t } = useTranslation();
const contentRef = useAutoSizeWindow<HTMLDivElement>(WINDOW_WIDTH);
const signIn = useCallback(() => {
void Events.Emit(EVENT_TRIGGER_LOGIN);
WindowManager.CloseSessionExpired().catch(console.error);
}, []);
const later = useCallback(() => {
WindowManager.CloseSessionExpired().catch(console.error);
}, []);
return (
<ConfirmDialog ref={contentRef}>
<SquareIcon icon={AlertCircleIcon} className={"mt-4"} />
<div className={"flex flex-col items-center gap-1"}>
<DialogHeading>{t("sessionExpired.title")}</DialogHeading>
<DialogDescription>{t("sessionExpired.description")}</DialogDescription>
</div>
<DialogActions>
<Button variant={"primary"} size={"md"} className={"w-full"} onClick={signIn}>
{t("sessionExpired.signIn")}
</Button>
<Button variant={"secondary"} size={"md"} className={"w-full"} onClick={later}>
{t("sessionExpired.later")}
</Button>
</DialogActions>
</ConfirmDialog>
);
}

View File

@@ -0,0 +1,71 @@
import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { useSearchParams } from "react-router-dom";
import { Events } from "@wailsio/runtime";
import { Loader2 } from "lucide-react";
import { Connection } from "@bindings/services";
import { Button } from "@/components/Button";
import { ConfirmDialog } from "@/components/ConfirmDialog";
import { DialogActions } from "@/components/DialogActions";
import { DialogDescription } from "@/components/DialogDescription";
import { DialogHeading } from "@/components/DialogHeading";
import { SquareIcon } from "@/components/SquareIcon";
import { useAutoSizeWindow } from "@/lib/useAutoSizeWindow";
const EVENT_CANCEL = "browser-login:cancel";
const WINDOW_WIDTH = 360;
export default function WaitingForBrowserDialog() {
const { t } = useTranslation();
const [params] = useSearchParams();
const uri = params.get("uri") ?? "";
const contentRef = useAutoSizeWindow<HTMLDivElement>(WINDOW_WIDTH);
const tryAgain = useCallback(() => {
if (!uri) return;
Connection.OpenURL(uri).catch(console.error);
}, [uri]);
const cancel = useCallback(() => {
void Events.Emit(EVENT_CANCEL);
}, []);
return (
<ConfirmDialog ref={contentRef}>
<SquareIcon
icon={Loader2}
className={"mt-4 [&_svg]:animate-spin"}
/>
<div className={"flex flex-col items-center gap-2"}>
<DialogHeading className={"text-balance"}>
{t("browserLogin.title")}
</DialogHeading>
<DialogDescription>
{t("browserLogin.notSeeing")}{" "}
<button
type={"button"}
onClick={tryAgain}
disabled={!uri}
className={
"wails-no-draggable text-netbird hover:underline disabled:opacity-40 disabled:cursor-not-allowed"
}
>
{t("browserLogin.tryAgain")}
</button>
</DialogDescription>
</div>
<DialogActions>
<Button
variant={"secondary"}
size={"md"}
className={"w-full"}
onClick={cancel}
>
{t("common.cancel")}
</Button>
</DialogActions>
</ConfirmDialog>
);
}

View File

@@ -1,61 +0,0 @@
import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { Events } from "@wailsio/runtime";
import { ShieldAlertIcon } from "lucide-react";
import { Button } from "@/components/Button";
import { WindowManager } from "@bindings/services";
const EVENT_TRIGGER_LOGIN = "trigger-login";
export default function SessionExpired() {
const { t } = useTranslation();
const signIn = useCallback(() => {
void Events.Emit(EVENT_TRIGGER_LOGIN);
WindowManager.CloseSessionExpired().catch(console.error);
}, []);
const later = useCallback(() => {
WindowManager.CloseSessionExpired().catch(console.error);
}, []);
return (
<div
className={
"h-screen w-full flex flex-col items-center justify-center text-center px-6 py-8 bg-nb-gray-950"
}
>
<div
className={
"h-12 w-12 rounded-full flex items-center justify-center bg-nb-gray-900 text-netbird mb-4"
}
>
<ShieldAlertIcon size={22} />
</div>
<h1 className={"text-base font-semibold text-nb-gray-100"}>
{t("sessionExpired.title")}
</h1>
<p className={"text-xs text-nb-gray-400 mt-1.5 max-w-[20rem] leading-snug"}>
{t("sessionExpired.description")}
</p>
<div className={"flex gap-2 mt-5 w-full max-w-[18rem]"}>
<Button
variant={"secondary"}
size={"xs"}
className={"flex-1"}
onClick={later}
>
{t("sessionExpired.later")}
</Button>
<Button
variant={"primary"}
size={"xs"}
className={"flex-1"}
onClick={signIn}
>
{t("sessionExpired.signIn")}
</Button>
</div>
</div>
);
}