// apps/mobile/app/login.tsx // OAuth 로그인 화면 import { useState } from 'react' import { View, Text, Pressable, Alert, StyleSheet, ActivityIndicator } from 'react-native' import * as WebBrowser from 'expo-web-browser' import * as Linking from 'expo-linking' import { supabase, isSupabaseConfigured } from '../lib/supabase' WebBrowser.maybeCompleteAuthSession() export default function LoginScreen(): React.ReactElement { const [busy, setBusy] = useState(false) const configured = isSupabaseConfigured() async function signInWithProvider(provider: 'google' | 'github'): Promise { if (!configured) { Alert.alert('미설정', 'Supabase 환경변수가 설정되지 않았습니다.') return } setBusy(true) try { const redirectTo = Linking.createURL('auth-callback') const { data, error } = await supabase.auth.signInWithOAuth({ provider, options: { redirectTo, skipBrowserRedirect: true } }) if (error || !data.url) { Alert.alert('로그인 실패', error?.message ?? 'OAuth URL을 받지 못했습니다') return } const result = await WebBrowser.openAuthSessionAsync(data.url, redirectTo) if (result.type === 'success' && result.url) { const url = new URL(result.url) const code = url.searchParams.get('code') if (code) { const { error: exchangeErr } = await supabase.auth.exchangeCodeForSession(code) if (exchangeErr) { Alert.alert('세션 교환 실패', exchangeErr.message) } } } } finally { setBusy(false) } } return ( D3RO VOICE AI 음성 어시스턴트 {!configured && ( Supabase가 설정되지 않았습니다. app.json의 extra 필드를 확인하세요. )} void signInWithProvider('google')} disabled={!configured || busy} > Google로 계속하기 void signInWithProvider('github')} disabled={!configured || busy} > GitHub로 계속하기 {busy && } ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#19191b', justifyContent: 'center', padding: 32 }, title: { color: '#f25b29', fontSize: 36, fontWeight: '300', textAlign: 'center', letterSpacing: 2, marginBottom: 8 }, subtitle: { color: '#8e8e93', fontSize: 14, textAlign: 'center', marginBottom: 48 }, warning: { color: '#f59e0b', fontSize: 12, textAlign: 'center', marginBottom: 24, padding: 12, borderWidth: 1, borderColor: '#f59e0b', borderRadius: 8 }, buttons: { gap: 12 }, button: { paddingVertical: 14, borderRadius: 8, alignItems: 'center' }, google: { backgroundColor: '#f25b29' }, github: { backgroundColor: 'transparent', borderWidth: 1, borderColor: '#8e8e93' }, disabled: { opacity: 0.5 }, buttonText: { color: '#ffffff', fontSize: 14, fontWeight: '600' } })