import React, { useState } from "react"; import { View, ScrollView, Pressable, TextInput, StyleSheet, ActivityIndicator, KeyboardAvoidingView, Platform, Image, } from "react-native"; import { useSirouRouter } from "@sirou/react-native"; import { AppRoutes } from "@/lib/routes"; import { router } from "expo-router"; import { Text } from "@/components/ui/text"; import { Button } from "@/components/ui/button"; import { Mail, Lock, ArrowRight, Eye, EyeOff, Chrome, User, Globe, Phone, } from "@/lib/icons"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { useAuthStore } from "@/lib/auth-store"; import * as Linking from "expo-linking"; import { api, BASE_URL, rbacApi } from "@/lib/api"; import { useColorScheme } from "nativewind"; import { toast } from "@/lib/toast-store"; import { useLanguageStore, AppLanguage } from "@/lib/language-store"; import { getPlaceholderColor } from "@/lib/colors"; import { LanguageModal } from "@/components/LanguageModal"; // Lazy-load Google Sign-In to prevent crash when native module is missing (e.g. Expo Go) let GoogleSignin: any = null; let statusCodes: any = {}; let googleAvailable = false; try { const gsi = require("@react-native-google-signin/google-signin"); GoogleSignin = gsi.GoogleSignin; statusCodes = gsi.statusCodes; googleAvailable = true; GoogleSignin.configure({ webClientId: "377689842258-42ofqmfhf61p03jamlr5nfi18ovqehvf.apps.googleusercontent.com", offlineAccess: true, }); } catch (e) { console.warn("[Login] Google Sign-In native module not available:", (e as any).message); } export default function LoginScreen() { const nav = useSirouRouter(); const setAuth = useAuthStore((state) => state.setAuth); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; const { language, setLanguage } = useLanguageStore(); const [languageModalVisible, setLanguageModalVisible] = useState(false); const [identifier, setIdentifier] = useState(""); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [loading, setLoading] = useState(false); const [loginMode, setLoginMode] = useState<"email" | "phone">("email"); const handleLogin = async () => { if (loginMode === "phone") { if (!identifier) { toast.error("Required Field", "Please enter your phone number"); return; } setLoading(true); const fullPhone = `+251${identifier}`; try { const response = await api.auth.sendOtp({ body: { phone: fullPhone } }); toast.success("Success", response.message || "OTP sent successfully"); // Navigate to OTP screen router.push({ pathname: "/otp", params: { phone: fullPhone, verificationId: response.verificationId }, }); } catch (err: any) { if ( err.message?.includes("Unable to send a verification code to this number") || err.status === 401 ) { toast.info("Account Not Found", "Let's create one for you."); router.push({ pathname: "/register", params: { phone: identifier }, }); } else { toast.error("Error", err.message || "Failed to send OTP"); } } finally { setLoading(false); } return; } if (!identifier || !password) { toast.error( "Required Fields", "Please enter both identifier and password", ); return; } setLoading(true); const isEmail = identifier.includes("@"); const payload = isEmail ? { email: identifier, password } : { phone: `+251${identifier}`, password }; try { // Using the new api.auth.login which is powered by simple-api const response = await api.auth.login({ body: payload }); const permissions: string[] = []; setAuth( response.user, response.accessToken, response.refreshToken, permissions, ); toast.success("Welcome Back!", "You have successfully logged in."); nav.go("(tabs)"); } catch (err: any) { toast.error("Login Failed", err.message || "Invalid credentials"); } finally { setLoading(false); } }; const handleGoogleLogin = async () => { if (!googleAvailable || !GoogleSignin) { toast.error("Unavailable", "Google Sign-In requires a native build. Please use email/phone login."); return; } try { setLoading(true); await GoogleSignin.hasPlayServices(); const userInfo = await GoogleSignin.signIn(); // In newer versions of the library, the response is in data // If using idToken, ensure you configured webClientId const idToken = userInfo.data?.idToken || (userInfo as any).idToken; if (!idToken) { throw new Error("Failed to obtain Google ID Token"); } // Send idToken to our new consolidated endpoint const response = await api.auth.googleMobile({ body: { idToken } }); // Fetch roles to get permissions // const rolesResponse = await rbacApi.roles(); // const userRole = response.user.role; // const roleData = rolesResponse.find((r: any) => r.role === userRole); // const permissions = roleData ? roleData.permissions : []; const permissions: string[] = []; setAuth( response.user, response.accessToken, response.refreshToken, permissions, ); toast.success("Welcome!", "Signed in with Google."); nav.go("(tabs)"); } catch (error: any) { if (error.code === statusCodes.SIGN_IN_CANCELLED) { // User cancelled the login flow } else if (error.code === statusCodes.IN_PROGRESS) { toast.error("Login in progress", "Please wait..."); } else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) { toast.error("Play Services", "Google Play Services not available"); } else { console.error("[Login] Google Error:", error); toast.error( "Google Login Failed", error.message || "An error occurred", ); } } finally { setLoading(false); } }; return ( setLanguageModalVisible(true)} className="p-2 rounded-full bg-card border border-border" > {/* Logo / Branding */} Login Sign in to manage your tickets & invoices {/* Login Type Toggle */} { setLoginMode("email"); setIdentifier(""); }} className={`flex-1 py-2 rounded-lg items-center ${loginMode === "email" ? "bg-primary" : ""}`} > Email Login { setLoginMode("phone"); setIdentifier(""); }} className={`flex-1 py-2 rounded-lg items-center ${loginMode === "phone" ? "bg-primary" : ""}`} > Phone Number {/* Form */} {loginMode === "email" ? "Email Address" : "Phone Number"} {loginMode === "email" ? ( ) : ( +251 )} {loginMode === "email" && ( Password setShowPassword(!showPassword)}> {showPassword ? ( ) : ( )} )} {/* Social / Other */} or {loading ? ( ) : ( <> Continue with Google )} nav.go("register")} > Don't have an account?{" "} Create one setLanguage(lang)} onClose={() => setLanguageModalVisible(false)} /> ); }