Session 13: The Slate, Africa geo-restriction, OAuth providers, PropRow + GameCard (1311 tests)

This commit is contained in:
Kev
2026-06-11 03:48:07 -04:00
parent d957dee17b
commit 10159209fa
18 changed files with 1452 additions and 64 deletions
+37 -7
View File
@@ -28,6 +28,11 @@ interface AuthContextValue {
signUp: (email: string, password: string, ageVerified: boolean) => Promise<{ error?: string }>;
signIn: (email: string, password: string) => Promise<{ error?: string }>;
signInWithGoogle: () => Promise<void>;
// Session 13 — generalized OAuth dispatch. Apple/Twitter call paths
// exist in the UI; whether the call SUCCEEDS depends on the
// provider being configured in the Supabase dashboard. Unconfigured
// providers return an error string the login page surfaces inline.
signInWithProvider: (provider: 'google' | 'apple' | 'twitter') => Promise<{ error?: string }>;
signOut: () => Promise<void>;
refresh: () => Promise<void>;
bumpScanCount: () => void;
@@ -151,13 +156,36 @@ export default function AuthProvider({ children }: { children: React.ReactNode }
[supabase],
);
// Session 13 — generic OAuth dispatcher. Supabase returns an error
// object when the provider isn't configured in the dashboard
// (Apple needs a Service ID + private key; Twitter/X needs an
// OAuth 2.0 client). We translate the upstream error into a flat
// `{ error: string }` shape so the login UI can show a friendly
// line without inspecting Supabase internals.
const signInWithProvider = useCallback<AuthContextValue['signInWithProvider']>(
async (provider) => {
if (!supabase) return { error: 'Auth not initialized' };
try {
const { error } = await supabase.auth.signInWithOAuth({
provider,
options: { redirectTo: `${window.location.origin}/auth/callback` },
});
if (error) {
return { error: `${provider} login isn't available yet. Use email or another method.` };
}
return {};
} catch {
return { error: 'Login failed. Try another method.' };
}
},
[supabase],
);
// Kept as a thin alias so legacy callers (signup/login pages) keep
// working without churn. New code should call signInWithProvider.
const signInWithGoogle = useCallback(async () => {
if (!supabase) return;
await supabase.auth.signInWithOAuth({
provider: 'google',
options: { redirectTo: `${window.location.origin}/auth/callback` },
});
}, [supabase]);
await signInWithProvider('google');
}, [signInWithProvider]);
const signOut = useCallback(async () => {
if (!supabase) return;
@@ -189,12 +217,13 @@ export default function AuthProvider({ children }: { children: React.ReactNode }
signUp,
signIn,
signInWithGoogle,
signInWithProvider,
signOut,
refresh,
bumpScanCount,
markMFAPrompted,
};
}, [user, session, profile, loading, signUp, signIn, signInWithGoogle, signOut, refresh, bumpScanCount, markMFAPrompted]);
}, [user, session, profile, loading, signUp, signIn, signInWithGoogle, signInWithProvider, signOut, refresh, bumpScanCount, markMFAPrompted]);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
@@ -214,6 +243,7 @@ export function useAuth(): AuthContextValue {
signUp: async () => ({ error: 'Auth not initialized' }),
signIn: async () => ({ error: 'Auth not initialized' }),
signInWithGoogle: async () => {},
signInWithProvider: async () => ({ error: 'Auth not initialized' }),
signOut: async () => {},
refresh: async () => {},
bumpScanCount: () => {},