refactor: wrap tunnel connect/disconnect operations in spawn_blocking and add pending state UI feedback

Move tunnel_manager::connect and disconnect calls into spawn_blocking tasks to prevent blocking async runtime. Clone app handle and profile path before spawning. Add map_err for task join failures.

Add tunnelActionPending state to track in-progress tunnel operations. Pass busy prop to AppHeader and disable sync/logout/connect buttons during tunnel actions. Update connect button text to show "
This commit is contained in:
2026-03-18 12:35:25 +01:00
parent 10dbd186ed
commit 0ac93dfeb6
3 changed files with 26 additions and 8 deletions

View File

@@ -1,6 +1,7 @@
import { ActionButton } from "./ActionButton";
type AppHeaderProps = {
busy: boolean;
enrolled: boolean;
connected: boolean;
syncing: boolean;
@@ -10,6 +11,7 @@ type AppHeaderProps = {
};
export function AppHeader({
busy,
enrolled,
connected,
syncing,
@@ -36,10 +38,10 @@ export function AppHeader({
<div className="header-actions-secondary">
{enrolled ? (
<>
<ActionButton disabled={syncing} onClick={onSync} variant="secondary">
<ActionButton disabled={busy || syncing} onClick={onSync} variant="secondary">
{syncing ? "Syncing..." : "Sync"}
</ActionButton>
<ActionButton onClick={onLogout} variant="ghost">
<ActionButton disabled={busy || syncing} onClick={onLogout} variant="ghost">
Logout
</ActionButton>
</>
@@ -47,11 +49,11 @@ export function AppHeader({
</div>
<div className="header-actions-primary">
<ActionButton
disabled={!enrolled}
disabled={!enrolled || busy || syncing}
onClick={onToggleConnection}
variant={connected ? "danger" : "primary"}
>
{!enrolled ? "Provision first" : connected ? "Disconnect" : "Connect"}
{!enrolled ? "Provision first" : busy ? connected ? "Disconnecting..." : "Connecting..." : connected ? "Disconnect" : "Connect"}
</ActionButton>
</div>
</div>