anisav-mobile/app/(tabs)/profile.tsx
2026-07-19 06:05:12 +05:00

265 lines
7.1 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 { useRef } from 'react'
import { Animated, Image, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { useRouter } from 'expo-router'
import Ionicons from '@expo/vector-icons/Ionicons'
import { useAuth } from '../../src/lib/AuthContext'
import { imageUrl } from '../../src/lib/api'
import { colors, radius, spacing } from '../../src/lib/theme'
import Loader from '../../src/components/Loader'
import Footer from '../../src/components/Footer'
interface ProfileRowProps {
icon: keyof typeof Ionicons.glyphMap
label: string
onPress: () => void
danger?: boolean
}
function ProfileRow({ icon, label, onPress, danger }: ProfileRowProps) {
const scale = useRef(new Animated.Value(1)).current
function pressIn() {
Animated.spring(scale, { toValue: 0.97, useNativeDriver: true, speed: 50 }).start()
}
function pressOut() {
Animated.spring(scale, { toValue: 1, useNativeDriver: true, speed: 50 }).start()
}
return (
<Pressable onPress={onPress} onPressIn={pressIn} onPressOut={pressOut}>
<Animated.View style={[styles.row, { transform: [{ scale }] }]}>
<View style={styles.rowLeft}>
<Ionicons name={icon} size={19} color={danger ? colors.accent : colors.textDim} />
<Text style={[styles.rowLabel, danger && styles.rowLabelDanger]}>{label}</Text>
</View>
{!danger && <Ionicons name="chevron-forward" size={18} color={colors.textFaint} />}
</Animated.View>
</Pressable>
)
}
function LoginButton() {
const router = useRouter()
const scale = useRef(new Animated.Value(1)).current
function pressIn() {
Animated.spring(scale, { toValue: 0.96, useNativeDriver: true, speed: 50 }).start()
}
function pressOut() {
Animated.spring(scale, { toValue: 1, useNativeDriver: true, speed: 50 }).start()
}
return (
<Pressable
onPress={() => router.push('/login')}
onPressIn={pressIn}
onPressOut={pressOut}
>
<Animated.View style={[styles.loginButton, { transform: [{ scale }] }]}>
<Text style={styles.loginButtonText}>Войти</Text>
</Animated.View>
</Pressable>
)
}
export default function ProfileScreen() {
const insets = useSafeAreaInsets()
const router = useRouter()
const { user, loading, logout } = useAuth()
if (loading) {
return (
<View style={[styles.container, { paddingTop: insets.top }]}>
<Loader label="Загрузка..." />
</View>
)
}
const avatarUri = user
? imageUrl(user.avatar?.optimized?.preview || user.avatar?.preview)
: undefined
return (
<ScrollView
style={[styles.container, { paddingTop: insets.top }]}
contentContainerStyle={{ paddingBottom: insets.bottom + spacing(6) }}
>
<Text style={styles.title}>Профиль</Text>
{user ? (
<View style={styles.userHeader}>
{avatarUri ? (
<Image source={{ uri: avatarUri }} style={styles.avatar} />
) : (
<View style={styles.avatarFallback}>
<Text style={styles.avatarFallbackText}>
{user.nickname?.[0]?.toUpperCase() ?? '?'}
</Text>
</View>
)}
<Text style={styles.nickname}>{user.nickname}</Text>
</View>
) : (
<View style={styles.loggedOutWrap}>
<View style={styles.loggedOutIconCircle}>
<Ionicons name="person" size={30} color={colors.textDim} />
</View>
<Text style={styles.loggedOutTitle}>Вы не вошли в аккаунт</Text>
<Text style={styles.loggedOutText}>
Войдите в аккаунт AniLiberty, чтобы синхронизировать избранное, списки и историю
просмотра между устройствами
</Text>
<LoginButton />
</View>
)}
<View style={styles.section}>
<ProfileRow
icon="star-outline"
label="Понравилось"
onPress={() => router.push('/wishlist')}
/>
{user && (
<>
<ProfileRow
icon="list-outline"
label="Мои списки"
onPress={() => router.push('/account/lists')}
/>
<ProfileRow
icon="time-outline"
label="История"
onPress={() => router.push('/account/history')}
/>
</>
)}
</View>
{user && (
<View style={styles.section}>
<ProfileRow icon="log-out-outline" label="Выйти" onPress={logout} danger />
</View>
)}
<Footer />
</ScrollView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
title: {
color: colors.text,
fontSize: 26,
fontWeight: '800',
paddingHorizontal: spacing(4),
marginTop: spacing(2),
marginBottom: spacing(4),
},
userHeader: {
alignItems: 'center',
paddingHorizontal: spacing(4),
marginBottom: spacing(6),
},
avatar: {
width: 84,
height: 84,
borderRadius: radius.full,
backgroundColor: colors.surfaceRaised,
},
avatarFallback: {
width: 84,
height: 84,
borderRadius: radius.full,
backgroundColor: colors.accent,
alignItems: 'center',
justifyContent: 'center',
},
avatarFallbackText: {
color: colors.text,
fontSize: 32,
fontWeight: '700',
},
nickname: {
color: colors.text,
fontSize: 18,
fontWeight: '700',
marginTop: spacing(3),
},
loggedOutWrap: {
alignItems: 'center',
paddingHorizontal: spacing(6),
marginBottom: spacing(6),
},
loggedOutIconCircle: {
width: 68,
height: 68,
borderRadius: radius.full,
backgroundColor: colors.surfaceRaised,
alignItems: 'center',
justifyContent: 'center',
},
loggedOutTitle: {
color: colors.text,
fontSize: 16,
fontWeight: '700',
marginTop: spacing(4),
textAlign: 'center',
},
loggedOutText: {
color: colors.textFaint,
fontSize: 13,
textAlign: 'center',
marginTop: spacing(2),
lineHeight: 18,
},
loginButton: {
marginTop: spacing(5),
backgroundColor: colors.accent,
borderRadius: radius.full,
paddingHorizontal: spacing(8),
paddingVertical: spacing(3),
},
loginButtonText: {
color: colors.text,
fontSize: 15,
fontWeight: '700',
},
section: {
marginHorizontal: spacing(4),
marginBottom: spacing(4),
backgroundColor: colors.surfaceCard,
borderRadius: radius.lg,
borderWidth: StyleSheet.hairlineWidth,
borderColor: colors.border,
overflow: 'hidden',
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: spacing(4),
paddingVertical: spacing(4),
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.border,
},
rowLeft: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing(3),
},
rowLabel: {
color: colors.text,
fontSize: 15,
fontWeight: '500',
},
rowLabelDanger: {
color: colors.accent,
},
})