diff --git a/app/hooks/useMediaQuery.ts b/app/hooks/useMediaQuery.ts index adf0f65..e184f1e 100644 --- a/app/hooks/useMediaQuery.ts +++ b/app/hooks/useMediaQuery.ts @@ -1,15 +1,13 @@ -import { useState, useEffect } from "react"; +import { useSyncExternalStore } from "react"; export function useMediaQuery(query: string): boolean { - const [matches, setMatches] = useState(false); - - useEffect(() => { - const mq = window.matchMedia(query); - setMatches(mq.matches); - const handler = (e: MediaQueryListEvent) => setMatches(e.matches); - mq.addEventListener("change", handler); - return () => mq.removeEventListener("change", handler); - }, [query]); - - return matches; + return useSyncExternalStore( + (callback) => { + const mq = window.matchMedia(query); + mq.addEventListener("change", callback); + return () => mq.removeEventListener("change", callback); + }, + () => window.matchMedia(query).matches, + () => false + ); } diff --git a/app/routes/leagues/$leagueId.draft.$seasonId.tsx b/app/routes/leagues/$leagueId.draft.$seasonId.tsx index 4b89b0f..ac58bd3 100644 --- a/app/routes/leagues/$leagueId.draft.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft.$seasonId.tsx @@ -1,4 +1,5 @@ import { useLoaderData, Link, useRevalidator } from "react-router"; +import { useAuth } from "@clerk/react-router"; import { eq, and, asc, desc, inArray } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; @@ -229,6 +230,7 @@ export default function DraftRoom() { ownerMap, } = useLoaderData(); const { revalidate, state: revalidatorState } = useRevalidator(); + const { userId: clerkUserId } = useAuth(); const { isConnected, connectionError, isReconnecting, reconnectCount, on, off } = useDraftSocket(season.id, userTeam?.id); const { permissionState: notificationsPermission, @@ -268,6 +270,19 @@ export default function DraftRoom() { } }, [reconnectCount, revalidate]); + // Guard against Clerk JWT expiry race: if the loader ran without a userId (expired + // short-lived JWT) but the Clerk client SDK has a valid session, revalidate once so + // the loader re-runs with a fresh token and returns correct userTeam/userQueue data. + // The ref prevents an infinite loop if the server consistently fails to return a + // userId (e.g. misconfigured secret key) — we only attempt this recovery once. + const authRevalidatedRef = useRef(false); + useEffect(() => { + if (clerkUserId && !currentUserId && !authRevalidatedRef.current) { + authRevalidatedRef.current = true; + revalidate(); + } + }, [clerkUserId, currentUserId, revalidate]); + // Track revalidation lifecycle to sync local state from fresh loader data. // // The problem: `picks` and `currentPick` are local state initialized once