mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Polish sidebar update and relay cards (#1009)
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
* Deliberately uses `relayClient.preconnect()` + `queryClient.invalidateQueries()`
|
||||
* rather than the full `reconnectWorkspace()` path, which unmounts the entire
|
||||
* React tree and clears drafts. The goal here is a transparent re-handshake
|
||||
* when WARP VPN comes back online; the user should not lose their in-progress
|
||||
* when the transport comes back online; the user should not lose their in-progress
|
||||
* compose state.
|
||||
*/
|
||||
|
||||
@@ -16,8 +16,30 @@ import { toast } from "sonner";
|
||||
|
||||
import { relayClient } from "@/shared/api/relayClient";
|
||||
|
||||
const RECONNECT_HOOK_TIMEOUT_MS = 20_000;
|
||||
const RELAY_PRECONNECT_TIMEOUT_MS = 15_000;
|
||||
|
||||
function withTimeout<T>(
|
||||
promise: Promise<T>,
|
||||
timeoutMs: number,
|
||||
label: string,
|
||||
): Promise<T> {
|
||||
let timeoutId: number | null = null;
|
||||
const timeout = new Promise<never>((_, reject) => {
|
||||
timeoutId = window.setTimeout(() => {
|
||||
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
||||
}, timeoutMs);
|
||||
});
|
||||
|
||||
return Promise.race([promise, timeout]).finally(() => {
|
||||
if (timeoutId !== null) {
|
||||
window.clearTimeout(timeoutId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function useReconnectRelay(): {
|
||||
reconnect: () => Promise<void>;
|
||||
reconnect: () => Promise<boolean>;
|
||||
isPending: boolean;
|
||||
} {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -28,25 +50,44 @@ export function useReconnectRelay(): {
|
||||
const inFlightRef = React.useRef(false);
|
||||
|
||||
const reconnect = React.useCallback(async () => {
|
||||
if (inFlightRef.current) return;
|
||||
if (inFlightRef.current) return false;
|
||||
inFlightRef.current = true;
|
||||
setIsPending(true);
|
||||
try {
|
||||
// Run transport-layer reconnect hook (e.g. WARP VPN re-auth for internal builds).
|
||||
// Run the transport-layer reconnect hook configured by internal builds.
|
||||
// No-op in OSS builds. Non-fatal — transport failure shouldn't block relay reconnect.
|
||||
try {
|
||||
await invoke("relay_reconnect_hook");
|
||||
await withTimeout(
|
||||
invoke("relay_reconnect_hook"),
|
||||
RECONNECT_HOOK_TIMEOUT_MS,
|
||||
"reconnect hook",
|
||||
);
|
||||
} catch (err) {
|
||||
console.warn("[useReconnectRelay] reconnect hook failed:", err);
|
||||
}
|
||||
|
||||
await relayClient.preconnect();
|
||||
await queryClient.invalidateQueries();
|
||||
await withTimeout(
|
||||
relayClient.preconnect(),
|
||||
RELAY_PRECONNECT_TIMEOUT_MS,
|
||||
"relay preconnect",
|
||||
);
|
||||
// Let callers render the recovered/connected state before refetching the
|
||||
// sidebar data. The refetch can briefly swap the sidebar into loading UI.
|
||||
window.setTimeout(() => {
|
||||
void queryClient.invalidateQueries().catch((error) => {
|
||||
console.error(
|
||||
"[useReconnectRelay] failed to refresh queries after reconnect:",
|
||||
error,
|
||||
);
|
||||
});
|
||||
}, 0);
|
||||
// No success toast — the banner auto-hides once the connection state
|
||||
// transitions back to "connected", which is the user-visible confirmation.
|
||||
return true;
|
||||
} catch (err) {
|
||||
toast.error("Reconnect failed — check your VPN or network.");
|
||||
toast.error("Reconnect failed — check your network.");
|
||||
console.error("[useReconnectRelay] reconnect failed:", err);
|
||||
return false;
|
||||
} finally {
|
||||
inFlightRef.current = false;
|
||||
setIsPending(false);
|
||||
|
||||
Reference in New Issue
Block a user