Implement custom dark theme for the app and enhance login/register screens with animations. Update dependencies and fix package versions in package.json and package-lock.json.
This commit is contained in:
@ -1,9 +1,23 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, StyleSheet, KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { View, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, Dimensions } from 'react-native';
|
||||
import { TextInput, Button, Text, Headline, HelperText } from 'react-native-paper';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { LOGIN } from '../graphql/mutations';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import Animated, {
|
||||
useSharedValue,
|
||||
useAnimatedStyle,
|
||||
withTiming,
|
||||
withSpring,
|
||||
withDelay,
|
||||
withSequence,
|
||||
withRepeat,
|
||||
interpolate,
|
||||
Easing,
|
||||
} from 'react-native-reanimated';
|
||||
|
||||
const { width: screenWidth } = Dimensions.get('window');
|
||||
const AnimatedView = Animated.View;
|
||||
|
||||
export const LoginScreen = ({ navigation }: any) => {
|
||||
const [username, setUsername] = useState('');
|
||||
@ -11,6 +25,32 @@ export const LoginScreen = ({ navigation }: any) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const { login } = useAuth();
|
||||
|
||||
// Анимации
|
||||
const translateY = useSharedValue(50);
|
||||
const opacity = useSharedValue(0);
|
||||
const scale = useSharedValue(0.9);
|
||||
const glowAnimation = useSharedValue(0);
|
||||
const buttonScale = useSharedValue(1);
|
||||
const inputFocusAnimation1 = useSharedValue(0);
|
||||
const inputFocusAnimation2 = useSharedValue(0);
|
||||
|
||||
useEffect(() => {
|
||||
// Анимация появления
|
||||
translateY.value = withSpring(0, { damping: 15, stiffness: 100 });
|
||||
opacity.value = withTiming(1, { duration: 800 });
|
||||
scale.value = withSpring(1, { damping: 15, stiffness: 100 });
|
||||
|
||||
// Пульсирующее свечение
|
||||
glowAnimation.value = withRepeat(
|
||||
withSequence(
|
||||
withTiming(1, { duration: 2000, easing: Easing.inOut(Easing.ease) }),
|
||||
withTiming(0, { duration: 2000, easing: Easing.inOut(Easing.ease) })
|
||||
),
|
||||
-1,
|
||||
false
|
||||
);
|
||||
}, []);
|
||||
|
||||
const [loginMutation, { loading, error }] = useMutation(LOGIN, {
|
||||
onCompleted: async (data) => {
|
||||
await login(data.login.access_token, data.login.user);
|
||||
@ -25,66 +65,173 @@ export const LoginScreen = ({ navigation }: any) => {
|
||||
loginMutation({ variables: { username, password } });
|
||||
};
|
||||
|
||||
// Анимированные стили
|
||||
const containerAnimatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
transform: [
|
||||
{ translateY: translateY.value },
|
||||
{ scale: scale.value }
|
||||
],
|
||||
opacity: opacity.value,
|
||||
};
|
||||
});
|
||||
|
||||
const glowContainerStyle = useAnimatedStyle(() => {
|
||||
const glowOpacity = interpolate(glowAnimation.value, [0, 1], [0.3, 0.8]);
|
||||
const shadowRadius = interpolate(glowAnimation.value, [0, 1], [10, 30]);
|
||||
|
||||
return {
|
||||
shadowOpacity: glowOpacity,
|
||||
shadowRadius: shadowRadius,
|
||||
elevation: interpolate(glowAnimation.value, [0, 1], [5, 15]),
|
||||
};
|
||||
});
|
||||
|
||||
const buttonAnimatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
transform: [{ scale: buttonScale.value }],
|
||||
};
|
||||
});
|
||||
|
||||
const createInputAnimatedStyle = (focusAnimation: any) => {
|
||||
return useAnimatedStyle(() => {
|
||||
const borderWidth = interpolate(focusAnimation.value, [0, 1], [1, 2]);
|
||||
const shadowOpacity = interpolate(focusAnimation.value, [0, 1], [0, 0.6]);
|
||||
|
||||
return {
|
||||
borderWidth: borderWidth,
|
||||
shadowOpacity: shadowOpacity,
|
||||
elevation: interpolate(focusAnimation.value, [0, 1], [2, 8]),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const inputStyle1 = createInputAnimatedStyle(inputFocusAnimation1);
|
||||
const inputStyle2 = createInputAnimatedStyle(inputFocusAnimation2);
|
||||
|
||||
const handleButtonPressIn = () => {
|
||||
buttonScale.value = withSpring(0.95);
|
||||
};
|
||||
|
||||
const handleButtonPressOut = () => {
|
||||
buttonScale.value = withSpring(1);
|
||||
};
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
style={styles.container}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
>
|
||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||
<View style={styles.content}>
|
||||
<Headline style={styles.title}>Вход в Prism</Headline>
|
||||
<Animated.View style={[styles.content, containerAnimatedStyle]}>
|
||||
<Animated.View style={[styles.glowContainer, glowContainerStyle]}>
|
||||
<Headline style={styles.title}>Вход в Prism</Headline>
|
||||
<Text style={styles.subtitle}>Добро пожаловать обратно</Text>
|
||||
</Animated.View>
|
||||
|
||||
<TextInput
|
||||
label="Имя пользователя"
|
||||
value={username}
|
||||
onChangeText={setUsername}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
autoCapitalize="none"
|
||||
disabled={loading}
|
||||
/>
|
||||
<AnimatedView style={inputStyle1}>
|
||||
<TextInput
|
||||
label="Имя пользователя"
|
||||
value={username}
|
||||
onChangeText={setUsername}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
autoCapitalize="none"
|
||||
disabled={loading}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#9333ea',
|
||||
placeholder: '#a855f7',
|
||||
text: '#ffffff',
|
||||
background: '#1a1a2e',
|
||||
outline: '#7c3aed',
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
inputFocusAnimation1.value = withSpring(1);
|
||||
}}
|
||||
onBlur={() => {
|
||||
inputFocusAnimation1.value = withSpring(0);
|
||||
}}
|
||||
/>
|
||||
</AnimatedView>
|
||||
|
||||
<TextInput
|
||||
label="Пароль"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
secureTextEntry={!showPassword}
|
||||
disabled={loading}
|
||||
right={
|
||||
<TextInput.Icon
|
||||
icon={showPassword ? 'eye-off' : 'eye'}
|
||||
onPress={() => setShowPassword(!showPassword)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AnimatedView style={inputStyle2}>
|
||||
<TextInput
|
||||
label="Пароль"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
secureTextEntry={!showPassword}
|
||||
disabled={loading}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#9333ea',
|
||||
placeholder: '#a855f7',
|
||||
text: '#ffffff',
|
||||
background: '#1a1a2e',
|
||||
outline: '#7c3aed',
|
||||
}
|
||||
}}
|
||||
right={
|
||||
<TextInput.Icon
|
||||
icon={showPassword ? 'eye-off' : 'eye'}
|
||||
onPress={() => setShowPassword(!showPassword)}
|
||||
color="#a855f7"
|
||||
/>
|
||||
}
|
||||
onFocus={() => {
|
||||
inputFocusAnimation2.value = withSpring(1);
|
||||
}}
|
||||
onBlur={() => {
|
||||
inputFocusAnimation2.value = withSpring(0);
|
||||
}}
|
||||
/>
|
||||
</AnimatedView>
|
||||
|
||||
{error && (
|
||||
<HelperText type="error" visible={true}>
|
||||
<HelperText type="error" visible={true} style={styles.errorText}>
|
||||
{error.message}
|
||||
</HelperText>
|
||||
)}
|
||||
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={handleLogin}
|
||||
loading={loading}
|
||||
disabled={loading || !username || !password}
|
||||
style={styles.button}
|
||||
>
|
||||
Войти
|
||||
</Button>
|
||||
<AnimatedView style={buttonAnimatedStyle}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={handleLogin}
|
||||
onPressIn={handleButtonPressIn}
|
||||
onPressOut={handleButtonPressOut}
|
||||
loading={loading}
|
||||
disabled={loading || !username || !password}
|
||||
style={styles.button}
|
||||
contentStyle={styles.buttonContent}
|
||||
labelStyle={styles.buttonLabel}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#9333ea',
|
||||
}
|
||||
}}
|
||||
>
|
||||
Войти
|
||||
</Button>
|
||||
</AnimatedView>
|
||||
|
||||
<Button
|
||||
mode="text"
|
||||
onPress={() => navigation.navigate('Register')}
|
||||
disabled={loading}
|
||||
style={styles.linkButton}
|
||||
labelStyle={styles.linkButtonLabel}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#a855f7',
|
||||
}
|
||||
}}
|
||||
>
|
||||
Нет аккаунта? Зарегистрироваться
|
||||
</Button>
|
||||
</View>
|
||||
</Animated.View>
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
@ -93,11 +240,12 @@ export const LoginScreen = ({ navigation }: any) => {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f5f5f5',
|
||||
backgroundColor: '#0a0a0f',
|
||||
},
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 40,
|
||||
},
|
||||
content: {
|
||||
padding: 20,
|
||||
@ -105,18 +253,90 @@ const styles = StyleSheet.create({
|
||||
width: '100%',
|
||||
alignSelf: 'center',
|
||||
},
|
||||
glowContainer: {
|
||||
marginBottom: 40,
|
||||
padding: 20,
|
||||
borderRadius: 20,
|
||||
backgroundColor: '#1a1a2e',
|
||||
// iOS тени
|
||||
shadowColor: '#9333ea',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
shadowOpacity: 0.5,
|
||||
shadowRadius: 20,
|
||||
// Android тень
|
||||
elevation: 10,
|
||||
},
|
||||
title: {
|
||||
textAlign: 'center',
|
||||
marginBottom: 30,
|
||||
marginBottom: 10,
|
||||
fontSize: 32,
|
||||
fontWeight: 'bold',
|
||||
color: '#ffffff',
|
||||
textShadowColor: '#9333ea',
|
||||
textShadowOffset: { width: 0, height: 0 },
|
||||
textShadowRadius: 10,
|
||||
},
|
||||
subtitle: {
|
||||
textAlign: 'center',
|
||||
fontSize: 16,
|
||||
color: '#a855f7',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
input: {
|
||||
marginBottom: 15,
|
||||
marginBottom: 20,
|
||||
backgroundColor: '#1a1a2e',
|
||||
borderRadius: 12,
|
||||
// iOS тени
|
||||
shadowColor: '#7c3aed',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 4,
|
||||
},
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 6,
|
||||
// Android тень
|
||||
elevation: 5,
|
||||
},
|
||||
button: {
|
||||
marginTop: 10,
|
||||
marginTop: 20,
|
||||
marginBottom: 10,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#9333ea',
|
||||
// iOS тени для кнопки
|
||||
shadowColor: '#9333ea',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 8,
|
||||
},
|
||||
shadowOpacity: 0.6,
|
||||
shadowRadius: 15,
|
||||
// Android тень
|
||||
elevation: 10,
|
||||
},
|
||||
buttonContent: {
|
||||
paddingVertical: 8,
|
||||
},
|
||||
buttonLabel: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
letterSpacing: 1,
|
||||
},
|
||||
linkButton: {
|
||||
marginTop: 10,
|
||||
},
|
||||
linkButtonLabel: {
|
||||
fontSize: 16,
|
||||
color: '#a855f7',
|
||||
},
|
||||
errorText: {
|
||||
color: '#ef4444',
|
||||
textAlign: 'center',
|
||||
marginBottom: 10,
|
||||
textShadowColor: '#ef4444',
|
||||
textShadowOffset: { width: 0, height: 0 },
|
||||
textShadowRadius: 5,
|
||||
},
|
||||
});
|
@ -1,9 +1,23 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, StyleSheet, KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { View, StyleSheet, KeyboardAvoidingView, Platform, ScrollView, Dimensions } from 'react-native';
|
||||
import { TextInput, Button, Text, Headline, HelperText } from 'react-native-paper';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { REGISTER } from '../graphql/mutations';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import Animated, {
|
||||
useSharedValue,
|
||||
useAnimatedStyle,
|
||||
withTiming,
|
||||
withSpring,
|
||||
withDelay,
|
||||
withSequence,
|
||||
withRepeat,
|
||||
interpolate,
|
||||
Easing,
|
||||
} from 'react-native-reanimated';
|
||||
|
||||
const { width: screenWidth } = Dimensions.get('window');
|
||||
const AnimatedView = Animated.View;
|
||||
|
||||
export const RegisterScreen = ({ navigation }: any) => {
|
||||
const [username, setUsername] = useState('');
|
||||
@ -13,6 +27,34 @@ export const RegisterScreen = ({ navigation }: any) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const { login } = useAuth();
|
||||
|
||||
// Анимации
|
||||
const translateY = useSharedValue(50);
|
||||
const opacity = useSharedValue(0);
|
||||
const scale = useSharedValue(0.9);
|
||||
const glowAnimation = useSharedValue(0);
|
||||
const buttonScale = useSharedValue(1);
|
||||
const inputFocusAnimation1 = useSharedValue(0);
|
||||
const inputFocusAnimation2 = useSharedValue(0);
|
||||
const inputFocusAnimation3 = useSharedValue(0);
|
||||
const inputFocusAnimation4 = useSharedValue(0);
|
||||
|
||||
useEffect(() => {
|
||||
// Анимация появления
|
||||
translateY.value = withSpring(0, { damping: 15, stiffness: 100 });
|
||||
opacity.value = withTiming(1, { duration: 800 });
|
||||
scale.value = withSpring(1, { damping: 15, stiffness: 100 });
|
||||
|
||||
// Пульсирующее свечение
|
||||
glowAnimation.value = withRepeat(
|
||||
withSequence(
|
||||
withTiming(1, { duration: 2000, easing: Easing.inOut(Easing.ease) }),
|
||||
withTiming(0, { duration: 2000, easing: Easing.inOut(Easing.ease) })
|
||||
),
|
||||
-1,
|
||||
false
|
||||
);
|
||||
}, []);
|
||||
|
||||
const [registerMutation, { loading, error }] = useMutation(REGISTER, {
|
||||
onCompleted: async (data) => {
|
||||
await login(data.register.access_token, data.register.user);
|
||||
@ -29,94 +71,238 @@ export const RegisterScreen = ({ navigation }: any) => {
|
||||
|
||||
const passwordsMatch = password === confirmPassword || confirmPassword === '';
|
||||
|
||||
// Анимированные стили
|
||||
const containerAnimatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
transform: [
|
||||
{ translateY: translateY.value },
|
||||
{ scale: scale.value }
|
||||
],
|
||||
opacity: opacity.value,
|
||||
};
|
||||
});
|
||||
|
||||
const glowContainerStyle = useAnimatedStyle(() => {
|
||||
const glowOpacity = interpolate(glowAnimation.value, [0, 1], [0.3, 0.8]);
|
||||
const shadowRadius = interpolate(glowAnimation.value, [0, 1], [10, 30]);
|
||||
|
||||
return {
|
||||
shadowOpacity: glowOpacity,
|
||||
shadowRadius: shadowRadius,
|
||||
elevation: interpolate(glowAnimation.value, [0, 1], [5, 15]),
|
||||
};
|
||||
});
|
||||
|
||||
const buttonAnimatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
transform: [{ scale: buttonScale.value }],
|
||||
};
|
||||
});
|
||||
|
||||
const createInputAnimatedStyle = (focusAnimation: any) => {
|
||||
return useAnimatedStyle(() => {
|
||||
const borderWidth = interpolate(focusAnimation.value, [0, 1], [1, 2]);
|
||||
const shadowOpacity = interpolate(focusAnimation.value, [0, 1], [0, 0.6]);
|
||||
|
||||
return {
|
||||
borderWidth: borderWidth,
|
||||
shadowOpacity: shadowOpacity,
|
||||
elevation: interpolate(focusAnimation.value, [0, 1], [2, 8]),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const inputStyle1 = createInputAnimatedStyle(inputFocusAnimation1);
|
||||
const inputStyle2 = createInputAnimatedStyle(inputFocusAnimation2);
|
||||
const inputStyle3 = createInputAnimatedStyle(inputFocusAnimation3);
|
||||
const inputStyle4 = createInputAnimatedStyle(inputFocusAnimation4);
|
||||
|
||||
const handleButtonPressIn = () => {
|
||||
buttonScale.value = withSpring(0.95);
|
||||
};
|
||||
|
||||
const handleButtonPressOut = () => {
|
||||
buttonScale.value = withSpring(1);
|
||||
};
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
style={styles.container}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
||||
>
|
||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||
<View style={styles.content}>
|
||||
<Headline style={styles.title}>Регистрация в Prism</Headline>
|
||||
<Animated.View style={[styles.content, containerAnimatedStyle]}>
|
||||
<Animated.View style={[styles.glowContainer, glowContainerStyle]}>
|
||||
<Headline style={styles.title}>Регистрация в Prism</Headline>
|
||||
<Text style={styles.subtitle}>Присоединяйтесь к будущему</Text>
|
||||
</Animated.View>
|
||||
|
||||
<TextInput
|
||||
label="Имя пользователя"
|
||||
value={username}
|
||||
onChangeText={setUsername}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
autoCapitalize="none"
|
||||
disabled={loading}
|
||||
/>
|
||||
<AnimatedView style={inputStyle1}>
|
||||
<TextInput
|
||||
label="Имя пользователя"
|
||||
value={username}
|
||||
onChangeText={setUsername}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
autoCapitalize="none"
|
||||
disabled={loading}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#9333ea',
|
||||
placeholder: '#a855f7',
|
||||
text: '#ffffff',
|
||||
background: '#1a1a2e',
|
||||
outline: '#7c3aed',
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
inputFocusAnimation1.value = withSpring(1);
|
||||
}}
|
||||
onBlur={() => {
|
||||
inputFocusAnimation1.value = withSpring(0);
|
||||
}}
|
||||
/>
|
||||
</AnimatedView>
|
||||
|
||||
<TextInput
|
||||
label="Email"
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
disabled={loading}
|
||||
/>
|
||||
<AnimatedView style={inputStyle2}>
|
||||
<TextInput
|
||||
label="Email"
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
disabled={loading}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#9333ea',
|
||||
placeholder: '#a855f7',
|
||||
text: '#ffffff',
|
||||
background: '#1a1a2e',
|
||||
outline: '#7c3aed',
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
inputFocusAnimation2.value = withSpring(1);
|
||||
}}
|
||||
onBlur={() => {
|
||||
inputFocusAnimation2.value = withSpring(0);
|
||||
}}
|
||||
/>
|
||||
</AnimatedView>
|
||||
|
||||
<TextInput
|
||||
label="Пароль"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
secureTextEntry={!showPassword}
|
||||
disabled={loading}
|
||||
right={
|
||||
<TextInput.Icon
|
||||
icon={showPassword ? 'eye-off' : 'eye'}
|
||||
onPress={() => setShowPassword(!showPassword)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<AnimatedView style={inputStyle3}>
|
||||
<TextInput
|
||||
label="Пароль"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
secureTextEntry={!showPassword}
|
||||
disabled={loading}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#9333ea',
|
||||
placeholder: '#a855f7',
|
||||
text: '#ffffff',
|
||||
background: '#1a1a2e',
|
||||
outline: '#7c3aed',
|
||||
}
|
||||
}}
|
||||
right={
|
||||
<TextInput.Icon
|
||||
icon={showPassword ? 'eye-off' : 'eye'}
|
||||
onPress={() => setShowPassword(!showPassword)}
|
||||
color="#a855f7"
|
||||
/>
|
||||
}
|
||||
onFocus={() => {
|
||||
inputFocusAnimation3.value = withSpring(1);
|
||||
}}
|
||||
onBlur={() => {
|
||||
inputFocusAnimation3.value = withSpring(0);
|
||||
}}
|
||||
/>
|
||||
</AnimatedView>
|
||||
|
||||
<TextInput
|
||||
label="Подтвердите пароль"
|
||||
value={confirmPassword}
|
||||
onChangeText={setConfirmPassword}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
secureTextEntry={!showPassword}
|
||||
disabled={loading}
|
||||
error={!passwordsMatch}
|
||||
/>
|
||||
<AnimatedView style={inputStyle4}>
|
||||
<TextInput
|
||||
label="Подтвердите пароль"
|
||||
value={confirmPassword}
|
||||
onChangeText={setConfirmPassword}
|
||||
mode="outlined"
|
||||
style={styles.input}
|
||||
secureTextEntry={!showPassword}
|
||||
disabled={loading}
|
||||
error={!passwordsMatch}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#9333ea',
|
||||
placeholder: '#a855f7',
|
||||
text: '#ffffff',
|
||||
background: '#1a1a2e',
|
||||
outline: '#7c3aed',
|
||||
error: '#ef4444',
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
inputFocusAnimation4.value = withSpring(1);
|
||||
}}
|
||||
onBlur={() => {
|
||||
inputFocusAnimation4.value = withSpring(0);
|
||||
}}
|
||||
/>
|
||||
</AnimatedView>
|
||||
|
||||
{!passwordsMatch && (
|
||||
<HelperText type="error" visible={true}>
|
||||
<HelperText type="error" visible={true} style={styles.errorText}>
|
||||
Пароли не совпадают
|
||||
</HelperText>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<HelperText type="error" visible={true}>
|
||||
<HelperText type="error" visible={true} style={styles.errorText}>
|
||||
{error.message}
|
||||
</HelperText>
|
||||
)}
|
||||
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={handleRegister}
|
||||
loading={loading}
|
||||
disabled={loading || !username || !email || !password || !passwordsMatch}
|
||||
style={styles.button}
|
||||
>
|
||||
Зарегистрироваться
|
||||
</Button>
|
||||
<AnimatedView style={buttonAnimatedStyle}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={handleRegister}
|
||||
onPressIn={handleButtonPressIn}
|
||||
onPressOut={handleButtonPressOut}
|
||||
loading={loading}
|
||||
disabled={loading || !username || !email || !password || !passwordsMatch}
|
||||
style={styles.button}
|
||||
contentStyle={styles.buttonContent}
|
||||
labelStyle={styles.buttonLabel}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#9333ea',
|
||||
}
|
||||
}}
|
||||
>
|
||||
Зарегистрироваться
|
||||
</Button>
|
||||
</AnimatedView>
|
||||
|
||||
<Button
|
||||
mode="text"
|
||||
onPress={() => navigation.navigate('Login')}
|
||||
disabled={loading}
|
||||
style={styles.linkButton}
|
||||
labelStyle={styles.linkButtonLabel}
|
||||
theme={{
|
||||
colors: {
|
||||
primary: '#a855f7',
|
||||
}
|
||||
}}
|
||||
>
|
||||
Уже есть аккаунт? Войти
|
||||
</Button>
|
||||
</View>
|
||||
</Animated.View>
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
@ -125,11 +311,12 @@ export const RegisterScreen = ({ navigation }: any) => {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f5f5f5',
|
||||
backgroundColor: '#0a0a0f',
|
||||
},
|
||||
scrollContent: {
|
||||
flexGrow: 1,
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 40,
|
||||
},
|
||||
content: {
|
||||
padding: 20,
|
||||
@ -137,18 +324,90 @@ const styles = StyleSheet.create({
|
||||
width: '100%',
|
||||
alignSelf: 'center',
|
||||
},
|
||||
glowContainer: {
|
||||
marginBottom: 40,
|
||||
padding: 20,
|
||||
borderRadius: 20,
|
||||
backgroundColor: '#1a1a2e',
|
||||
// iOS тени
|
||||
shadowColor: '#9333ea',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
shadowOpacity: 0.5,
|
||||
shadowRadius: 20,
|
||||
// Android тень
|
||||
elevation: 10,
|
||||
},
|
||||
title: {
|
||||
textAlign: 'center',
|
||||
marginBottom: 30,
|
||||
marginBottom: 10,
|
||||
fontSize: 32,
|
||||
fontWeight: 'bold',
|
||||
color: '#ffffff',
|
||||
textShadowColor: '#9333ea',
|
||||
textShadowOffset: { width: 0, height: 0 },
|
||||
textShadowRadius: 10,
|
||||
},
|
||||
subtitle: {
|
||||
textAlign: 'center',
|
||||
fontSize: 16,
|
||||
color: '#a855f7',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
input: {
|
||||
marginBottom: 15,
|
||||
marginBottom: 20,
|
||||
backgroundColor: '#1a1a2e',
|
||||
borderRadius: 12,
|
||||
// iOS тени
|
||||
shadowColor: '#7c3aed',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 4,
|
||||
},
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 6,
|
||||
// Android тень
|
||||
elevation: 5,
|
||||
},
|
||||
button: {
|
||||
marginTop: 10,
|
||||
marginTop: 20,
|
||||
marginBottom: 10,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#9333ea',
|
||||
// iOS тени для кнопки
|
||||
shadowColor: '#9333ea',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 8,
|
||||
},
|
||||
shadowOpacity: 0.6,
|
||||
shadowRadius: 15,
|
||||
// Android тень
|
||||
elevation: 10,
|
||||
},
|
||||
buttonContent: {
|
||||
paddingVertical: 8,
|
||||
},
|
||||
buttonLabel: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
letterSpacing: 1,
|
||||
},
|
||||
linkButton: {
|
||||
marginTop: 10,
|
||||
},
|
||||
linkButtonLabel: {
|
||||
fontSize: 16,
|
||||
color: '#a855f7',
|
||||
},
|
||||
errorText: {
|
||||
color: '#ef4444',
|
||||
textAlign: 'center',
|
||||
marginBottom: 10,
|
||||
textShadowColor: '#ef4444',
|
||||
textShadowOffset: { width: 0, height: 0 },
|
||||
textShadowRadius: 5,
|
||||
},
|
||||
});
|
Reference in New Issue
Block a user