Initial commit
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import { supabase } from '../supabaseClient';
|
||||
import { logger } from '../debugConfig';
|
||||
import { CCUser, CCUserMetadata } from '../services/auth/authService';
|
||||
import { UserPreferences } from '../services/auth/profileService';
|
||||
import { DatabaseNameService } from '../services/graph/databaseNameService';
|
||||
|
||||
export interface UserContextType {
|
||||
user: CCUser | null;
|
||||
loading: boolean;
|
||||
error: Error | null;
|
||||
profile: CCUser | null;
|
||||
preferences: UserPreferences;
|
||||
isMobile: boolean;
|
||||
isInitialized: boolean;
|
||||
updateProfile: (updates: Partial<CCUser>) => Promise<void>;
|
||||
updatePreferences: (updates: Partial<UserPreferences>) => Promise<void>;
|
||||
clearError: () => void;
|
||||
}
|
||||
|
||||
export const UserContext = createContext<UserContextType>({
|
||||
user: null,
|
||||
loading: true,
|
||||
error: null,
|
||||
profile: null,
|
||||
preferences: {},
|
||||
isMobile: false,
|
||||
isInitialized: false,
|
||||
updateProfile: async () => {},
|
||||
updatePreferences: async () => {},
|
||||
clearError: () => {}
|
||||
});
|
||||
|
||||
export function UserProvider({ children }: { children: React.ReactNode }) {
|
||||
const [user] = useState<CCUser | null>(null);
|
||||
const [profile, setProfile] = useState<CCUser | null>(null);
|
||||
const [preferences, setPreferences] = useState<UserPreferences>({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const [isMobile] = useState(window.innerWidth <= 768);
|
||||
|
||||
useEffect(() => {
|
||||
const loadUserProfile = async () => {
|
||||
try {
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
|
||||
if (!user) {
|
||||
setProfile(null);
|
||||
setLoading(false);
|
||||
setIsInitialized(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('profiles')
|
||||
.select('*')
|
||||
.eq('id', user.id)
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const metadata = user.user_metadata as CCUserMetadata;
|
||||
const userDbName = DatabaseNameService.getUserPrivateDB(metadata.user_type || '', metadata.username || '');
|
||||
const schoolDbName = DatabaseNameService.getDevelopmentSchoolDB();
|
||||
|
||||
const userProfile: CCUser = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
user_type: metadata.user_type || '',
|
||||
username: metadata.username || '',
|
||||
display_name: metadata.display_name || '',
|
||||
user_db_name: userDbName,
|
||||
school_db_name: schoolDbName,
|
||||
created_at: user.created_at,
|
||||
updated_at: user.updated_at
|
||||
};
|
||||
|
||||
setProfile(userProfile);
|
||||
|
||||
logger.debug('user-context', '✅ User profile loaded', {
|
||||
userId: userProfile.id,
|
||||
userType: userProfile.user_type,
|
||||
username: userProfile.username,
|
||||
userDbName: userProfile.user_db_name,
|
||||
schoolDbName: userProfile.school_db_name
|
||||
});
|
||||
|
||||
// Load preferences from profile data
|
||||
setPreferences({
|
||||
theme: data.theme || 'system',
|
||||
notifications: data.notifications_enabled || false
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('user-context', '❌ Failed to load user profile', { error });
|
||||
setError(error instanceof Error ? error : new Error('Failed to load user profile'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setIsInitialized(true);
|
||||
}
|
||||
};
|
||||
|
||||
loadUserProfile();
|
||||
}, []);
|
||||
|
||||
const updateProfile = async (updates: Partial<CCUser>) => {
|
||||
if (!user?.id || !profile) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const { error } = await supabase
|
||||
.from('profiles')
|
||||
.update({
|
||||
...updates,
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
.eq('id', user.id);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
setProfile(prev => prev ? { ...prev, ...updates } : null);
|
||||
logger.info('user-context', '✅ Profile updated successfully');
|
||||
} catch (error) {
|
||||
logger.error('user-context', '❌ Failed to update profile', { error });
|
||||
setError(error instanceof Error ? error : new Error('Failed to update profile'));
|
||||
throw error;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const updatePreferences = async (updates: Partial<UserPreferences>) => {
|
||||
if (!user?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const newPreferences = { ...preferences, ...updates };
|
||||
setPreferences(newPreferences);
|
||||
|
||||
const { error } = await supabase
|
||||
.from('profiles')
|
||||
.update({
|
||||
preferences: newPreferences,
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
.eq('id', user.id);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
logger.info('user-context', '✅ Preferences updated successfully');
|
||||
} catch (error) {
|
||||
logger.error('user-context', '❌ Failed to update preferences', { error });
|
||||
setError(error instanceof Error ? error : new Error('Failed to update preferences'));
|
||||
throw error;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
user: profile,
|
||||
loading,
|
||||
error,
|
||||
profile,
|
||||
preferences,
|
||||
isMobile,
|
||||
isInitialized,
|
||||
updateProfile,
|
||||
updatePreferences,
|
||||
clearError: () => setError(null)
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</UserContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useUser = () => useContext(UserContext);
|
||||
Reference in New Issue
Block a user