Simplify reconnect handlers and fix connect_error overlay flicker
- Merge handleOnline and the shared branch of handleVisibilityChange into a single handleReturn function. visibilitychange is now a thin guard that calls it only on show. Both events share the same logic: reconnect if socket dropped, or restore UI state + rejoin room + revalidate if the socket survived. - Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires immediately after and resets it to true anyway, causing the "Reconnecting" overlay to flicker off and back on during every retry cycle. https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt
This commit is contained in:
parent
96f02d2643
commit
80ab6a3650
1 changed files with 20 additions and 27 deletions
|
|
@ -57,7 +57,8 @@ export function useDraftSocket(seasonId: string, teamId?: string): UseDraftSocke
|
||||||
socket.on("connect_error", (error) => {
|
socket.on("connect_error", (error) => {
|
||||||
console.error("Socket.IO connection error:", error);
|
console.error("Socket.IO connection error:", error);
|
||||||
setConnectionError(error.message || "Failed to connect to draft server");
|
setConnectionError(error.message || "Failed to connect to draft server");
|
||||||
setIsReconnecting(false);
|
// Don't clear isReconnecting here — reconnect_attempt fires immediately
|
||||||
|
// after and sets it back to true, causing a visible flicker in the overlay.
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.io.on("reconnect_attempt", () => {
|
socket.io.on("reconnect_attempt", () => {
|
||||||
|
|
@ -75,48 +76,40 @@ export function useDraftSocket(seasonId: string, teamId?: string): UseDraftSocke
|
||||||
const handleOffline = () => {
|
const handleOffline = () => {
|
||||||
// Mark as reconnecting immediately — the OS fires this before Socket.IO's
|
// Mark as reconnecting immediately — the OS fires this before Socket.IO's
|
||||||
// heartbeat would detect the drop (~15s later). Note: "offline" can fire
|
// heartbeat would detect the drop (~15s later). Note: "offline" can fire
|
||||||
// spuriously on mobile; handleOnline corrects the state if the socket is
|
// spuriously on mobile; handleReturn corrects the state if the socket is
|
||||||
// still alive when the network returns.
|
// still alive when the network returns.
|
||||||
setIsConnected(false);
|
setIsConnected(false);
|
||||||
setIsReconnecting(true);
|
setIsReconnecting(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnline = () => {
|
// Unified handler for both "network returned" (online event) and
|
||||||
if (socketRef.current?.connected) {
|
// "browser tab/app regained focus" (visibilitychange). Two cases:
|
||||||
// Network blipped but the socket stayed alive (came back within ping timeout window).
|
// - Socket dropped: reconnect immediately, bypassing backoff.
|
||||||
// The socket never disconnected so no "connect" event will fire — correct UI directly.
|
// - Socket alive: restore UI state and rejoin the draft room so the page
|
||||||
|
// revalidates and picks up any events missed while the client was away.
|
||||||
|
const handleReturn = () => {
|
||||||
|
if (!socketRef.current?.connected) {
|
||||||
|
socketRef.current?.connect();
|
||||||
|
} else {
|
||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
setIsReconnecting(false);
|
setIsReconnecting(false);
|
||||||
} else {
|
socketRef.current.emit("join-draft", seasonId, teamId);
|
||||||
// Genuinely disconnected — skip backoff and reconnect immediately.
|
|
||||||
socketRef.current?.connect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleVisibilityChange = () => {
|
|
||||||
if (document.visibilityState !== "visible") return;
|
|
||||||
|
|
||||||
if (!socketRef.current?.connected) {
|
|
||||||
// Mobile browsers kill WebSockets when the app is backgrounded without
|
|
||||||
// firing "offline". Reconnect immediately when the user returns.
|
|
||||||
socketRef.current?.connect();
|
|
||||||
} else {
|
|
||||||
// Socket appears connected but may have gone stale while JS was suspended.
|
|
||||||
// Rejoin the draft room (idempotent) and bump reconnectCount so the draft
|
|
||||||
// page revalidates loader data to catch any picks/state changes missed
|
|
||||||
// while the browser was backgrounded.
|
|
||||||
socketRef.current?.emit("join-draft", seasonId, teamId);
|
|
||||||
setReconnectCount((c) => c + 1);
|
setReconnectCount((c) => c + 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// visibilitychange fires on both hide and show — only act on show.
|
||||||
|
const handleVisibilityChange = () => {
|
||||||
|
if (document.visibilityState === "visible") handleReturn();
|
||||||
|
};
|
||||||
|
|
||||||
window.addEventListener("offline", handleOffline);
|
window.addEventListener("offline", handleOffline);
|
||||||
window.addEventListener("online", handleOnline);
|
window.addEventListener("online", handleReturn);
|
||||||
document.addEventListener("visibilitychange", handleVisibilityChange);
|
document.addEventListener("visibilitychange", handleVisibilityChange);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("offline", handleOffline);
|
window.removeEventListener("offline", handleOffline);
|
||||||
window.removeEventListener("online", handleOnline);
|
window.removeEventListener("online", handleReturn);
|
||||||
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||||
console.log("Leaving draft room:", seasonId);
|
console.log("Leaving draft room:", seasonId);
|
||||||
socket.emit("leave-draft", seasonId);
|
socket.emit("leave-draft", seasonId);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue