mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 05:37:56 +00:00
f2e70a8150
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
630 lines
18 KiB
TypeScript
630 lines
18 KiB
TypeScript
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;
|