* Show "Syncing Draft State..." overlay during draft reconnection When a user reconnects to a draft room (socket reconnect, tab visibility return, etc.), there was a gap between when the socket connected and when the full draft state sync arrived. The ConnectionOverlay vanished instantly on reconnect, showing potentially stale data. Add an isSyncing flag that keeps the overlay visible with "Syncing Draft State..." messaging until the actual data arrives via socket or HTTP revalidation. A 5-second safety timeout prevents the overlay from getting stuck. Fixes #78 * Add team names utility tests
107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
import { Button } from "~/components/ui/button";
|
|
import { Card } from "~/components/ui/card";
|
|
|
|
interface ConnectionOverlayProps {
|
|
isConnected: boolean;
|
|
isReconnecting: boolean;
|
|
connectionError: string | null;
|
|
isSyncing: boolean;
|
|
}
|
|
|
|
export function ConnectionOverlay({
|
|
isConnected,
|
|
isReconnecting,
|
|
connectionError,
|
|
isSyncing,
|
|
}: ConnectionOverlayProps) {
|
|
if (isConnected && !isSyncing) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-[100]">
|
|
<Card className="w-full max-w-md mx-4 p-8 text-center">
|
|
<div className="flex flex-col items-center gap-6">
|
|
{/* Spinner or Error Icon */}
|
|
{connectionError ? (
|
|
<div className="w-16 h-16 rounded-full bg-destructive/15 flex items-center justify-center">
|
|
<svg
|
|
className="w-8 h-8 text-destructive"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
) : (
|
|
<div className="relative w-16 h-16">
|
|
<div className="absolute inset-0 border-4 border-primary/30 rounded-full" />
|
|
<div className="absolute inset-0 border-4 border-primary border-t-transparent rounded-full animate-spin" />
|
|
</div>
|
|
)}
|
|
|
|
{/* Title */}
|
|
<div>
|
|
<h2 className="text-2xl font-bold mb-2">
|
|
{connectionError
|
|
? "Connection Error"
|
|
: isSyncing
|
|
? "Syncing Draft State..."
|
|
: isReconnecting
|
|
? "Reconnecting..."
|
|
: "Connecting to Draft"}
|
|
</h2>
|
|
|
|
<p className="text-muted-foreground">
|
|
{connectionError ? (
|
|
connectionError
|
|
) : isSyncing ? (
|
|
"Reconnected. Syncing the latest draft state..."
|
|
) : isReconnecting ? (
|
|
"Lost connection to the draft server. Attempting to reconnect..."
|
|
) : (
|
|
"Please wait while we connect you to the live draft room."
|
|
)}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Action Button (only on persistent error) */}
|
|
{connectionError && (
|
|
<Button
|
|
onClick={() => window.location.reload()}
|
|
className="w-full"
|
|
variant="default"
|
|
>
|
|
Reload Page
|
|
</Button>
|
|
)}
|
|
|
|
{/* Loading Dots */}
|
|
{!connectionError && (
|
|
<div className="flex gap-1.5">
|
|
<div
|
|
className="w-2 h-2 bg-primary rounded-full animate-bounce"
|
|
style={{ animationDelay: "0ms" }}
|
|
/>
|
|
<div
|
|
className="w-2 h-2 bg-primary rounded-full animate-bounce"
|
|
style={{ animationDelay: "150ms" }}
|
|
/>
|
|
<div
|
|
className="w-2 h-2 bg-primary rounded-full animate-bounce"
|
|
style={{ animationDelay: "300ms" }}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|