diff --git a/src/components/settings/PrivacyTab.tsx b/src/components/settings/PrivacyTab.tsx index 5103c28b..56709c84 100644 --- a/src/components/settings/PrivacyTab.tsx +++ b/src/components/settings/PrivacyTab.tsx @@ -60,19 +60,24 @@ export function PrivacyTab() { } if (data?.privacy_settings) { - // Validate the data before using it - const validatedSettings = privacySettingsSchema.parse(data.privacy_settings); - setPreferences(validatedSettings); - - form.reset({ - privacy_level: (profile?.privacy_level === 'public' || profile?.privacy_level === 'private') - ? profile.privacy_level - : 'public', - show_pronouns: profile?.show_pronouns || false, - ...validatedSettings - }); + const parseResult = privacySettingsSchema.safeParse(data.privacy_settings); + + if (parseResult.success) { + setPreferences(parseResult.data); + form.reset({ + privacy_level: (profile?.privacy_level === 'public' || profile?.privacy_level === 'private') + ? profile.privacy_level + : 'public', + show_pronouns: profile?.show_pronouns || false, + ...parseResult.data + }); + } else { + console.warn('Invalid privacy settings, reinitializing with defaults', { + errors: parseResult.error.issues + }); + await initializePreferences(); + } } else { - // Initialize preferences if they don't exist await initializePreferences(); } } catch (error) { diff --git a/src/components/settings/SecurityTab.tsx b/src/components/settings/SecurityTab.tsx index e8e1a8e3..7185651a 100644 --- a/src/components/settings/SecurityTab.tsx +++ b/src/components/settings/SecurityTab.tsx @@ -146,19 +146,30 @@ export function SecurityTab() { const fetchSessions = async () => { if (!user) return; - const { data, error } = await supabase.rpc('get_my_sessions'); + setLoadingSessions(true); - if (error) { + try { + const { data, error } = await supabase.rpc('get_my_sessions'); + + if (error) { + throw error; + } + + setSessions((data as AuthSession[]) || []); + } catch (error) { logger.error('Failed to fetch sessions', { userId: user.id, action: 'fetch_sessions', - error: error.message + error: error instanceof Error ? error.message : String(error) }); - handleError(error, { action: 'Load sessions', userId: user.id }); - } else { - setSessions((data as AuthSession[]) || []); + handleError(error, { + action: 'Load active sessions', + userId: user.id + }); + setSessions([]); + } finally { + setLoadingSessions(false); } - setLoadingSessions(false); }; const initiateSessionRevoke = async (sessionId: string) => { diff --git a/src/lib/privacyValidation.ts b/src/lib/privacyValidation.ts index f77072f8..cd368688 100644 --- a/src/lib/privacyValidation.ts +++ b/src/lib/privacyValidation.ts @@ -18,17 +18,18 @@ import { z } from 'zod'; /** * Schema for privacy settings in user_preferences + * Uses defaults for backward compatibility with incomplete data */ export const privacySettingsSchema = z.object({ - activity_visibility: z.enum(['public', 'private'] as const), - search_visibility: z.boolean(), - show_location: z.boolean(), - show_age: z.boolean(), - show_avatar: z.boolean(), - show_bio: z.boolean(), - show_activity_stats: z.boolean(), - show_home_park: z.boolean() -}); + activity_visibility: z.enum(['public', 'private'] as const).default('public'), + search_visibility: z.boolean().default(true), + show_location: z.boolean().default(false), + show_age: z.boolean().default(false), + show_avatar: z.boolean().default(true), + show_bio: z.boolean().default(true), + show_activity_stats: z.boolean().default(true), + show_home_park: z.boolean().default(false) +}).passthrough(); /** * Schema for profile privacy settings diff --git a/src/types/auth.ts b/src/types/auth.ts index 234f91b2..230c7b56 100644 --- a/src/types/auth.ts +++ b/src/types/auth.ts @@ -84,7 +84,7 @@ export interface AuthSession { user_agent: string | null; ip: string | null; // Pre-hashed by database function not_after: string | null; - aal: AALLevel | null; + aal: string | null; // Changed to string to match get_my_sessions() return type } /** diff --git a/supabase/migrations/20251014234728_a5c67cd0-be0f-4966-af35-092d1856310f.sql b/supabase/migrations/20251014234728_a5c67cd0-be0f-4966-af35-092d1856310f.sql new file mode 100644 index 00000000..41421ac4 --- /dev/null +++ b/supabase/migrations/20251014234728_a5c67cd0-be0f-4966-af35-092d1856310f.sql @@ -0,0 +1,16 @@ +-- Backfill missing privacy settings fields for existing users +UPDATE user_preferences +SET privacy_settings = privacy_settings || jsonb_build_object( + 'show_avatar', COALESCE((privacy_settings->>'show_avatar')::boolean, true), + 'show_bio', COALESCE((privacy_settings->>'show_bio')::boolean, true), + 'show_activity_stats', COALESCE((privacy_settings->>'show_activity_stats')::boolean, true), + 'show_home_park', COALESCE((privacy_settings->>'show_home_park')::boolean, false) +) +WHERE + privacy_settings IS NOT NULL + AND ( + privacy_settings->>'show_avatar' IS NULL OR + privacy_settings->>'show_bio' IS NULL OR + privacy_settings->>'show_activity_stats' IS NULL OR + privacy_settings->>'show_home_park' IS NULL + ); \ No newline at end of file