import { Feather, Ionicons } from "@expo/vector-icons"; import { LinearGradient } from "expo-linear-gradient"; import React, { useEffect, useRef, useState } from "react"; import { ActivityIndicator, Alert, Animated, Dimensions, KeyboardAvoidingView, Modal, Platform, StyleSheet, Text, TextInput, TouchableOpacity, View, } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { useAuth } from "@/hooks/use-auth"; import { authClient } from "@/lib/auth/client"; import { storage } from "@/lib/storage"; interface LoginModalProps { visible: boolean; onClose: () => void; } export function LoginModal({ visible, onClose }: LoginModalProps) { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [isPasswordVisible, setPasswordVisible] = useState(false); const [isLoading, setIsLoading] = useState(false); const { isAuthenticated } = useAuth(); const slideAnim = useRef(new Animated.Value(Dimensions.get("window").height)).current; const fadeAnim = useRef(new Animated.Value(0)).current; useEffect(() => { if (visible) { Animated.parallel([ Animated.timing(slideAnim, { toValue: 0, duration: 300, useNativeDriver: true, }), Animated.timing(fadeAnim, { toValue: 0.5, duration: 300, useNativeDriver: true, }), ]).start(); } else { Animated.parallel([ Animated.timing(slideAnim, { toValue: Dimensions.get("window").height, duration: 300, useNativeDriver: true, }), Animated.timing(fadeAnim, { toValue: 0, duration: 300, useNativeDriver: true, }), ]).start(); } }, [visible]); useEffect(() => { if (isAuthenticated && visible) { onClose(); setEmail(""); setPassword(""); } }, [isAuthenticated, visible, onClose]); const handleClose = () => { Animated.parallel([ Animated.timing(slideAnim, { toValue: Dimensions.get("window").height, duration: 300, useNativeDriver: true, }), Animated.timing(fadeAnim, { toValue: 0, duration: 300, useNativeDriver: true, }), ]).start(() => onClose()); }; const handleLogin = async () => { if (!email.trim() || !password.trim()) { Alert.alert("错误", "请输入邮箱和密码"); return; } setIsLoading(true); try { await new Promise(async (resolve, reject) => { await authClient.signIn.username( { username: email.trim(), password, }, { onSuccess: async (ctx) => { const authToken = ctx.response.headers.get("set-auth-token"); if (authToken) { await storage.setItem( `bestaibest.better-auth.session_token`, authToken ); resolve(); } else { console.error("Bearer token not set"); reject(); } }, } ); }); Alert.alert("成功", "登录成功!"); } catch (error: any) { Alert.alert("登录失败", error?.message || "邮箱或密码错误"); } finally { setIsLoading(false); } }; const handleBackdropPress = () => { handleClose(); }; return ( Email Login setPasswordVisible((visible) => !visible)} style={styles.visibilityToggle} > {isLoading ? ( ) : ( Log in )} 已阅读并同意 用户协议 隐私政策 ); } const styles = StyleSheet.create({ overlay: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, }, backdrop: { flex: 1, }, modalContainer: { position: "absolute", left: 0, right: 0, bottom: 0, }, modalContent: { backgroundColor: "#14161c", borderTopLeftRadius: 28, borderTopRightRadius: 28, borderWidth: 1, borderColor: "rgba(255, 255, 255, 0.08)", shadowColor: "#000", shadowOffset: { width: 0, height: -8 }, shadowRadius: 18, shadowOpacity: 0.25, elevation: 24, maxHeight: Dimensions.get("window").height * 0.75, }, sheetIndicator: { width: 40, height: 4, backgroundColor: "rgba(255, 255, 255, 0.3)", borderRadius: 2, alignSelf: "center", marginTop: 12, marginBottom: 8, }, safeArea: { flex: 1, }, flex: { flex: 1, }, content: { flex: 1, paddingHorizontal: 24, paddingBottom: 34, }, header: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginTop: 16, marginBottom: 28, }, titleGroup: { flexDirection: "row", alignItems: "center", }, iconBadge: { width: 40, height: 40, borderRadius: 16, alignItems: "center", justifyContent: "center", backgroundColor: "#f6474d", marginRight: 12, }, title: { color: "#f5f6f8", fontSize: 22, fontWeight: "700", letterSpacing: 0.2, }, closeButton: { width: 40, height: 40, borderRadius: 20, alignItems: "center", justifyContent: "center", backgroundColor: "rgba(255, 255, 255, 0.05)", borderWidth: 1, borderColor: "rgba(255, 255, 255, 0.06)", }, form: { marginTop: 4, }, inputWrapper: { position: "relative", flexDirection: "row", alignItems: "center", paddingHorizontal: 18, backgroundColor: "#1a1d23", borderRadius: 18, borderWidth: 1, borderColor: "rgba(255, 255, 255, 0.05)", marginBottom: 18, }, input: { flex: 1, paddingVertical: 16, fontSize: 16, color: "#f5f6f8", letterSpacing: 0.3, }, visibilityToggle: { marginLeft: 12, }, loginButton: { height: 56, borderRadius: 18, alignItems: "center", justifyContent: "center", backgroundColor: "#d7ff1f", shadowColor: "#d7ff1f", shadowOpacity: 0.35, shadowRadius: 16, shadowOffset: { width: 0, height: 10 }, elevation: 10, marginTop: 4, }, loginButtonDisabled: { opacity: 0.7, }, loginButtonText: { color: "#10120d", fontSize: 17, fontWeight: "700", letterSpacing: 0.5, }, termsRow: { flexDirection: "row", alignItems: "center", justifyContent: "center", marginTop: 18, }, termsText: { color: "#a7abb5", fontSize: 13, lineHeight: 18, marginLeft: 8, }, linkText: { color: "#f5f6f8", fontWeight: "600", }, });