Files
k-tv/k-tv-frontend/app/providers.tsx
Gabriel Kaszewski c4d2e48f73 fix(frontend): resolve all eslint warnings and errors
- block-timeline: ref updates moved to useLayoutEffect
- channel-card, guide/page: Date.now() wrapped in useMemo + suppress purity rule
- auth-context: lazy localStorage init (removes setState-in-effect)
- use-channel-order: lazy localStorage init (removes setState-in-effect)
- use-idle: start timer on mount without calling resetIdle (removes setState-in-effect)
- use-subtitles, transcode-settings-dialog: inline eslint-disable on exact violating line
- providers: block-level eslint-disable for tokenRef closure in useState initializer
- edit-channel-sheet: remove unused minsToTime and BlockContent imports
- docs/page: escape unescaped quote and apostrophe entities
2026-03-17 02:40:32 +01:00

68 lines
2.2 KiB
TypeScript

"use client";
import {
QueryCache,
QueryClient,
QueryClientProvider,
MutationCache,
} from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { useState, useRef, useEffect } from "react";
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { AuthProvider, useAuthContext } from "@/context/auth-context";
import { Toaster } from "@/components/ui/sonner";
import { ApiRequestError } from "@/lib/api";
function QueryProvider({ children }: { children: React.ReactNode }) {
const { token, setToken } = useAuthContext();
const router = useRouter();
const tokenRef = useRef(token);
useEffect(() => { tokenRef.current = token; }, [token]);
/* eslint-disable react-hooks/refs -- tokenRef is only read in onError callbacks, not during render */
const [queryClient] = useState(() => {
return new QueryClient({
queryCache: new QueryCache({
onError: (error) => {
// Only redirect on 401 if the user had a token (expired session).
// Guests hitting 401 on restricted content should not be redirected.
if (error instanceof ApiRequestError && error.status === 401 && tokenRef.current) {
toast.warning("Session expired, please log in again.");
setToken(null);
router.push("/login");
}
},
}),
mutationCache: new MutationCache({
onError: (error) => {
// Mutations always require auth — redirect on 401 regardless.
if (error instanceof ApiRequestError && error.status === 401) {
toast.warning("Session expired, please log in again.");
setToken(null);
router.push("/login");
}
},
}),
defaultOptions: { queries: { staleTime: 60 * 1000 } },
});
});
/* eslint-enable react-hooks/refs */
return (
<QueryClientProvider client={queryClient}>
{children}
<Toaster position="bottom-right" richColors />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}
export function Providers({ children }: { children: React.ReactNode }) {
return (
<AuthProvider>
<QueryProvider>{children}</QueryProvider>
</AuthProvider>
);
}