expo-duooomi-app/app/auth.tsx

301 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useCallback } from 'react'
import { Block, Text, Toast, VideoBox } from '@share/components'
import { Ionicons } from '@expo/vector-icons'
import { Dimensions, TextInput, Platform } from 'react-native'
import { useAuth } from '@/hooks/core/use-auth'
import { setAuthToken } from '@/lib/auth'
import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'
const BACKGROUND_VIDEOS = [
'https://cdn.roasmax.cn/material/b46f380532e14cf58dd350dbacc7c34a.mp4',
'https://cdn.roasmax.cn/material/992e6c5d940c42feb71c27e556b754c0.mp4',
'https://cdn.roasmax.cn/material/e4947477843f4067be7c37569a33d17b.mp4',
]
type AuthMode = 'login' | 'register'
export default function Auth() {
const { signIn, signUp } = useAuth()
const [mode, setMode] = useState<AuthMode>('login')
const [bgVideo] = useState(() => BACKGROUND_VIDEOS[Math.floor(Math.random() * BACKGROUND_VIDEOS.length)])
const [username, setUsername] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const [loading, setLoading] = useState(false)
const handleLogin = useCallback(async () => {
if (!username || !password) {
Toast.show({ title: '请填写账号和密码' })
return
}
setLoading(true)
try {
const result = await signIn.username(
{
username,
password,
},
{
onSuccess: async (ctx) => {
const authToken = ctx.response.headers.get('set-auth-token')
if (authToken) {
setAuthToken(authToken)
}
},
onError: (ctx) => {
console.error(`[LOGIN] login error`, ctx)
},
},
)
if (result.error) {
Toast.show({ title: result.error.message || '登录失败' })
} else {
Toast.show({ title: '登录成功!' })
}
} catch (error: any) {
Toast.show({ title: error.message || '登录失败' })
} finally {
setLoading(false)
}
}, [username, password, signIn])
const handleRegister = useCallback(async () => {
if (!email || !username || !password) {
Toast.show({ title: '请填写所有必填项' })
return
}
if (password !== confirmPassword) {
Toast.show({ title: '两次密码输入不一致' })
return
}
if (password.length < 6) {
Toast.show({ title: '密码长度至少6位' })
return
}
setLoading(true)
try {
const result = await signUp.email({
email,
username,
password,
name: username,
})
// console.log('result------------', result)
if (result.error) {
Toast.show({ title: result.error.message || '注册失败' })
} else {
Toast.show({ title: '注册成功!' })
}
} catch (error: any) {
Toast.show({ title: error.message || '注册失败' })
} finally {
setLoading(false)
}
}, [email, username, password, confirmPassword, signUp])
const handleSubmit = useCallback(() => {
if (mode === 'login') {
handleLogin()
} else {
handleRegister()
}
}, [mode, handleLogin, handleRegister])
return (
<Block className="relative flex-1 bg-black">
<Block className="absolute inset-[0px] z-[0] overflow-hidden">
<VideoBox url={bgVideo} style={{ position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, opacity: 0.4 }} />
<Block className="absolute inset-[0px] bg-black/20" />
</Block>
<KeyboardAwareScrollView bottomOffset={100}>
<Block className="flex-1 items-center justify-center px-[24px] py-[40px]">
<Block className="relative w-full max-w-[400px]">
<Block className="relative mb-[32px] items-center">
<Block className="h-[80px] w-[80px] items-center justify-center rounded-full border-[4px] border-black bg-accent shadow-deep-black">
<Ionicons name="flash" size={40} color="black" />
</Block>
<Text className="font-900 mt-[16px] text-[32px] italic text-white">LOOMART</Text>
<Block className="mt-[8px] h-[4px] w-[120px] bg-accent" />
</Block>
<Block className="mb-[24px] flex-row gap-[12px]">
{(['login', 'register'] as const).map((tabMode) => {
const isActive = mode === tabMode
return (
<Block
key={tabMode}
onClick={() => setMode(tabMode)}
className={`flex-1 items-center justify-center border-[3px] border-black py-[12px] shadow-hard-black ${isActive ? 'bg-accent' : 'bg-white'}`}
style={{ transform: [{ skewX: '-6deg' }] }}
>
<Text
className={`font-900 text-[14px] italic ${isActive ? 'text-black' : 'text-gray-500'}`}
style={{ transform: [{ skewX: '6deg' }] }}
>
{tabMode === 'login' ? '登录' : '注册'}
</Text>
</Block>
)
})}
</Block>
<Block className="relative border-[4px] border-black bg-white p-[24px] shadow-deep-black" style={{ transform: [{ skewX: '-3deg' }] }}>
<Block style={{ transform: [{ skewX: '3deg' }] }}>
<Block className="mt-[4px] gap-[16px]">
{mode === 'login' ? (
<Block>
<Text className="font-900 mb-[8px] text-[12px] italic text-black"></Text>
<Block
className="flex-row items-center border-[3px] border-black bg-white px-[12px] shadow-medium-black"
style={{ height: 48 }}
>
<Ionicons name="person-outline" size={20} color="black" style={{ marginRight: 8 }} />
<TextInput
value={username}
onChangeText={setUsername}
placeholder="用户名"
placeholderTextColor="#9CA3AF"
autoCapitalize="none"
style={{
flex: 1,
fontSize: 14,
fontWeight: 'bold',
color: 'black',
}}
/>
</Block>
</Block>
) : (
<>
<Block>
<Text className="font-900 mb-[8px] text-[12px] italic text-black"></Text>
<Block
className="flex-row items-center border-[3px] border-black bg-white px-[12px] shadow-medium-black"
style={{ height: 48 }}
>
<Ionicons name="mail-outline" size={20} color="black" style={{ marginRight: 8 }} />
<TextInput
value={email}
onChangeText={setEmail}
placeholder="your@email.com"
placeholderTextColor="#9CA3AF"
keyboardType="email-address"
autoCapitalize="none"
style={{
flex: 1,
fontSize: 14,
fontWeight: 'bold',
color: 'black',
}}
/>
</Block>
</Block>
<Block>
<Text className="font-900 mb-[8px] text-[12px] italic text-black"></Text>
<Block
className="flex-row items-center border-[3px] border-black bg-white px-[12px] shadow-medium-black"
style={{ height: 48 }}
>
<Ionicons name="person-outline" size={20} color="black" style={{ marginRight: 8 }} />
<TextInput
value={username}
onChangeText={setUsername}
placeholder="username"
placeholderTextColor="#9CA3AF"
autoCapitalize="none"
style={{
flex: 1,
fontSize: 14,
fontWeight: 'bold',
color: 'black',
}}
/>
</Block>
</Block>
</>
)}
<Block>
<Text className="font-900 mb-[8px] text-[12px] italic text-black"></Text>
<Block className="flex-row items-center border-[3px] border-black bg-white px-[12px] shadow-medium-black" style={{ height: 48 }}>
<Ionicons name="lock-closed-outline" size={20} color="black" style={{ marginRight: 8 }} />
<TextInput
value={password}
onChangeText={setPassword}
placeholder="••••••••"
placeholderTextColor="#9CA3AF"
secureTextEntry
style={{
flex: 1,
fontSize: 14,
fontWeight: 'bold',
color: 'black',
}}
/>
</Block>
</Block>
{mode === 'register' && (
<Block>
<Text className="font-900 mb-[8px] text-[12px] italic text-black"></Text>
<Block
className="flex-row items-center border-[3px] border-black bg-white px-[12px] shadow-medium-black"
style={{ height: 48 }}
>
<Ionicons name="lock-closed-outline" size={20} color="black" style={{ marginRight: 8 }} />
<TextInput
value={confirmPassword}
onChangeText={setConfirmPassword}
placeholder="••••••••"
placeholderTextColor="#9CA3AF"
secureTextEntry
style={{
flex: 1,
fontSize: 14,
fontWeight: 'bold',
color: 'black',
}}
/>
</Block>
</Block>
)}
<Block
onClick={handleSubmit}
className={`font-900 mt-[8px] flex-row items-center justify-center gap-[8px] border-[3px] border-black py-[14px] shadow-hard-black ${loading ? 'bg-gray-300' : 'bg-accent'}`}
>
{loading ? <Ionicons name="hourglass-outline" size={20} color="black" /> : <Ionicons name="flash" size={20} color="black" />}
<Text className="font-900 text-[16px] italic text-black">{loading ? '处理中...' : mode === 'login' ? '登录' : '注册'}</Text>
</Block>
{mode === 'login' && (
<Block className="mt-[8px] items-center">
<Text className="font-700 text-[12px] text-gray-500"></Text>
</Block>
)}
</Block>
</Block>
</Block>
<Block className="mt-[24px] items-center">
<Text className="font-700 text-[12px] text-gray-400">© 2025 LOOMART. All rights reserved.</Text>
</Block>
</Block>
</Block>
<Block className="h-[200px]" />
</KeyboardAwareScrollView>
</Block>
)
}