brackt/app/hooks/useMediaQuery.ts

14 lines
381 B
TypeScript
Raw Permalink Normal View History

fix: eliminate queue tab flash on mobile by using useSyncExternalStore (#46) * fix: eliminate queue tab flash on mobile by using useSyncExternalStore useMediaQuery initialized to false and updated in useEffect, causing a paint where isMobile was false on mobile devices. This rendered the desktop layout first, hiding the queue tab until the effect fired. useSyncExternalStore reads matchMedia synchronously on the client so the correct layout is used on the first render with no extra paint. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: revalidate draft room when Clerk JWT expires at load time Clerk's short-lived JWTs (~1 min) can expire between navigations. Server-side clerkMiddleware can't refresh them for client-side loader fetches, so getAuth() returns userId=null — causing userTeam=undefined, the queue button to disappear, and the tab to default to "board". Added a useEffect that detects the mismatch: if the Clerk client SDK has a valid userId but the loader ran without one, trigger revalidate() so the loader re-runs with a fresh token and returns correct userTeam/userQueue data. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: prevent infinite revalidation loop in auth guard If the server consistently fails to populate currentUserId after JWT refresh (e.g. misconfigured CLERK_SECRET_KEY), the previous effect would loop indefinitely. A one-shot ref ensures we attempt auth recovery at most once per mount. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 09:54:14 -08:00
import { useSyncExternalStore } from "react";
export function useMediaQuery(query: string): boolean {
fix: eliminate queue tab flash on mobile by using useSyncExternalStore (#46) * fix: eliminate queue tab flash on mobile by using useSyncExternalStore useMediaQuery initialized to false and updated in useEffect, causing a paint where isMobile was false on mobile devices. This rendered the desktop layout first, hiding the queue tab until the effect fired. useSyncExternalStore reads matchMedia synchronously on the client so the correct layout is used on the first render with no extra paint. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: revalidate draft room when Clerk JWT expires at load time Clerk's short-lived JWTs (~1 min) can expire between navigations. Server-side clerkMiddleware can't refresh them for client-side loader fetches, so getAuth() returns userId=null — causing userTeam=undefined, the queue button to disappear, and the tab to default to "board". Added a useEffect that detects the mismatch: if the Clerk client SDK has a valid userId but the loader ran without one, trigger revalidate() so the loader re-runs with a fresh token and returns correct userTeam/userQueue data. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: prevent infinite revalidation loop in auth guard If the server consistently fails to populate currentUserId after JWT refresh (e.g. misconfigured CLERK_SECRET_KEY), the previous effect would loop indefinitely. A one-shot ref ensures we attempt auth recovery at most once per mount. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 09:54:14 -08:00
return useSyncExternalStore(
(callback) => {
const mq = window.matchMedia(query);
mq.addEventListener("change", callback);
return () => mq.removeEventListener("change", callback);
},
() => window.matchMedia(query).matches,
() => false
);
}