import { useEffect, useRef } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { doesSessionExist } from 'supertokens-web-js/recipe/session'; import { getOrCreateRefreshPromise } from '@/lib/supertokens/client'; import { attemptRefreshingSession } from 'supertokens-web-js/recipe/session'; import { logger } from '@/lib/supertokens'; export function SessionMonitor() { const navigate = useNavigate(); const lastRefreshTimeRef = useRef(0); const REFRESH_COOLDOWN = 30 * 1000; useEffect(() => { if (typeof window === 'undefined') return; const handleVisibilityChange = async () => { if (document.visibilityState !== 'visible') return; const publicRoutes = ['/login', '/logout', '/refresh-session']; if (publicRoutes.some(route => window.location.pathname === route)) { return; } const now = Date.now(); if (now - lastRefreshTimeRef.current < REFRESH_COOLDOWN) { logger.info('Session monitor: skipping refresh (cooldown)'); return; } try { const sessionExists = await doesSessionExist(); if (!sessionExists) { logger.info('Session monitor: no session exists, skipping refresh'); return; } logger.info('Session monitor: tab became visible, refreshing session'); const refreshed = await getOrCreateRefreshPromise(async () => { return await attemptRefreshingSession(); }); if (refreshed) { lastRefreshTimeRef.current = Date.now(); logger.info('Session monitor: session refreshed successfully'); } else { logger.warn('Session monitor: refresh returned false'); } } catch (error) { logger.error('Session monitor: error refreshing session', error); } }; handleVisibilityChange(); document.addEventListener('visibilitychange', handleVisibilityChange); return () => { document.removeEventListener('visibilitychange', handleVisibilityChange); }; }, [navigate]); return null; }