mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 16:51:13 -05:00
Fix: Resolve authentication loading state issues
This commit is contained in:
@@ -34,8 +34,9 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
const sessionVerifiedRef = useRef(false);
|
const sessionVerifiedRef = useRef(false);
|
||||||
const novuUpdateTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
const novuUpdateTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const previousEmailRef = useRef<string | null>(null);
|
const previousEmailRef = useRef<string | null>(null);
|
||||||
|
const loadingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const fetchProfile = async (userId: string, retryCount = 0) => {
|
const fetchProfile = async (userId: string, retryCount = 0, onComplete?: () => void) => {
|
||||||
try {
|
try {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('profiles')
|
.from('profiles')
|
||||||
@@ -52,27 +53,26 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
console.log(`[Auth] Retrying profile fetch in ${delay}ms (attempt ${retryCount + 1}/3)`);
|
console.log(`[Auth] Retrying profile fetch in ${delay}ms (attempt ${retryCount + 1}/3)`);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (isMountedRef.current) {
|
if (isMountedRef.current) {
|
||||||
fetchProfile(userId, retryCount + 1);
|
fetchProfile(userId, retryCount + 1, onComplete);
|
||||||
}
|
}
|
||||||
}, delay);
|
}, delay);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show user-friendly error notification only after all retries
|
// All retries exhausted - complete anyway
|
||||||
if (isMountedRef.current && retryCount >= 3) {
|
if (isMountedRef.current) {
|
||||||
toast({
|
console.warn('[Auth] Profile fetch failed after 3 retries');
|
||||||
title: "Profile Loading Error",
|
setProfile(null);
|
||||||
description: "Unable to load your profile. Please refresh the page or try again later.",
|
onComplete?.();
|
||||||
variant: "destructive",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only update state if component is still mounted
|
// Success - update state and complete
|
||||||
if (isMountedRef.current) {
|
if (isMountedRef.current) {
|
||||||
setProfile(data as Profile);
|
setProfile(data as Profile);
|
||||||
profileRetryCountRef.current = 0; // Reset retry count on success
|
profileRetryCountRef.current = 0;
|
||||||
|
onComplete?.();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Auth] Error fetching profile:', error);
|
console.error('[Auth] Error fetching profile:', error);
|
||||||
@@ -82,9 +82,15 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
const delay = Math.pow(2, retryCount) * 1000;
|
const delay = Math.pow(2, retryCount) * 1000;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (isMountedRef.current) {
|
if (isMountedRef.current) {
|
||||||
fetchProfile(userId, retryCount + 1);
|
fetchProfile(userId, retryCount + 1, onComplete);
|
||||||
}
|
}
|
||||||
}, delay);
|
}, delay);
|
||||||
|
} else {
|
||||||
|
// All retries exhausted
|
||||||
|
if (isMountedRef.current) {
|
||||||
|
setProfile(null);
|
||||||
|
onComplete?.();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -96,18 +102,28 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Verify session is still valid
|
// Verify session is still valid
|
||||||
const verifySession = async () => {
|
const verifySession = async (updateLoadingState = false) => {
|
||||||
|
if (updateLoadingState && isMountedRef.current) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data: { session }, error } = await supabase.auth.getSession();
|
const { data: { session }, error } = await supabase.auth.getSession();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('[Auth] Session verification failed:', error);
|
console.error('[Auth] Session verification failed:', error);
|
||||||
setSessionError(error.message);
|
setSessionError(error.message);
|
||||||
|
if (updateLoadingState && isMountedRef.current) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
console.log('[Auth] No active session found');
|
console.log('[Auth] No active session found');
|
||||||
|
if (updateLoadingState && isMountedRef.current) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,12 +134,21 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
if (!user && isMountedRef.current) {
|
if (!user && isMountedRef.current) {
|
||||||
setSession(session);
|
setSession(session);
|
||||||
setUser(session.user);
|
setUser(session.user);
|
||||||
fetchProfile(session.user.id);
|
fetchProfile(session.user.id, 0, () => {
|
||||||
|
if (updateLoadingState && isMountedRef.current) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (updateLoadingState && isMountedRef.current) {
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Auth] Session verification error:', error);
|
console.error('[Auth] Session verification error:', error);
|
||||||
|
if (updateLoadingState && isMountedRef.current) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -149,7 +174,6 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
setSession(session);
|
setSession(session);
|
||||||
setUser(session.user);
|
setUser(session.user);
|
||||||
sessionVerifiedRef.current = true;
|
sessionVerifiedRef.current = true;
|
||||||
setLoading(false);
|
|
||||||
} else if (event === 'SIGNED_OUT') {
|
} else if (event === 'SIGNED_OUT') {
|
||||||
console.log('[Auth] SIGNED_OUT detected, clearing session');
|
console.log('[Auth] SIGNED_OUT detected, clearing session');
|
||||||
setSession(null);
|
setSession(null);
|
||||||
@@ -236,9 +260,14 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Defer profile fetch to avoid deadlock
|
// Defer profile fetch to avoid deadlock
|
||||||
|
const shouldWaitForProfile = event === 'SIGNED_IN';
|
||||||
profileFetchTimeoutRef.current = setTimeout(() => {
|
profileFetchTimeoutRef.current = setTimeout(() => {
|
||||||
if (!isMountedRef.current) return;
|
if (!isMountedRef.current) return;
|
||||||
fetchProfile(session.user.id);
|
fetchProfile(session.user.id, 0, () => {
|
||||||
|
if (isMountedRef.current && shouldWaitForProfile) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
profileFetchTimeoutRef.current = null;
|
profileFetchTimeoutRef.current = null;
|
||||||
}, 0);
|
}, 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -247,7 +276,10 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only set loading false immediately for non-SIGNED_IN events
|
||||||
|
if (event !== 'SIGNED_IN') {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// THEN get initial session
|
// THEN get initial session
|
||||||
@@ -257,16 +289,33 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
if (error) {
|
if (error) {
|
||||||
console.error('[Auth] Initial session fetch error:', error);
|
console.error('[Auth] Initial session fetch error:', error);
|
||||||
setSessionError(error.message);
|
setSessionError(error.message);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSession(session);
|
setSession(session);
|
||||||
setUser(session?.user ?? null);
|
setUser(session?.user ?? null);
|
||||||
|
|
||||||
if (session?.user) {
|
if (session?.user) {
|
||||||
fetchProfile(session.user.id);
|
|
||||||
sessionVerifiedRef.current = true;
|
sessionVerifiedRef.current = true;
|
||||||
}
|
// Fetch profile with completion callback
|
||||||
|
fetchProfile(session.user.id, 0, () => {
|
||||||
|
if (isMountedRef.current) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a safety timeout to force loading to resolve
|
||||||
|
loadingTimeoutRef.current = setTimeout(() => {
|
||||||
|
if (isMountedRef.current && loading) {
|
||||||
|
console.warn('[Auth] Loading timeout reached, forcing loading to false');
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
// Session verification fallback after 2 seconds
|
// Session verification fallback after 2 seconds
|
||||||
const verificationTimeout = setTimeout(() => {
|
const verificationTimeout = setTimeout(() => {
|
||||||
@@ -290,6 +339,9 @@ function AuthProviderComponent({ children }: { children: React.ReactNode }) {
|
|||||||
isMountedRef.current = false;
|
isMountedRef.current = false;
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
clearTimeout(verificationTimeout);
|
clearTimeout(verificationTimeout);
|
||||||
|
if (loadingTimeoutRef.current) {
|
||||||
|
clearTimeout(loadingTimeoutRef.current);
|
||||||
|
}
|
||||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||||
|
|
||||||
// Clear any pending timeouts
|
// Clear any pending timeouts
|
||||||
|
|||||||
Reference in New Issue
Block a user