import React, { useMemo, useState } from 'react'; import { IonButton, IonCard, IonCardContent, IonContent, IonIcon, IonInput, IonItem, IonLabel, IonPage, IonSegment, IonSegmentButton, IonText, useIonViewWillEnter, } from '@ionic/react'; import { FirebaseAuthentication } from '@capacitor-firebase/authentication'; import { Capacitor } from '@capacitor/core'; import { Redirect, useHistory } from 'react-router-dom'; import { Style, setStatusBarBackground, setStatusBarStyle, } from '../utils/statusBar'; import { supabase } from '../supabase'; const Auth: React.FC = () => { const history = useHistory(); const [mode, setMode] = useState<'login' | 'signup'>('login'); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(false); const [googleLoading, setGoogleLoading] = useState(false); const [appleLoading, setAppleLoading] = useState(false); const [error, setError] = useState(''); const [info, setInfo] = useState(''); const [verificationMessage, setVerificationMessage] = useState(''); const [sessionChecked, setSessionChecked] = useState(false); const [hasSession, setHasSession] = useState(false); const showError = (msg: string) => { setError(msg); setTimeout(() => { setError((current) => (current === msg ? '' : current)); }, 4000); }; useIonViewWillEnter(() => { setStatusBarStyle(Style.Dark); setStatusBarBackground('#f6f7fb'); }); React.useEffect(() => { let mounted = true; supabase.auth.getSession().then(({ data }) => { if (!mounted) return; setHasSession(Boolean(data.session)); setSessionChecked(true); }); const { data: { subscription }, } = supabase.auth.onAuthStateChange((_event, session) => { if (!mounted) return; setHasSession(Boolean(session)); setSessionChecked(true); }); return () => { mounted = false; subscription.unsubscribe(); }; }, []); const formValid = useMemo(() => { return email.trim().length > 3 && password.length >= 6; }, [email, password]); const handleSubmit = async () => { setError(''); setInfo(''); setVerificationMessage(''); setLoading(true); try { if (mode === 'login') { const { error: signInError } = await supabase.auth.signInWithPassword({ email: email.trim(), password, }); if (signInError) { showError(signInError.message); return; } history.replace('/home'); return; } const { data, error: signUpError } = await supabase.auth.signUp({ email: email.trim(), password, }); if (signUpError) { showError(signUpError.message); return; } if (data.session) { history.replace('/home'); return; } setVerificationMessage( 'Account created! Check your email for a verification link.' ); setInfo('Once verified, come back and sign in.'); } finally { setLoading(false); } }; const handleGoogleLogin = async () => { setError(''); setInfo(''); setVerificationMessage(''); setGoogleLoading(true); if (!Capacitor.isNativePlatform()) { showError( 'Google Sign-In is only available in the native app. Use email to sign in here.' ); setGoogleLoading(false); return; } try { const result = await FirebaseAuthentication.signInWithGoogle(); const idToken = result.credential?.idToken; if (!idToken) { showError('Google sign-in did not return an ID token.'); return; } const { error: googleAuthError } = await supabase.auth.signInWithIdToken({ provider: 'google', token: idToken, }); if (googleAuthError) { showError(googleAuthError.message); return; } history.replace('/home'); } catch (err) { const message = err instanceof Error ? err.message : 'Google sign-in failed.'; showError(message); } finally { setGoogleLoading(false); } }; const handleAppleLogin = async () => { setError(''); setInfo(''); setVerificationMessage(''); setAppleLoading(true); if (!Capacitor.isNativePlatform()) { showError( 'Apple Sign-In is only available in the native app. Use email to sign in here.' ); setAppleLoading(false); return; } try { const result = await FirebaseAuthentication.signInWithApple({ skipNativeAuth: true, }); const idToken = result.credential?.idToken; const rawNonce = result.credential?.nonce; if (!idToken) { showError('Apple sign-in did not return an ID token.'); return; } const { error: appleAuthError } = await supabase.auth.signInWithIdToken({ provider: 'apple', token: idToken, nonce: rawNonce || undefined, }); if (appleAuthError) { showError(appleAuthError.message); return; } history.replace('/home'); } catch (err) { const message = err instanceof Error ? err.message : 'Apple sign-in failed.'; showError(message); } finally { setAppleLoading(false); } }; if (sessionChecked && hasSession) { return ; } return (
Protect your streaks

Build habits that actually stick.

Track daily wins, recover faster from misses, and see which habits are getting real momentum.

setMode(e.detail.value as 'login' | 'signup') } > Login Sign Up {verificationMessage ? (

Check your inbox

{verificationMessage}

{info}

setMode('login')}> Back to Login
) : (
setEmail(e.detail.value ?? '')} /> setPassword(e.detail.value ?? '')} /> {error ? (

{error}

) : null} {info ? (

{info}

) : null} {loading ? 'Please wait…' : mode === 'login' ? 'Log In' : 'Create Account'} {mode === 'login' ? ( <>
or
{googleLoading ? 'Connecting…' : 'Continue with Google'} {appleLoading ? 'Connecting…' : 'Continue with Apple'} ) : null}
)}
); }; export default Auth;