import React, { useEffect, useState } from 'react'; import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native'; import { COLORS, FONT_SIZE, RADIUS, SPACING } from '../constants'; import Modal from './Modal'; import { hapticError, hapticLight } from '../utils/haptics'; interface Props { visible: boolean; baseUrl: string; // 已填的服务器地址(注册成功后写设置) onClose: () => void; onAuthed: ( token: string, user: { id: number; displayName: string; avatarEmoji: string; familyId: number | null }, ) => void; onError: (msg: string) => void; } // 登录 / 注册弹窗(注册成功自动登录) export default function LoginModal({ visible, baseUrl, onClose, onAuthed, onError }: Props) { const [mode, setMode] = useState<'login' | 'register'>('login'); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); const [displayName, setDisplayName] = useState(''); const [busy, setBusy] = useState(false); useEffect(() => { if (visible) { setUsername(''); setPassword(''); setConfirm(''); setDisplayName(''); setBusy(false); } }, [visible]); const submit = async () => { if (!username.trim() || !password) { hapticError(); onError('请输入用户名和密码'); return; } if (mode === 'register') { if (password.length < 6) { hapticError(); onError('密码至少 6 位'); return; } if (password !== confirm) { hapticError(); onError('两次输入的密码不一致'); return; } } setBusy(true); try { const { apiRegister, apiLogin } = await import('../sync/apiClient'); const res = mode === 'register' ? await apiRegister(baseUrl, username.trim(), password, displayName.trim() || undefined) : await apiLogin(baseUrl, username.trim(), password); hapticLight(); onAuthed(res.token, res.user); } catch (e) { hapticError(); onError(e instanceof Error ? e.message : '登录失败'); } finally { setBusy(false); } }; return ( 用户名(中英文/数字/下划线) {mode === 'register' ? ( 昵称(家庭成员列表中显示) ) : null} 密码 {mode === 'register' ? ( 确认密码 ) : null} setMode(mode === 'login' ? 'register' : 'login')} accessibilityRole="button" > {mode === 'login' ? '没有账号?注册一个' : '已有账号?直接登录'} ); } const styles = StyleSheet.create({ formGroup: { marginBottom: SPACING.md, }, fieldLabel: { fontSize: FONT_SIZE.xs, color: COLORS.textTertiary, fontWeight: '600', marginBottom: 6, }, input: { backgroundColor: COLORS.bgAlt, borderRadius: RADIUS.sm, paddingHorizontal: SPACING.md, paddingVertical: 10, fontSize: FONT_SIZE.md, color: COLORS.text, }, switchRow: { alignItems: 'center', paddingVertical: SPACING.md, }, switchText: { fontSize: FONT_SIZE.sm, color: COLORS.accentDark, fontWeight: '600', }, });