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:
parent
5e21a2c7fc
commit
83f17c9ab6
@ -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;
|
||||||
// Set loading state when we start the actual database query
|
const fastStoredUserDb = DatabaseNameService.getStoredUserDatabase();
|
||||||
setLoading(true);
|
const fastStoredSchoolDb = DatabaseNameService.getStoredSchoolDatabase();
|
||||||
|
const fastUserDb = fastStoredUserDb || DatabaseNameService.getUserPrivateDB(fastMetadata?.user_type || '', userInfo.id);
|
||||||
// Query profiles table without timeout to see actual error
|
const fastProfile: CCUser = {
|
||||||
logger.debug('user-context', '🔧 Step 5b: Starting profiles query...', {
|
id: userInfo.id,
|
||||||
userId: userInfo.id,
|
email: userInfo.email,
|
||||||
clientType: 'authenticated'
|
user_type: fastMetadata?.user_type || '',
|
||||||
});
|
username: fastMetadata?.username || userInfo.email?.split('@')[0] || 'user',
|
||||||
|
display_name: String(fastMetadata?.display_name || ''),
|
||||||
logger.debug('user-context', '🔧 Step 5b1: About to make profiles query...', {
|
user_db_name: String(fastUserDb || ''),
|
||||||
userId: userInfo.id,
|
school_db_name: String(fastStoredSchoolDb || ''),
|
||||||
queryStarted: true
|
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) {
|
||||||
|
setProfile(fastProfile);
|
||||||
|
setLoading(false);
|
||||||
|
setIsInitialized(true);
|
||||||
|
logger.debug('user-context', '⚡ Fast-path: profile initialized from auth metadata, no spinner');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background: query Supabase profiles for authoritative data (user_db_name, school_db_name, display_name).
|
||||||
|
// No setLoading toggles — spinner is already cleared above.
|
||||||
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,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user