fix: UserContext fast-path initialization — no spinner on page refresh

Before: UserContext waited for a Supabase profiles table query (~200ms)
before setting isInitialized=true, causing FullContextRoutes to show
a spinner on every page refresh even with a valid stored session.

After: UserContext immediately builds a profile from auth metadata +
localStorage (synchronous, zero network calls) and sets isInitialized=true
before the Supabase query. The Supabase profiles table is still queried
in the background and the profile is updated when it returns with
authoritative user_db_name/school_db_name data.

On refresh with a stored session: auth metadata is available
synchronously via INITIAL_SESSION, so the page renders with no visible
spinner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kcar 2026-05-21 19:16:02 +00:00
parent 5e21a2c7fc
commit 83f17c9ab6

View File

@ -112,73 +112,59 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
let profileRow: Record<string, unknown> | null = null; let profileRow: Record<string, unknown> | null = null;
logger.debug('user-context', '🔧 Step 5: Querying profiles table...', { // Fast-path: build profile from auth metadata + localStorage immediately.
userId: userInfo.id // This clears the spinner before any network call so the page renders on refresh
// without waiting for the Supabase profiles query (~200ms).
const fastMetadata = userInfo.user_metadata as CCUserMetadata;
const fastStoredUserDb = DatabaseNameService.getStoredUserDatabase();
const fastStoredSchoolDb = DatabaseNameService.getStoredSchoolDatabase();
const fastUserDb = fastStoredUserDb || DatabaseNameService.getUserPrivateDB(fastMetadata?.user_type || '', userInfo.id);
const fastProfile: CCUser = {
id: userInfo.id,
email: userInfo.email,
user_type: fastMetadata?.user_type || '',
username: fastMetadata?.username || userInfo.email?.split('@')[0] || 'user',
display_name: String(fastMetadata?.display_name || ''),
user_db_name: String(fastUserDb || ''),
school_db_name: String(fastStoredSchoolDb || ''),
created_at: userInfo.created_at,
updated_at: userInfo.updated_at
};
DatabaseNameService.rememberDatabaseNames({
userDbName: fastProfile.user_db_name,
schoolDbName: fastProfile.school_db_name
}); });
if (mountedRef.current && !isInitialized) {
// Set loading state when we start the actual database query setProfile(fastProfile);
setLoading(true); setLoading(false);
setIsInitialized(true);
// Query profiles table without timeout to see actual error logger.debug('user-context', '⚡ Fast-path: profile initialized from auth metadata, no spinner');
logger.debug('user-context', '🔧 Step 5b: Starting profiles query...', { }
userId: userInfo.id,
clientType: 'authenticated' // Background: query Supabase profiles for authoritative data (user_db_name, school_db_name, display_name).
}); // No setLoading toggles — spinner is already cleared above.
logger.debug('user-context', '🔧 Step 5b1: About to make profiles query...', {
userId: userInfo.id,
queryStarted: true
});
const { data, error } = await supabase const { data, error } = await supabase
.from('profiles') .from('profiles')
.select('*') .select('*')
.eq('id', userInfo.id) .eq('id', userInfo.id)
.single(); .single();
logger.debug('user-context', '🔧 Step 5b2: Direct fetch completed...', {
userId: userInfo.id,
hasData: !!data,
hasError: !!error
});
logger.debug('user-context', '🔧 Step 5c: Profiles query completed', {
hasData: !!data,
hasError: !!error,
errorCode: error?.code,
errorMessage: error?.message
});
logger.debug('user-context', '🔧 Step 5a: Profiles query result', {
hasData: !!data,
hasError: !!error,
errorCode: error?.code,
errorMessage: error?.message
});
if (error && error.code !== 'PGRST116') { if (error && error.code !== 'PGRST116') {
logger.warn('user-context', '⚠️ Profiles query failed, using fallback', { logger.warn('user-context', '⚠️ Profiles query failed, using fast-path profile', {
error: error.message, error: error.message,
code: error.code code: error.code
}); });
// Don't throw error, just use fallback profile
profileRow = null; profileRow = null;
} else if (data) { } else if (data) {
profileRow = data; profileRow = data;
logger.debug('user-context', '✅ Found profile data in database', { logger.debug('user-context', '✅ Supabase profile fetched', {
userId: data.id, userId: data.id,
userType: data.user_type,
userDbName: data.user_db_name userDbName: data.user_db_name
}); });
} else { } else {
logger.debug('user-context', '⚠️ No profile data found - will create default');
profileRow = null; profileRow = null;
} }
// Clear loading state after profiles query completes
setLoading(false);
logger.debug('user-context', '🔧 Step 5d: Loading state cleared');
logger.debug('user-context', '🔧 Step 6: Processing profile data...', { logger.debug('user-context', '🔧 Step 6: Processing profile data...', {
userId: userInfo.id, userId: userInfo.id,
hasProfileRow: !!profileRow, hasProfileRow: !!profileRow,