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

@@ -69,6 +69,7 @@ export function App() {
const [loading, setLoading] = useState(false);
const [syncing, setSyncing] = useState(false);
const [selectingProfile, setSelectingProfile] = useState(false);
const [tunnelActionPending, setTunnelActionPending] = useState(false);
const [error, setError] = useState<string | null>(null);
const [connected, setConnected] = useState(false);
const [state, setState] = useState<EnrollmentState | null>(null);
@@ -187,6 +188,7 @@ export function App() {
async function toggleConnection() {
const command = connected ? "disconnect_tunnel" : "connect_tunnel";
setTunnelActionPending(true);
try {
if (!connected) {
const syncedState = await invoke<EnrollmentState>("connect_tunnel");
@@ -213,6 +215,8 @@ export function App() {
}
}
setError(formatInvokeError(err, "Tunnel action failed"));
} finally {
setTunnelActionPending(false);
}
}
@@ -231,6 +235,7 @@ export function App() {
<div className="client-shell">
<div className="app-shell">
<AppHeader
busy={tunnelActionPending}
connected={connected}
enrolled={Boolean(state)}
onLogout={resetEnrollment}
@@ -290,7 +295,7 @@ export function App() {
profiles={state?.availableProfiles ?? []}
resources={state?.resources ?? []}
selectedProfileId={state?.selectedProfileId ?? null}
selectingProfile={selectingProfile}
selectingProfile={selectingProfile || tunnelActionPending}
/>
</div>
</div>