Files
pwap/mobile/src/screens/AuthScreen.tsx
T
pezkuwichain f2e70a8150 refactor(mobile): Remove i18n, expand core screens, update plan
BREAKING: Removed multi-language support (i18n) - will be re-added later

Changes:
- Removed i18n system (6 language files, LanguageContext)
- Expanded WalletScreen, SettingsScreen, SwapScreen with more features
- Added KurdistanSun component, HEZ/PEZ token icons
- Added EditProfileScreen, WalletSetupScreen
- Added button e2e tests (Profile, Settings, Wallet)
- Updated plan: honest assessment - 42 nav buttons with mock data
- Fixed terminology: Polkadot→Pezkuwi, Substrate→Bizinikiwi

Reality check: UI complete with mock data, converting to production one-by-one
2026-01-15 05:08:21 +03:00

630 lines
18 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
SafeAreaView,
KeyboardAvoidingView,
Platform,
ScrollView,
StatusBar,
ActivityIndicator,
Image,
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { useAuth } from '../contexts/AuthContext';
import { KurdistanColors } from '../theme/colors';
const AuthScreen: React.FC = () => {
const { signIn, signUp } = useAuth();
// Tab state
const [activeTab, setActiveTab] = useState<'signin' | 'signup'>('signin');
// Sign In state
const [loginEmail, setLoginEmail] = useState('');
const [loginPassword, setLoginPassword] = useState('');
const [showLoginPassword, setShowLoginPassword] = useState(false);
const [rememberMe, setRememberMe] = useState(false);
// Sign Up state
const [signupName, setSignupName] = useState('');
const [signupEmail, setSignupEmail] = useState('');
const [signupPassword, setSignupPassword] = useState('');
const [signupConfirmPassword, setSignupConfirmPassword] = useState('');
const [signupReferralCode, setSignupReferralCode] = useState('');
const [showSignupPassword, setShowSignupPassword] = useState(false);
// Common state
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const handleSignIn = async () => {
setError('');
if (!loginEmail || !loginPassword) {
setError('Please fill in all fields');
return;
}
setLoading(true);
try {
const { error: signInError } = await signIn(loginEmail, loginPassword, rememberMe);
if (signInError) {
if (signInError.message?.includes('Invalid login credentials')) {
setError('Email or password is incorrect');
} else {
setError(signInError.message || 'Login failed');
}
}
} catch (err) {
setError('Login failed. Please try again.');
if (__DEV__) console.error('Sign in error:', err);
} finally {
setLoading(false);
}
};
const handleSignUp = async () => {
setError('');
if (!signupName || !signupEmail || !signupPassword || !signupConfirmPassword) {
setError('Please fill in all required fields');
return;
}
if (signupPassword !== signupConfirmPassword) {
setError('Passwords do not match');
return;
}
if (signupPassword.length < 8) {
setError('Password must be at least 8 characters');
return;
}
setLoading(true);
try {
const { error: signUpError } = await signUp(
signupEmail,
signupPassword,
signupName,
signupReferralCode
);
if (signUpError) {
setError(signUpError.message || 'Sign up failed');
}
} catch (err) {
setError('Sign up failed. Please try again.');
if (__DEV__) console.error('Sign up error:', err);
} finally {
setLoading(false);
}
};
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="light-content" />
<LinearGradient
colors={['#111827', '#000000', '#111827']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.gradient}
>
{/* Grid overlay */}
<View style={styles.gridOverlay} />
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.keyboardView}
>
<ScrollView
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps="handled"
>
{/* Card Container */}
<View style={styles.card}>
{/* Header */}
<View style={styles.header}>
<View style={styles.logoContainer}>
<Image
source={require('../../assets/kurdistan-map.png')}
style={styles.logoImage}
resizeMode="contain"
/>
</View>
<Text style={styles.brandTitle}>PezkuwiChain</Text>
<Text style={styles.subtitle}>
Access your governance account
</Text>
</View>
{/* Tabs */}
<View style={styles.tabContainer}>
<TouchableOpacity
style={[styles.tab, activeTab === 'signin' && styles.tabActive]}
onPress={() => {
setActiveTab('signin');
setError('');
}}
>
<Text style={[styles.tabText, activeTab === 'signin' && styles.tabTextActive]}>
Sign In
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.tab, activeTab === 'signup' && styles.tabActive]}
onPress={() => {
setActiveTab('signup');
setError('');
}}
>
<Text style={[styles.tabText, activeTab === 'signup' && styles.tabTextActive]}>
Sign Up
</Text>
</TouchableOpacity>
</View>
{/* Sign In Form */}
{activeTab === 'signin' && (
<View style={styles.form}>
<View style={styles.inputGroup}>
<Text style={styles.label}>Email</Text>
<View style={styles.inputContainer}>
<Text style={styles.inputIcon}></Text>
<TextInput
style={styles.input}
placeholder="name@example.com"
placeholderTextColor="#9CA3AF"
value={loginEmail}
onChangeText={setLoginEmail}
keyboardType="email-address"
autoCapitalize="none"
editable={!loading}
/>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Password</Text>
<View style={styles.inputContainer}>
<Text style={styles.inputIcon}>🔒</Text>
<TextInput
style={styles.input}
placeholder="••••••••"
placeholderTextColor="#9CA3AF"
value={loginPassword}
onChangeText={setLoginPassword}
secureTextEntry={!showLoginPassword}
editable={!loading}
/>
<TouchableOpacity
style={styles.eyeButton}
onPress={() => setShowLoginPassword(!showLoginPassword)}
>
<Text style={styles.eyeIcon}>{showLoginPassword ? '👁️' : '👁️‍🗨️'}</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.rowBetween}>
<TouchableOpacity
style={styles.checkboxContainer}
onPress={() => setRememberMe(!rememberMe)}
>
<View style={[styles.checkbox, rememberMe && styles.checkboxChecked]}>
{rememberMe && <Text style={styles.checkmark}></Text>}
</View>
<Text style={styles.checkboxLabel}>
Remember me
</Text>
</TouchableOpacity>
<TouchableOpacity>
<Text style={styles.linkText}>
Forgot password?
</Text>
</TouchableOpacity>
</View>
{error && (
<View style={styles.errorContainer}>
<Text style={styles.errorIcon}></Text>
<Text style={styles.errorText}>{error}</Text>
</View>
)}
<TouchableOpacity
style={[styles.primaryButton, styles.signInButton, loading && styles.buttonDisabled]}
onPress={handleSignIn}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#FFFFFF" />
) : (
<Text style={styles.primaryButtonText}>
Sign In
</Text>
)}
</TouchableOpacity>
</View>
)}
{/* Sign Up Form */}
{activeTab === 'signup' && (
<View style={styles.form}>
<View style={styles.inputGroup}>
<Text style={styles.label}>Full Name</Text>
<View style={styles.inputContainer}>
<Text style={styles.inputIcon}>👤</Text>
<TextInput
style={styles.input}
placeholder="John Doe"
placeholderTextColor="#9CA3AF"
value={signupName}
onChangeText={setSignupName}
editable={!loading}
/>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Email</Text>
<View style={styles.inputContainer}>
<Text style={styles.inputIcon}></Text>
<TextInput
style={styles.input}
placeholder="name@example.com"
placeholderTextColor="#9CA3AF"
value={signupEmail}
onChangeText={setSignupEmail}
keyboardType="email-address"
autoCapitalize="none"
editable={!loading}
/>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Password</Text>
<View style={styles.inputContainer}>
<Text style={styles.inputIcon}>🔒</Text>
<TextInput
style={styles.input}
placeholder="••••••••"
placeholderTextColor="#9CA3AF"
value={signupPassword}
onChangeText={setSignupPassword}
secureTextEntry={!showSignupPassword}
editable={!loading}
/>
<TouchableOpacity
style={styles.eyeButton}
onPress={() => setShowSignupPassword(!showSignupPassword)}
>
<Text style={styles.eyeIcon}>{showSignupPassword ? '👁️' : '👁️‍🗨️'}</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Confirm Password</Text>
<View style={styles.inputContainer}>
<Text style={styles.inputIcon}>🔒</Text>
<TextInput
style={styles.input}
placeholder="••••••••"
placeholderTextColor="#9CA3AF"
value={signupConfirmPassword}
onChangeText={setSignupConfirmPassword}
secureTextEntry
editable={!loading}
/>
</View>
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>
Referral Code{' '}
<Text style={styles.optionalText}>
(Optional)
</Text>
</Text>
<View style={styles.inputContainer}>
<Text style={styles.inputIcon}>👥</Text>
<TextInput
style={styles.input}
placeholder="Referral code (optional)"
placeholderTextColor="#9CA3AF"
value={signupReferralCode}
onChangeText={setSignupReferralCode}
editable={!loading}
/>
</View>
<Text style={styles.hintText}>
If someone referred you, enter their code here
</Text>
</View>
{error && (
<View style={styles.errorContainer}>
<Text style={styles.errorIcon}></Text>
<Text style={styles.errorText}>{error}</Text>
</View>
)}
<TouchableOpacity
style={[styles.primaryButton, styles.signUpButton, loading && styles.buttonDisabled]}
onPress={handleSignUp}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#FFFFFF" />
) : (
<Text style={styles.primaryButtonText}>
Create Account
</Text>
)}
</TouchableOpacity>
</View>
)}
{/* Footer */}
<View style={styles.footer}>
<Text style={styles.footerText}>
By continuing, you agree to our{' '}
</Text>
<View style={styles.footerLinks}>
<Text style={styles.footerLink}>
Terms of Service
</Text>
<Text style={styles.footerText}> and </Text>
<Text style={styles.footerLink}>
Privacy Policy
</Text>
</View>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
</LinearGradient>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000000',
},
gradient: {
flex: 1,
},
gridOverlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
opacity: 0.1,
},
keyboardView: {
flex: 1,
},
scrollContent: {
flexGrow: 1,
padding: 16,
justifyContent: 'center',
},
card: {
backgroundColor: 'rgba(17, 24, 39, 0.9)',
borderRadius: 16,
padding: 24,
borderWidth: 1,
borderColor: 'rgba(55, 65, 81, 0.5)',
},
header: {
alignItems: 'center',
marginBottom: 24,
},
logoContainer: {
width: 64,
height: 64,
borderRadius: 32,
backgroundColor: '#FFFFFF',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 12,
padding: 8,
},
logoImage: {
width: '100%',
height: '100%',
},
brandTitle: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 4,
color: '#10B981',
},
subtitle: {
fontSize: 14,
color: '#9CA3AF',
textAlign: 'center',
},
tabContainer: {
flexDirection: 'row',
backgroundColor: '#1F2937',
borderRadius: 8,
padding: 4,
marginBottom: 24,
},
tab: {
flex: 1,
paddingVertical: 8,
alignItems: 'center',
borderRadius: 6,
},
tabActive: {
backgroundColor: '#374151',
},
tabText: {
fontSize: 14,
fontWeight: '600',
color: '#9CA3AF',
},
tabTextActive: {
color: '#FFFFFF',
},
form: {
gap: 16,
},
inputGroup: {
marginBottom: 16,
},
label: {
fontSize: 14,
fontWeight: '500',
color: '#D1D5DB',
marginBottom: 8,
},
optionalText: {
fontSize: 12,
color: '#6B7280',
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#1F2937',
borderRadius: 8,
borderWidth: 1,
borderColor: '#374151',
paddingHorizontal: 12,
},
inputIcon: {
fontSize: 16,
marginRight: 8,
},
input: {
flex: 1,
paddingVertical: 12,
fontSize: 16,
color: '#FFFFFF',
},
eyeButton: {
padding: 4,
},
eyeIcon: {
fontSize: 20,
},
hintText: {
fontSize: 12,
color: '#6B7280',
marginTop: 4,
},
rowBetween: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
checkboxContainer: {
flexDirection: 'row',
alignItems: 'center',
},
checkbox: {
width: 20,
height: 20,
borderRadius: 4,
borderWidth: 2,
borderColor: '#10B981',
marginRight: 8,
justifyContent: 'center',
alignItems: 'center',
},
checkboxChecked: {
backgroundColor: '#10B981',
},
checkmark: {
color: '#FFFFFF',
fontSize: 12,
fontWeight: 'bold',
},
checkboxLabel: {
fontSize: 14,
color: '#9CA3AF',
},
linkText: {
fontSize: 14,
color: '#10B981',
fontWeight: '500',
},
errorContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'rgba(239, 68, 68, 0.1)',
borderWidth: 1,
borderColor: 'rgba(239, 68, 68, 0.3)',
borderRadius: 8,
padding: 12,
marginBottom: 16,
},
errorIcon: {
fontSize: 16,
marginRight: 8,
},
errorText: {
flex: 1,
fontSize: 14,
color: '#FCA5A5',
},
primaryButton: {
paddingVertical: 14,
borderRadius: 8,
alignItems: 'center',
marginTop: 8,
},
signInButton: {
backgroundColor: '#059669',
},
signUpButton: {
backgroundColor: '#D97706',
},
primaryButtonText: {
fontSize: 16,
fontWeight: 'bold',
color: '#FFFFFF',
},
buttonDisabled: {
opacity: 0.5,
},
footer: {
marginTop: 24,
alignItems: 'center',
},
footerText: {
fontSize: 12,
color: '#6B7280',
textAlign: 'center',
},
footerLinks: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
marginTop: 4,
},
footerLink: {
fontSize: 12,
color: '#10B981',
},
});
export default AuthScreen;