import { Ionicons } from '@expo/vector-icons'; import * as AppleAuthentication from 'expo-apple-authentication'; import { useRouter } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Alert, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { ThemedText } from '@/components/ThemedText'; import { ThemedView } from '@/components/ThemedView'; import { Colors } from '@/constants/Colors'; import { useAppDispatch } from '@/hooks/redux'; import { useColorScheme } from '@/hooks/useColorScheme'; import { login } from '@/store/userSlice'; export default function LoginScreen() { const router = useRouter(); const scheme = (useColorScheme() ?? 'light') as 'light' | 'dark'; const color = Colors[scheme]; const dispatch = useAppDispatch(); const [hasAgreed, setHasAgreed] = useState(false); const [appleAvailable, setAppleAvailable] = useState(false); const [loading, setLoading] = useState(false); useEffect(() => { AppleAuthentication.isAvailableAsync().then(setAppleAvailable).catch(() => setAppleAvailable(false)); }, []); const guardAgreement = useCallback((action: () => void) => { if (!hasAgreed) { Alert.alert('请先阅读并同意', '勾选“我已阅读并同意用户协议与隐私政策”后才可继续登录'); return; } action(); }, [hasAgreed]); const onAppleLogin = useCallback(async () => { if (!appleAvailable) return; try { setLoading(true); const credential = await AppleAuthentication.signInAsync({ requestedScopes: [ AppleAuthentication.AppleAuthenticationScope.FULL_NAME, AppleAuthentication.AppleAuthenticationScope.EMAIL, ], }); const identityToken = (credential as any)?.identityToken; await dispatch(login({ appleIdentityToken: identityToken })).unwrap(); router.back(); } catch (err: any) { if (err?.code === 'ERR_CANCELED') return; const message = err?.message || '登录失败,请稍后再试'; Alert.alert('登录失败', message); } finally { setLoading(false); } }, [appleAvailable, router]); const onGuestLogin = useCallback(() => { // TODO: 标记为游客身份,可在此写入本地状态/上报统计 router.back(); }, [router]); const disabledStyle = useMemo(() => ({ opacity: hasAgreed ? 1 : 0.5 }), [hasAgreed]); return ( {/* 自定义头部,与其它页面风格一致 */} router.back()} style={styles.backButton}> 登录 数字普拉提 欢迎登录 {/* Apple 登录 */} {appleAvailable && ( guardAgreement(onAppleLogin)} disabled={!hasAgreed || loading} style={({ pressed }) => [ styles.appleButton, { backgroundColor: '#000000' }, disabledStyle, pressed && { transform: [{ scale: 0.98 }] }, ]} > 使用 Apple 登录 )} {/* 游客登录(弱化样式) */} guardAgreement(onGuestLogin)} disabled={!hasAgreed || loading} style={({ pressed }) => [ styles.guestButton, { borderColor: color.border, backgroundColor: color.surface }, disabledStyle, pressed && { transform: [{ scale: 0.98 }] }, ]} > 以游客身份继续 {/* 协议勾选 */} setHasAgreed((v) => !v)} style={styles.checkboxWrap} accessibilityRole="checkbox" accessibilityState={{ checked: hasAgreed }}> {hasAgreed && } 我已阅读并同意 router.push('/legal/privacy-policy')}> 《隐私政策》 router.push('/legal/user-agreement')}> 《用户协议》 {/* 占位底部间距 */} ); } const styles = StyleSheet.create({ safeArea: { flex: 1 }, container: { flex: 1 }, content: { flexGrow: 1, paddingHorizontal: 24, justifyContent: 'center', }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingTop: 4, paddingBottom: 8, }, backButton: { width: 32, height: 32, alignItems: 'center', justifyContent: 'center' }, headerTitle: { fontSize: 18, fontWeight: '700' }, headerWrap: { marginBottom: 36, }, title: { fontSize: 32, fontWeight: '500', letterSpacing: 0.5, }, subtitle: { marginTop: 8, fontSize: 14, fontWeight: '500', }, appleButton: { height: 56, borderRadius: 28, alignItems: 'center', justifyContent: 'center', flexDirection: 'row', marginBottom: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.15, shadowRadius: 12, elevation: 2, }, appleText: { fontSize: 16, color: '#FFFFFF', fontWeight: '600', }, guestButton: { height: 52, borderRadius: 26, alignItems: 'center', justifyContent: 'center', flexDirection: 'row', borderWidth: 1, marginTop: 6, }, guestText: { fontSize: 15, fontWeight: '500', }, agreementRow: { flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', marginTop: 24, }, checkboxWrap: { marginRight: 8 }, checkbox: { width: 18, height: 18, borderRadius: 5, borderWidth: 1, alignItems: 'center', justifyContent: 'center', }, agreementText: { fontSize: 12 }, link: { fontSize: 12, fontWeight: '600' }, footerHint: { marginTop: 24 }, hintText: { fontSize: 12 }, });