@oxyhq/services 5.4.1 → 5.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/assets/icons/OxyServices.js +1 -1
- package/lib/commonjs/core/index.js +84 -2
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/index.js +22 -22
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/node/index.js +6 -6
- package/lib/commonjs/node/index.js.map +1 -1
- package/lib/commonjs/ui/components/Avatar.js +3 -3
- package/lib/commonjs/ui/components/Avatar.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +3 -3
- package/lib/commonjs/ui/components/GroupedSection.js +1 -1
- package/lib/commonjs/ui/components/OxyLogo.js +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +13 -13
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +2 -2
- package/lib/commonjs/ui/components/ProfileCard.js +2 -2
- package/lib/commonjs/ui/components/Section.js +1 -1
- package/lib/commonjs/ui/components/SectionTitle.js +1 -1
- package/lib/commonjs/ui/components/icon/index.js +1 -1
- package/lib/commonjs/ui/components/index.js +12 -12
- package/lib/commonjs/ui/context/OxyContext.js +20 -4
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/index.js +11 -11
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/navigation/OxyRouter.js +18 -18
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +18 -18
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountManagementDemo.js +3 -3
- package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +45 -27
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +29 -22
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +3 -3
- package/lib/commonjs/ui/screens/AppInfoScreen.js +6 -6
- package/lib/commonjs/ui/screens/BillingManagementScreen.js +3 -3
- package/lib/commonjs/ui/screens/FileManagementScreen.js +324 -306
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +3 -3
- package/lib/commonjs/ui/screens/ProfileScreen.js +2 -2
- package/lib/commonjs/ui/screens/SessionManagementScreen.js +2 -2
- package/lib/commonjs/ui/screens/SignInScreen.js +358 -310
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignUpScreen.js +483 -308
- package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +3 -3
- package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js +51 -26
- package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +2 -2
- package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +1 -1
- package/lib/commonjs/ui/styles/index.js +2 -2
- package/lib/commonjs/ui/styles/theme.js +1 -1
- package/lib/commonjs/utils/index.js +1 -1
- package/lib/module/assets/icons/OxyServices.js +1 -1
- package/lib/module/assets/icons/OxyServices.js.map +1 -1
- package/lib/module/core/index.js +84 -2
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/index.js +10 -10
- package/lib/module/index.js.map +1 -1
- package/lib/module/node/index.js +4 -4
- package/lib/module/node/index.js.map +1 -1
- package/lib/module/ui/components/Avatar.js +2 -2
- package/lib/module/ui/components/Avatar.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +3 -3
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/GroupedSection.js +1 -1
- package/lib/module/ui/components/GroupedSection.js.map +1 -1
- package/lib/module/ui/components/OxyLogo.js +1 -1
- package/lib/module/ui/components/OxyLogo.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +10 -10
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +2 -2
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/components/ProfileCard.js +2 -2
- package/lib/module/ui/components/ProfileCard.js.map +1 -1
- package/lib/module/ui/components/Section.js +1 -1
- package/lib/module/ui/components/Section.js.map +1 -1
- package/lib/module/ui/components/SectionTitle.js +1 -1
- package/lib/module/ui/components/SectionTitle.js.map +1 -1
- package/lib/module/ui/components/icon/index.js +1 -1
- package/lib/module/ui/components/icon/index.js.map +1 -1
- package/lib/module/ui/components/index.js +12 -12
- package/lib/module/ui/components/index.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +20 -4
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/index.js +10 -10
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/navigation/OxyRouter.js +18 -18
- package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
- package/lib/module/ui/screens/AccountCenterScreen.js +5 -5
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountManagementDemo.js +2 -2
- package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js +46 -28
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +30 -23
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +3 -3
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/AppInfoScreen.js +6 -6
- package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/module/ui/screens/BillingManagementScreen.js +3 -3
- package/lib/module/ui/screens/BillingManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +325 -307
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js +3 -3
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +2 -2
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/SessionManagementScreen.js +2 -2
- package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +358 -310
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/SignUpScreen.js +486 -309
- package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js +3 -3
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaFAQScreen.js +52 -27
- package/lib/module/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +2 -2
- package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaRulesScreen.js +1 -1
- package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
- package/lib/module/ui/styles/index.js +2 -2
- package/lib/module/ui/styles/index.js.map +1 -1
- package/lib/module/ui/styles/theme.js +1 -1
- package/lib/module/ui/styles/theme.js.map +1 -1
- package/lib/module/utils/index.js +1 -1
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/core/index.d.ts +24 -0
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts +2 -2
- package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts +2 -2
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/karma/KarmaFAQScreen.d.ts +2 -2
- package/lib/typescript/ui/screens/karma/KarmaFAQScreen.d.ts.map +1 -1
- package/package.json +21 -5
- package/src/core/index.ts +68 -0
- package/src/ui/components/OxyProvider.tsx +5 -5
- package/src/ui/context/OxyContext.tsx +61 -41
- package/src/ui/screens/AccountOverviewScreen.tsx +44 -26
- package/src/ui/screens/AccountSettingsScreen.tsx +24 -18
- package/src/ui/screens/FileManagementScreen.tsx +246 -211
- package/src/ui/screens/SignInScreen.tsx +382 -326
- package/src/ui/screens/SignUpScreen.tsx +443 -273
- package/src/ui/screens/karma/KarmaFAQScreen.tsx +50 -29
- package/lib/commonjs/package.json +0 -1
- package/lib/module/package.json +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useRef, useEffect } from 'react';
|
|
1
|
+
import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
4
|
Text,
|
|
@@ -34,10 +34,17 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
34
34
|
const [password, setPassword] = useState('');
|
|
35
35
|
const [errorMessage, setErrorMessage] = useState('');
|
|
36
36
|
const [userProfile, setUserProfile] = useState<any>(null);
|
|
37
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
37
38
|
|
|
38
39
|
// Multi-step form states
|
|
39
40
|
const [currentStep, setCurrentStep] = useState(0);
|
|
40
41
|
const [isInputFocused, setIsInputFocused] = useState(false);
|
|
42
|
+
const [isValidating, setIsValidating] = useState(false);
|
|
43
|
+
const [validationStatus, setValidationStatus] = useState<'idle' | 'validating' | 'valid' | 'invalid'>('idle');
|
|
44
|
+
|
|
45
|
+
// Cache for validation results to prevent repeated API calls
|
|
46
|
+
const validationCache = useRef<Map<string, { profile: any; timestamp: number }>>(new Map());
|
|
47
|
+
|
|
41
48
|
const fadeAnim = useRef(new Animated.Value(1)).current;
|
|
42
49
|
const slideAnim = useRef(new Animated.Value(0)).current;
|
|
43
50
|
const scaleAnim = useRef(new Animated.Value(1)).current;
|
|
@@ -45,13 +52,19 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
45
52
|
const logoAnim = useRef(new Animated.Value(0)).current;
|
|
46
53
|
const progressAnim = useRef(new Animated.Value(0.5)).current;
|
|
47
54
|
|
|
48
|
-
const { login, isLoading, user, isAuthenticated, sessions } = useOxy();
|
|
55
|
+
const { login, isLoading, user, isAuthenticated, sessions, oxyServices } = useOxy();
|
|
49
56
|
|
|
50
57
|
const colors = useThemeColors(theme);
|
|
51
58
|
const commonStyles = createCommonStyles(theme);
|
|
52
59
|
|
|
53
60
|
// Check if this should be treated as "Add Account" mode
|
|
54
|
-
const isAddAccountMode =
|
|
61
|
+
const isAddAccountMode = useMemo(() =>
|
|
62
|
+
user && isAuthenticated && sessions && sessions.length > 0,
|
|
63
|
+
[user, isAuthenticated, sessions]
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Memoized styles to prevent rerenders
|
|
67
|
+
const styles = useMemo(() => createStyles(colors, theme), [colors, theme]);
|
|
55
68
|
|
|
56
69
|
// Initialize logo animation
|
|
57
70
|
useEffect(() => {
|
|
@@ -61,27 +74,161 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
61
74
|
friction: 8,
|
|
62
75
|
useNativeDriver: true,
|
|
63
76
|
}).start();
|
|
64
|
-
}, []);
|
|
77
|
+
}, [logoAnim]);
|
|
65
78
|
|
|
66
79
|
// Input focus animations
|
|
67
|
-
const handleInputFocus = () => {
|
|
80
|
+
const handleInputFocus = useCallback(() => {
|
|
68
81
|
setIsInputFocused(true);
|
|
69
82
|
Animated.spring(inputScaleAnim, {
|
|
70
83
|
toValue: 1.02,
|
|
71
84
|
useNativeDriver: true,
|
|
72
85
|
}).start();
|
|
73
|
-
};
|
|
86
|
+
}, [inputScaleAnim]);
|
|
74
87
|
|
|
75
|
-
const handleInputBlur = () => {
|
|
88
|
+
const handleInputBlur = useCallback(() => {
|
|
76
89
|
setIsInputFocused(false);
|
|
77
90
|
Animated.spring(inputScaleAnim, {
|
|
78
91
|
toValue: 1,
|
|
79
92
|
useNativeDriver: true,
|
|
80
93
|
}).start();
|
|
81
|
-
};
|
|
94
|
+
}, [inputScaleAnim]);
|
|
95
|
+
|
|
96
|
+
// Memoized input change handlers to prevent re-renders
|
|
97
|
+
const handleUsernameChange = useCallback((text: string) => {
|
|
98
|
+
setUsername(text);
|
|
99
|
+
// Only clear error if we're changing from an invalid state
|
|
100
|
+
if (validationStatus === 'invalid') {
|
|
101
|
+
setErrorMessage('');
|
|
102
|
+
setValidationStatus('idle');
|
|
103
|
+
}
|
|
104
|
+
}, [validationStatus]);
|
|
105
|
+
|
|
106
|
+
const handlePasswordChange = useCallback((text: string) => {
|
|
107
|
+
setPassword(text);
|
|
108
|
+
setErrorMessage(''); // Clear error when user types
|
|
109
|
+
}, []);
|
|
110
|
+
|
|
111
|
+
// Username validation using core services with caching
|
|
112
|
+
const validateUsername = useCallback(async (usernameToValidate: string) => {
|
|
113
|
+
if (!usernameToValidate || usernameToValidate.length < 3) {
|
|
114
|
+
setValidationStatus('invalid');
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check cache first (cache valid for 5 minutes)
|
|
119
|
+
const cached = validationCache.current.get(usernameToValidate);
|
|
120
|
+
const now = Date.now();
|
|
121
|
+
if (cached && (now - cached.timestamp) < 5 * 60 * 1000) {
|
|
122
|
+
setUserProfile(cached.profile);
|
|
123
|
+
setValidationStatus('valid');
|
|
124
|
+
setErrorMessage('');
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
setIsValidating(true);
|
|
129
|
+
setValidationStatus('validating');
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
// First check if username exists by trying to get profile
|
|
133
|
+
const profile = await oxyServices.getUserProfileByUsername(usernameToValidate);
|
|
134
|
+
|
|
135
|
+
if (profile) {
|
|
136
|
+
const profileData = {
|
|
137
|
+
displayName: profile.name?.full || profile.name?.first || profile.username,
|
|
138
|
+
name: profile.username,
|
|
139
|
+
avatar: profile.avatar,
|
|
140
|
+
id: profile.id
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
setUserProfile(profileData);
|
|
144
|
+
setValidationStatus('valid');
|
|
145
|
+
setErrorMessage(''); // Clear any previous errors
|
|
146
|
+
|
|
147
|
+
// Cache the result
|
|
148
|
+
validationCache.current.set(usernameToValidate, {
|
|
149
|
+
profile: profileData,
|
|
150
|
+
timestamp: now
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return true;
|
|
154
|
+
} else {
|
|
155
|
+
setValidationStatus('invalid');
|
|
156
|
+
setErrorMessage('Username not found. Please check your username or sign up.');
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
} catch (error: any) {
|
|
160
|
+
// If user not found (404), username doesn't exist
|
|
161
|
+
if (error.status === 404 || error.code === 'USER_NOT_FOUND') {
|
|
162
|
+
setValidationStatus('invalid');
|
|
163
|
+
setErrorMessage('Username not found. Please check your username or sign up.');
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// For other errors, show generic message
|
|
168
|
+
console.error('Username validation error:', error);
|
|
169
|
+
setValidationStatus('invalid');
|
|
170
|
+
setErrorMessage('Unable to validate username. Please try again.');
|
|
171
|
+
return false;
|
|
172
|
+
} finally {
|
|
173
|
+
setIsValidating(false);
|
|
174
|
+
}
|
|
175
|
+
}, [oxyServices]);
|
|
176
|
+
|
|
177
|
+
// Debounced username validation - increased debounce time and added better conditions
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (!username || username.length < 3) {
|
|
180
|
+
setValidationStatus('idle');
|
|
181
|
+
setUserProfile(null);
|
|
182
|
+
setErrorMessage(''); // Clear error when input is too short
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Only validate if we haven't already validated this exact username
|
|
187
|
+
if (validationStatus === 'valid' && userProfile?.name === username) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const timeoutId = setTimeout(() => {
|
|
192
|
+
validateUsername(username);
|
|
193
|
+
}, 800); // Increased debounce to 800ms
|
|
194
|
+
|
|
195
|
+
return () => clearTimeout(timeoutId);
|
|
196
|
+
}, [username, validateUsername, validationStatus, userProfile?.name]);
|
|
197
|
+
|
|
198
|
+
// Cleanup cache on unmount and limit cache size
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
return () => {
|
|
201
|
+
// Clear cache on unmount
|
|
202
|
+
validationCache.current.clear();
|
|
203
|
+
};
|
|
204
|
+
}, []);
|
|
205
|
+
|
|
206
|
+
// Clean up old cache entries periodically (older than 10 minutes)
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
const cleanupInterval = setInterval(() => {
|
|
209
|
+
const now = Date.now();
|
|
210
|
+
const maxAge = 10 * 60 * 1000; // 10 minutes
|
|
211
|
+
|
|
212
|
+
for (const [key, value] of validationCache.current.entries()) {
|
|
213
|
+
if (now - value.timestamp > maxAge) {
|
|
214
|
+
validationCache.current.delete(key);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Limit cache size to 50 entries
|
|
219
|
+
if (validationCache.current.size > 50) {
|
|
220
|
+
const entries = Array.from(validationCache.current.entries());
|
|
221
|
+
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
222
|
+
const toDelete = entries.slice(0, entries.length - 50);
|
|
223
|
+
toDelete.forEach(([key]) => validationCache.current.delete(key));
|
|
224
|
+
}
|
|
225
|
+
}, 5 * 60 * 1000); // Clean up every 5 minutes
|
|
226
|
+
|
|
227
|
+
return () => clearInterval(cleanupInterval);
|
|
228
|
+
}, []);
|
|
82
229
|
|
|
83
230
|
// Animation functions
|
|
84
|
-
const animateTransition = (nextStep: number) => {
|
|
231
|
+
const animateTransition = useCallback((nextStep: number) => {
|
|
85
232
|
// Scale down current content
|
|
86
233
|
Animated.timing(scaleAnim, {
|
|
87
234
|
toValue: 0.95,
|
|
@@ -122,9 +269,9 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
122
269
|
})
|
|
123
270
|
]).start();
|
|
124
271
|
});
|
|
125
|
-
};
|
|
272
|
+
}, [fadeAnim, slideAnim, scaleAnim]);
|
|
126
273
|
|
|
127
|
-
const nextStep = () => {
|
|
274
|
+
const nextStep = useCallback(() => {
|
|
128
275
|
if (currentStep < 1) {
|
|
129
276
|
// Animate progress bar
|
|
130
277
|
Animated.timing(progressAnim, {
|
|
@@ -132,12 +279,12 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
132
279
|
duration: 300,
|
|
133
280
|
useNativeDriver: false,
|
|
134
281
|
}).start();
|
|
135
|
-
|
|
282
|
+
|
|
136
283
|
animateTransition(currentStep + 1);
|
|
137
284
|
}
|
|
138
|
-
};
|
|
285
|
+
}, [currentStep, progressAnim, animateTransition]);
|
|
139
286
|
|
|
140
|
-
const prevStep = () => {
|
|
287
|
+
const prevStep = useCallback(() => {
|
|
141
288
|
if (currentStep > 0) {
|
|
142
289
|
// Animate progress bar
|
|
143
290
|
Animated.timing(progressAnim, {
|
|
@@ -145,43 +292,39 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
145
292
|
duration: 300,
|
|
146
293
|
useNativeDriver: false,
|
|
147
294
|
}).start();
|
|
148
|
-
|
|
295
|
+
|
|
149
296
|
animateTransition(currentStep - 1);
|
|
150
297
|
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
// Fetch user profile when username is entered
|
|
154
|
-
useEffect(() => {
|
|
155
|
-
const fetchUserProfile = async () => {
|
|
156
|
-
if (username.length >= 3 && currentStep === 1) {
|
|
157
|
-
try {
|
|
158
|
-
// For now, we'll create a mock profile based on username
|
|
159
|
-
// In a real app, you'd fetch this from your API
|
|
160
|
-
setUserProfile({
|
|
161
|
-
displayName: username,
|
|
162
|
-
name: username,
|
|
163
|
-
avatar: null, // Could be fetched from API
|
|
164
|
-
});
|
|
165
|
-
} catch (error) {
|
|
166
|
-
// If user not found, we'll show a generic avatar
|
|
167
|
-
setUserProfile(null);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
};
|
|
298
|
+
}, [currentStep, progressAnim, animateTransition]);
|
|
171
299
|
|
|
172
|
-
|
|
173
|
-
}, [username, currentStep]);
|
|
174
|
-
|
|
175
|
-
const handleUsernameNext = () => {
|
|
300
|
+
const handleUsernameNext = useCallback(() => {
|
|
176
301
|
if (!username) {
|
|
177
302
|
toast.error('Please enter your username');
|
|
178
303
|
return;
|
|
179
304
|
}
|
|
180
|
-
setErrorMessage('');
|
|
181
|
-
nextStep();
|
|
182
|
-
};
|
|
183
305
|
|
|
184
|
-
|
|
306
|
+
if (validationStatus === 'invalid') {
|
|
307
|
+
// Don't show toast if we already have an error message displayed
|
|
308
|
+
if (!errorMessage) {
|
|
309
|
+
toast.error('Please enter a valid username');
|
|
310
|
+
}
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (validationStatus === 'validating') {
|
|
315
|
+
toast.error('Please wait while we validate your username');
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (validationStatus === 'valid' && userProfile) {
|
|
320
|
+
setErrorMessage('');
|
|
321
|
+
nextStep();
|
|
322
|
+
} else {
|
|
323
|
+
toast.error('Please enter a valid username');
|
|
324
|
+
}
|
|
325
|
+
}, [username, validationStatus, userProfile, errorMessage, nextStep]);
|
|
326
|
+
|
|
327
|
+
const handleLogin = useCallback(async () => {
|
|
185
328
|
if (!username || !password) {
|
|
186
329
|
toast.error('Please enter both username and password');
|
|
187
330
|
return;
|
|
@@ -194,18 +337,18 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
194
337
|
} catch (error: any) {
|
|
195
338
|
toast.error(error.message || 'Login failed');
|
|
196
339
|
}
|
|
197
|
-
};
|
|
340
|
+
}, [username, password, login]);
|
|
198
341
|
|
|
199
|
-
//
|
|
200
|
-
const renderUsernameStep = () => (
|
|
342
|
+
// Memoized step components
|
|
343
|
+
const renderUsernameStep = useMemo(() => (
|
|
201
344
|
<Animated.View style={[
|
|
202
345
|
styles.stepContainer,
|
|
203
|
-
{
|
|
204
|
-
opacity: fadeAnim,
|
|
346
|
+
{
|
|
347
|
+
opacity: fadeAnim,
|
|
205
348
|
transform: [
|
|
206
349
|
{ translateX: slideAnim },
|
|
207
350
|
{ scale: scaleAnim }
|
|
208
|
-
]
|
|
351
|
+
]
|
|
209
352
|
}
|
|
210
353
|
]}>
|
|
211
354
|
<View style={styles.modernImageContainer}>
|
|
@@ -220,7 +363,7 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
220
363
|
<Stop offset="100%" stopColor={colors.primary} stopOpacity="0.3" />
|
|
221
364
|
</LinearGradient>
|
|
222
365
|
</Defs>
|
|
223
|
-
|
|
366
|
+
|
|
224
367
|
{/* Modern abstract shapes */}
|
|
225
368
|
<Circle cx="80" cy="80" r="45" fill="url(#primaryGradient)" />
|
|
226
369
|
<Circle cx="200" cy="80" r="35" fill="url(#secondaryGradient)" />
|
|
@@ -231,12 +374,12 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
231
374
|
fill="none"
|
|
232
375
|
strokeLinecap="round"
|
|
233
376
|
/>
|
|
234
|
-
|
|
377
|
+
|
|
235
378
|
{/* Floating elements */}
|
|
236
379
|
<Circle cx="60" cy="50" r="8" fill={colors.primary} opacity="0.6" />
|
|
237
380
|
<Circle cx="220" cy="120" r="6" fill={colors.primary} opacity="0.4" />
|
|
238
381
|
<Circle cx="250" cy="40" r="4" fill={colors.primary} opacity="0.8" />
|
|
239
|
-
|
|
382
|
+
|
|
240
383
|
{/* Central focus element */}
|
|
241
384
|
<Circle cx="140" cy="80" r="25" fill={colors.background} opacity="0.9" />
|
|
242
385
|
<Circle cx="135" cy="75" r="3" fill={colors.primary} />
|
|
@@ -256,7 +399,7 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
256
399
|
{isAddAccountMode ? 'Add Account' : 'Welcome Back'}
|
|
257
400
|
</Text>
|
|
258
401
|
<Text style={[styles.modernSubtitle, { color: colors.secondaryText }]}>
|
|
259
|
-
{isAddAccountMode
|
|
402
|
+
{isAddAccountMode
|
|
260
403
|
? 'Sign in with another account'
|
|
261
404
|
: 'Sign in to continue your journey'
|
|
262
405
|
}
|
|
@@ -283,10 +426,17 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
283
426
|
styles.modernInputContainer,
|
|
284
427
|
{ transform: [{ scale: inputScaleAnim }] }
|
|
285
428
|
]}>
|
|
286
|
-
<View style={[
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
429
|
+
<View style={[
|
|
430
|
+
styles.inputWrapper,
|
|
431
|
+
{
|
|
432
|
+
borderColor: validationStatus === 'valid' ? colors.success :
|
|
433
|
+
validationStatus === 'invalid' ? colors.error :
|
|
434
|
+
isInputFocused ? colors.primary : colors.border
|
|
435
|
+
}
|
|
436
|
+
]}>
|
|
437
|
+
<Ionicons
|
|
438
|
+
name="person-outline"
|
|
439
|
+
size={20}
|
|
290
440
|
color={isInputFocused ? colors.primary : colors.secondaryText}
|
|
291
441
|
style={styles.inputIcon}
|
|
292
442
|
/>
|
|
@@ -295,30 +445,64 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
295
445
|
placeholder="Enter your username"
|
|
296
446
|
placeholderTextColor={colors.placeholder}
|
|
297
447
|
value={username}
|
|
298
|
-
onChangeText={
|
|
448
|
+
onChangeText={handleUsernameChange}
|
|
299
449
|
onFocus={handleInputFocus}
|
|
300
450
|
onBlur={handleInputBlur}
|
|
301
451
|
autoCapitalize="none"
|
|
302
452
|
testID="username-input"
|
|
303
453
|
/>
|
|
454
|
+
{validationStatus === 'validating' && (
|
|
455
|
+
<ActivityIndicator size="small" color={colors.primary} style={styles.validationIndicator} />
|
|
456
|
+
)}
|
|
457
|
+
{validationStatus === 'valid' && (
|
|
458
|
+
<Ionicons name="checkmark-circle" size={20} color={colors.success} style={styles.validationIndicator} />
|
|
459
|
+
)}
|
|
460
|
+
{validationStatus === 'invalid' && username.length >= 3 && (
|
|
461
|
+
<Ionicons name="close-circle" size={20} color={colors.error} style={styles.validationIndicator} />
|
|
462
|
+
)}
|
|
304
463
|
</View>
|
|
464
|
+
|
|
465
|
+
{/* Validation feedback */}
|
|
466
|
+
{validationStatus === 'valid' && userProfile && (
|
|
467
|
+
<View style={[styles.validationSuccessCard, { backgroundColor: colors.success + '15' }]}>
|
|
468
|
+
<Ionicons name="checkmark-circle" size={16} color={colors.success} />
|
|
469
|
+
<Text style={[styles.validationText, { color: colors.success }]}>
|
|
470
|
+
Found user: {userProfile.displayName}
|
|
471
|
+
</Text>
|
|
472
|
+
</View>
|
|
473
|
+
)}
|
|
474
|
+
|
|
475
|
+
{validationStatus === 'invalid' && username.length >= 3 && !errorMessage && (
|
|
476
|
+
<View style={[styles.validationErrorCard, { backgroundColor: colors.error + '15' }]}>
|
|
477
|
+
<Ionicons name="alert-circle" size={16} color={colors.error} />
|
|
478
|
+
<Text style={[styles.validationText, { color: colors.error }]}>
|
|
479
|
+
Username not found
|
|
480
|
+
</Text>
|
|
481
|
+
</View>
|
|
482
|
+
)}
|
|
305
483
|
</Animated.View>
|
|
306
484
|
|
|
307
485
|
<TouchableOpacity
|
|
308
486
|
style={[
|
|
309
|
-
styles.modernButton,
|
|
310
|
-
{
|
|
487
|
+
styles.modernButton,
|
|
488
|
+
{
|
|
311
489
|
backgroundColor: colors.primary,
|
|
312
|
-
opacity: !username ? 0.5 : 1,
|
|
490
|
+
opacity: (!username || validationStatus !== 'valid') ? 0.5 : 1,
|
|
313
491
|
shadowColor: colors.primary,
|
|
314
492
|
}
|
|
315
493
|
]}
|
|
316
494
|
onPress={handleUsernameNext}
|
|
317
|
-
disabled={!username}
|
|
495
|
+
disabled={!username || validationStatus !== 'valid' || isValidating}
|
|
318
496
|
testID="username-next-button"
|
|
319
497
|
>
|
|
320
|
-
|
|
321
|
-
|
|
498
|
+
{isValidating ? (
|
|
499
|
+
<ActivityIndicator color="#FFFFFF" size="small" />
|
|
500
|
+
) : (
|
|
501
|
+
<>
|
|
502
|
+
<Text style={styles.modernButtonText}>Continue</Text>
|
|
503
|
+
<Ionicons name="arrow-forward" size={20} color="#FFFFFF" style={styles.buttonIcon} />
|
|
504
|
+
</>
|
|
505
|
+
)}
|
|
322
506
|
</TouchableOpacity>
|
|
323
507
|
|
|
324
508
|
<View style={styles.footerTextContainer}>
|
|
@@ -330,17 +514,22 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
330
514
|
</TouchableOpacity>
|
|
331
515
|
</View>
|
|
332
516
|
</Animated.View>
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
|
|
517
|
+
), [
|
|
518
|
+
fadeAnim, slideAnim, scaleAnim, colors, isAddAccountMode, user?.username,
|
|
519
|
+
errorMessage, inputScaleAnim, isInputFocused, username, validationStatus,
|
|
520
|
+
userProfile, isValidating, handleInputFocus, handleInputBlur, handleUsernameChange,
|
|
521
|
+
handleUsernameNext, navigate, styles
|
|
522
|
+
]);
|
|
523
|
+
|
|
524
|
+
const renderPasswordStep = useMemo(() => (
|
|
336
525
|
<Animated.View style={[
|
|
337
526
|
styles.stepContainer,
|
|
338
|
-
{
|
|
339
|
-
opacity: fadeAnim,
|
|
527
|
+
{
|
|
528
|
+
opacity: fadeAnim,
|
|
340
529
|
transform: [
|
|
341
530
|
{ translateX: slideAnim },
|
|
342
531
|
{ scale: scaleAnim }
|
|
343
|
-
]
|
|
532
|
+
]
|
|
344
533
|
}
|
|
345
534
|
]}>
|
|
346
535
|
<View style={styles.modernUserProfileContainer}>
|
|
@@ -357,14 +546,14 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
357
546
|
/>
|
|
358
547
|
<View style={[styles.statusIndicator, { backgroundColor: colors.primary }]} />
|
|
359
548
|
</Animated.View>
|
|
360
|
-
|
|
549
|
+
|
|
361
550
|
<Text style={[styles.modernUserDisplayName, { color: colors.text }]}>
|
|
362
551
|
{userProfile?.displayName || userProfile?.name || username}
|
|
363
552
|
</Text>
|
|
364
553
|
<Text style={[styles.modernUsernameSubtext, { color: colors.secondaryText }]}>
|
|
365
554
|
@{username}
|
|
366
555
|
</Text>
|
|
367
|
-
|
|
556
|
+
|
|
368
557
|
<View style={[styles.welcomeBackBadge, { backgroundColor: colors.primary + '15' }]}>
|
|
369
558
|
<Ionicons name="checkmark-circle" size={16} color={colors.primary} />
|
|
370
559
|
<Text style={[styles.welcomeBackText, { color: colors.primary }]}>
|
|
@@ -385,9 +574,9 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
385
574
|
{ transform: [{ scale: inputScaleAnim }] }
|
|
386
575
|
]}>
|
|
387
576
|
<View style={[styles.inputWrapper, { borderColor: isInputFocused ? colors.primary : colors.border }]}>
|
|
388
|
-
<Ionicons
|
|
389
|
-
name="lock-closed-outline"
|
|
390
|
-
size={20}
|
|
577
|
+
<Ionicons
|
|
578
|
+
name="lock-closed-outline"
|
|
579
|
+
size={20}
|
|
391
580
|
color={isInputFocused ? colors.primary : colors.secondaryText}
|
|
392
581
|
style={styles.inputIcon}
|
|
393
582
|
/>
|
|
@@ -396,36 +585,45 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
396
585
|
placeholder="Enter your password"
|
|
397
586
|
placeholderTextColor={colors.placeholder}
|
|
398
587
|
value={password}
|
|
399
|
-
onChangeText={
|
|
588
|
+
onChangeText={handlePasswordChange}
|
|
400
589
|
onFocus={handleInputFocus}
|
|
401
590
|
onBlur={handleInputBlur}
|
|
402
|
-
secureTextEntry
|
|
591
|
+
secureTextEntry={!showPassword}
|
|
592
|
+
autoCapitalize="none"
|
|
403
593
|
testID="password-input"
|
|
404
594
|
/>
|
|
595
|
+
<TouchableOpacity
|
|
596
|
+
style={styles.passwordToggle}
|
|
597
|
+
onPress={() => setShowPassword(!showPassword)}
|
|
598
|
+
>
|
|
599
|
+
<Ionicons
|
|
600
|
+
name={showPassword ? "eye-off" : "eye"}
|
|
601
|
+
size={20}
|
|
602
|
+
color={colors.secondaryText}
|
|
603
|
+
/>
|
|
604
|
+
</TouchableOpacity>
|
|
405
605
|
</View>
|
|
406
606
|
</Animated.View>
|
|
407
607
|
|
|
408
608
|
<TouchableOpacity
|
|
409
609
|
style={[
|
|
410
|
-
styles.modernButton,
|
|
411
|
-
{
|
|
610
|
+
styles.modernButton,
|
|
611
|
+
{
|
|
412
612
|
backgroundColor: colors.primary,
|
|
413
|
-
opacity:
|
|
613
|
+
opacity: !password ? 0.5 : 1,
|
|
414
614
|
shadowColor: colors.primary,
|
|
415
615
|
}
|
|
416
616
|
]}
|
|
417
617
|
onPress={handleLogin}
|
|
418
|
-
disabled={isLoading}
|
|
618
|
+
disabled={!password || isLoading}
|
|
419
619
|
testID="login-button"
|
|
420
620
|
>
|
|
421
621
|
{isLoading ? (
|
|
422
622
|
<ActivityIndicator color="#FFFFFF" size="small" />
|
|
423
623
|
) : (
|
|
424
624
|
<>
|
|
425
|
-
<Text style={styles.modernButtonText}>
|
|
426
|
-
|
|
427
|
-
</Text>
|
|
428
|
-
<Ionicons name="arrow-forward" size={20} color="#FFFFFF" style={styles.buttonIcon} />
|
|
625
|
+
<Text style={styles.modernButtonText}>Sign In</Text>
|
|
626
|
+
<Ionicons name="log-in" size={20} color="#FFFFFF" style={styles.buttonIcon} />
|
|
429
627
|
</>
|
|
430
628
|
)}
|
|
431
629
|
</TouchableOpacity>
|
|
@@ -435,232 +633,136 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
435
633
|
style={[styles.modernBackButton, { borderColor: colors.border }]}
|
|
436
634
|
onPress={prevStep}
|
|
437
635
|
>
|
|
438
|
-
<Ionicons name="
|
|
636
|
+
<Ionicons name="arrow-back" size={18} color={colors.text} />
|
|
439
637
|
<Text style={[styles.modernBackButtonText, { color: colors.text }]}>Back</Text>
|
|
440
638
|
</TouchableOpacity>
|
|
441
639
|
</View>
|
|
442
640
|
|
|
443
|
-
{/* Security notice */}
|
|
444
641
|
<View style={styles.securityNotice}>
|
|
445
|
-
<Ionicons name="shield-checkmark" size={
|
|
642
|
+
<Ionicons name="shield-checkmark" size={14} color={colors.secondaryText} />
|
|
446
643
|
<Text style={[styles.securityText, { color: colors.secondaryText }]}>
|
|
447
|
-
Your
|
|
644
|
+
Your data is encrypted and secure
|
|
448
645
|
</Text>
|
|
449
646
|
</View>
|
|
450
647
|
</Animated.View>
|
|
451
|
-
)
|
|
648
|
+
), [
|
|
649
|
+
fadeAnim, slideAnim, scaleAnim, colors, userProfile, username, theme, logoAnim,
|
|
650
|
+
errorMessage, inputScaleAnim, isInputFocused, password, showPassword,
|
|
651
|
+
handleInputFocus, handleInputBlur, handlePasswordChange, handleLogin, isLoading, prevStep, styles
|
|
652
|
+
]);
|
|
452
653
|
|
|
453
|
-
const renderCurrentStep = () => {
|
|
654
|
+
const renderCurrentStep = useCallback(() => {
|
|
454
655
|
switch (currentStep) {
|
|
455
656
|
case 0:
|
|
456
|
-
return renderUsernameStep
|
|
657
|
+
return renderUsernameStep;
|
|
457
658
|
case 1:
|
|
458
|
-
return renderPasswordStep
|
|
659
|
+
return renderPasswordStep;
|
|
459
660
|
default:
|
|
460
|
-
return renderUsernameStep
|
|
661
|
+
return renderUsernameStep;
|
|
461
662
|
}
|
|
462
|
-
};
|
|
663
|
+
}, [currentStep, renderUsernameStep, renderPasswordStep]);
|
|
463
664
|
|
|
464
665
|
return (
|
|
465
|
-
<
|
|
466
|
-
|
|
467
|
-
|
|
666
|
+
<KeyboardAvoidingView
|
|
667
|
+
style={[styles.container, { backgroundColor: colors.background }]}
|
|
668
|
+
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
468
669
|
>
|
|
469
|
-
<
|
|
470
|
-
|
|
471
|
-
{
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
<View style={styles.progressTrack}>
|
|
483
|
-
<Animated.View
|
|
484
|
-
style={[
|
|
485
|
-
styles.progressFill,
|
|
486
|
-
{
|
|
487
|
-
backgroundColor: colors.primary,
|
|
488
|
-
width: progressAnim.interpolate({
|
|
489
|
-
inputRange: [0, 1],
|
|
490
|
-
outputRange: ['50%', '100%']
|
|
491
|
-
})
|
|
492
|
-
}
|
|
493
|
-
]}
|
|
494
|
-
/>
|
|
495
|
-
</View>
|
|
496
|
-
<Text style={[styles.progressText, { color: colors.secondaryText }]}>
|
|
497
|
-
Step {currentStep + 1} of 2
|
|
498
|
-
</Text>
|
|
499
|
-
</View>
|
|
500
|
-
|
|
501
|
-
{renderCurrentStep()}
|
|
502
|
-
</BottomSheetScrollView>
|
|
670
|
+
<StatusBar
|
|
671
|
+
barStyle={theme === 'dark' ? 'light-content' : 'dark-content'}
|
|
672
|
+
backgroundColor={colors.background}
|
|
673
|
+
/>
|
|
674
|
+
|
|
675
|
+
<ScrollView
|
|
676
|
+
contentContainerStyle={styles.scrollContent}
|
|
677
|
+
showsVerticalScrollIndicator={false}
|
|
678
|
+
keyboardShouldPersistTaps="handled"
|
|
679
|
+
>
|
|
680
|
+
{renderCurrentStep()}
|
|
681
|
+
</ScrollView>
|
|
682
|
+
</KeyboardAvoidingView>
|
|
503
683
|
);
|
|
504
684
|
};
|
|
505
685
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
? 'Phudu'
|
|
511
|
-
: 'Phudu-Bold',
|
|
512
|
-
fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
|
|
513
|
-
fontSize: 54,
|
|
514
|
-
marginBottom: 24,
|
|
515
|
-
},
|
|
516
|
-
formContainer: {
|
|
517
|
-
width: '100%',
|
|
518
|
-
},
|
|
519
|
-
inputContainer: {
|
|
520
|
-
marginBottom: 16,
|
|
686
|
+
// Memoized styles creation
|
|
687
|
+
const createStyles = (colors: any, theme: string) => StyleSheet.create({
|
|
688
|
+
container: {
|
|
689
|
+
flex: 1,
|
|
521
690
|
},
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
691
|
+
scrollContent: {
|
|
692
|
+
flexGrow: 1,
|
|
693
|
+
paddingHorizontal: 24,
|
|
694
|
+
paddingTop: 40,
|
|
695
|
+
paddingBottom: 40,
|
|
526
696
|
},
|
|
527
|
-
|
|
528
|
-
|
|
697
|
+
stepContainer: {
|
|
698
|
+
flex: 1,
|
|
529
699
|
justifyContent: 'center',
|
|
530
|
-
marginTop: 24,
|
|
531
|
-
},
|
|
532
|
-
footerText: {
|
|
533
|
-
fontSize: 14,
|
|
534
|
-
lineHeight: 20,
|
|
535
|
-
},
|
|
536
|
-
linkText: {
|
|
537
|
-
fontSize: 14,
|
|
538
|
-
lineHeight: 20,
|
|
539
|
-
fontWeight: '600',
|
|
540
|
-
},
|
|
541
|
-
userInfoContainer: {
|
|
542
|
-
padding: 20,
|
|
543
|
-
marginVertical: 20,
|
|
544
|
-
borderRadius: 35,
|
|
545
700
|
alignItems: 'center',
|
|
701
|
+
minHeight: 600,
|
|
546
702
|
},
|
|
547
|
-
userInfoText: {
|
|
548
|
-
fontSize: 16,
|
|
549
|
-
lineHeight: 24,
|
|
550
|
-
textAlign: 'center',
|
|
551
|
-
},
|
|
552
|
-
actionButtonsContainer: {
|
|
553
|
-
marginTop: 20,
|
|
554
|
-
},
|
|
555
|
-
infoContainer: {
|
|
556
|
-
padding: 16,
|
|
557
|
-
marginVertical: 16,
|
|
558
|
-
borderRadius: 8,
|
|
559
|
-
alignItems: 'center',
|
|
560
|
-
},
|
|
561
|
-
infoText: {
|
|
562
|
-
fontSize: 14,
|
|
563
|
-
lineHeight: 20,
|
|
564
|
-
textAlign: 'center',
|
|
565
|
-
},
|
|
566
|
-
|
|
567
|
-
// Modern UI Styles
|
|
568
|
-
logoContainer: {
|
|
569
|
-
alignItems: 'center',
|
|
570
|
-
marginBottom: 16,
|
|
571
|
-
},
|
|
572
|
-
modernProgressContainer: {
|
|
573
|
-
alignItems: 'center',
|
|
574
|
-
marginBottom: 40,
|
|
575
|
-
paddingHorizontal: 20,
|
|
576
|
-
},
|
|
577
|
-
progressTrack: {
|
|
578
|
-
width: '100%',
|
|
579
|
-
height: 4,
|
|
580
|
-
backgroundColor: '#E5E5E5',
|
|
581
|
-
borderRadius: 2,
|
|
582
|
-
marginBottom: 8,
|
|
583
|
-
overflow: 'hidden',
|
|
584
|
-
},
|
|
585
|
-
progressFill: {
|
|
586
|
-
height: '100%',
|
|
587
|
-
borderRadius: 2,
|
|
588
|
-
},
|
|
589
|
-
progressText: {
|
|
590
|
-
fontSize: 12,
|
|
591
|
-
fontWeight: '500',
|
|
592
|
-
},
|
|
593
|
-
stepContainer: {
|
|
594
|
-
width: '100%',
|
|
595
|
-
minHeight: 450,
|
|
596
|
-
paddingHorizontal: 20,
|
|
597
|
-
},
|
|
598
|
-
|
|
599
|
-
// Modern Image Container
|
|
600
703
|
modernImageContainer: {
|
|
601
704
|
alignItems: 'center',
|
|
602
705
|
marginBottom: 40,
|
|
603
|
-
paddingVertical: 20,
|
|
604
706
|
},
|
|
605
707
|
modernHeader: {
|
|
606
|
-
alignItems: '
|
|
607
|
-
|
|
708
|
+
alignItems: 'flex-start',
|
|
709
|
+
width: '100%',
|
|
710
|
+
marginBottom: 32,
|
|
608
711
|
},
|
|
609
712
|
modernTitle: {
|
|
610
713
|
fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold',
|
|
611
714
|
fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
|
|
612
|
-
fontSize:
|
|
613
|
-
|
|
614
|
-
marginBottom:
|
|
615
|
-
|
|
715
|
+
fontSize: 42,
|
|
716
|
+
lineHeight: 48,
|
|
717
|
+
marginBottom: 12,
|
|
718
|
+
textAlign: 'left',
|
|
719
|
+
letterSpacing: -1,
|
|
616
720
|
},
|
|
617
721
|
modernSubtitle: {
|
|
618
|
-
fontSize:
|
|
619
|
-
lineHeight:
|
|
620
|
-
textAlign: '
|
|
722
|
+
fontSize: 18,
|
|
723
|
+
lineHeight: 24,
|
|
724
|
+
textAlign: 'left',
|
|
621
725
|
opacity: 0.8,
|
|
622
726
|
},
|
|
623
|
-
|
|
624
|
-
// Modern Cards
|
|
625
727
|
modernInfoCard: {
|
|
626
728
|
flexDirection: 'row',
|
|
627
729
|
alignItems: 'center',
|
|
628
730
|
padding: 16,
|
|
629
|
-
|
|
630
|
-
|
|
731
|
+
borderRadius: 16,
|
|
732
|
+
marginBottom: 24,
|
|
631
733
|
gap: 12,
|
|
734
|
+
width: '100%',
|
|
632
735
|
},
|
|
633
736
|
modernInfoText: {
|
|
634
737
|
fontSize: 14,
|
|
635
|
-
lineHeight: 20,
|
|
636
738
|
flex: 1,
|
|
637
739
|
},
|
|
638
740
|
modernErrorCard: {
|
|
639
741
|
flexDirection: 'row',
|
|
640
742
|
alignItems: 'center',
|
|
641
743
|
padding: 16,
|
|
642
|
-
|
|
643
|
-
|
|
744
|
+
borderRadius: 16,
|
|
745
|
+
marginBottom: 24,
|
|
644
746
|
gap: 12,
|
|
747
|
+
width: '100%',
|
|
645
748
|
},
|
|
646
749
|
errorText: {
|
|
647
750
|
fontSize: 14,
|
|
648
|
-
|
|
751
|
+
fontWeight: '500',
|
|
649
752
|
flex: 1,
|
|
650
753
|
},
|
|
651
|
-
|
|
652
|
-
// Modern Input Styles
|
|
653
754
|
modernInputContainer: {
|
|
755
|
+
width: '100%',
|
|
654
756
|
marginBottom: 24,
|
|
655
757
|
},
|
|
656
758
|
inputWrapper: {
|
|
657
759
|
flexDirection: 'row',
|
|
658
760
|
alignItems: 'center',
|
|
659
|
-
|
|
761
|
+
height: 56,
|
|
660
762
|
borderRadius: 16,
|
|
661
|
-
paddingHorizontal:
|
|
662
|
-
|
|
663
|
-
backgroundColor:
|
|
763
|
+
paddingHorizontal: 20,
|
|
764
|
+
borderWidth: 2,
|
|
765
|
+
backgroundColor: colors.inputBackground,
|
|
664
766
|
},
|
|
665
767
|
inputIcon: {
|
|
666
768
|
marginRight: 12,
|
|
@@ -668,11 +770,34 @@ const styles = StyleSheet.create({
|
|
|
668
770
|
modernInput: {
|
|
669
771
|
flex: 1,
|
|
670
772
|
fontSize: 16,
|
|
671
|
-
|
|
773
|
+
height: '100%',
|
|
774
|
+
},
|
|
775
|
+
passwordToggle: {
|
|
776
|
+
padding: 4,
|
|
777
|
+
},
|
|
778
|
+
validationIndicator: {
|
|
779
|
+
marginLeft: 8,
|
|
780
|
+
},
|
|
781
|
+
validationSuccessCard: {
|
|
782
|
+
flexDirection: 'row',
|
|
783
|
+
alignItems: 'center',
|
|
784
|
+
padding: 12,
|
|
785
|
+
borderRadius: 12,
|
|
786
|
+
marginTop: 8,
|
|
787
|
+
gap: 8,
|
|
788
|
+
},
|
|
789
|
+
validationErrorCard: {
|
|
790
|
+
flexDirection: 'row',
|
|
791
|
+
alignItems: 'center',
|
|
792
|
+
padding: 12,
|
|
793
|
+
borderRadius: 12,
|
|
794
|
+
marginTop: 8,
|
|
795
|
+
gap: 8,
|
|
796
|
+
},
|
|
797
|
+
validationText: {
|
|
798
|
+
fontSize: 12,
|
|
672
799
|
fontWeight: '500',
|
|
673
800
|
},
|
|
674
|
-
|
|
675
|
-
// Modern Button Styles
|
|
676
801
|
modernButton: {
|
|
677
802
|
flexDirection: 'row',
|
|
678
803
|
alignItems: 'center',
|
|
@@ -689,6 +814,7 @@ const styles = StyleSheet.create({
|
|
|
689
814
|
shadowRadius: 8,
|
|
690
815
|
elevation: 6,
|
|
691
816
|
gap: 8,
|
|
817
|
+
width: '100%',
|
|
692
818
|
},
|
|
693
819
|
modernButtonText: {
|
|
694
820
|
color: '#FFFFFF',
|
|
@@ -705,6 +831,14 @@ const styles = StyleSheet.create({
|
|
|
705
831
|
fontWeight: '600',
|
|
706
832
|
textDecorationLine: 'underline',
|
|
707
833
|
},
|
|
834
|
+
footerTextContainer: {
|
|
835
|
+
flexDirection: 'row',
|
|
836
|
+
justifyContent: 'center',
|
|
837
|
+
marginTop: 28,
|
|
838
|
+
},
|
|
839
|
+
footerText: {
|
|
840
|
+
fontSize: 15,
|
|
841
|
+
},
|
|
708
842
|
|
|
709
843
|
// Modern User Profile Styles
|
|
710
844
|
modernUserProfileContainer: {
|
|
@@ -791,84 +925,6 @@ const styles = StyleSheet.create({
|
|
|
791
925
|
fontSize: 12,
|
|
792
926
|
fontWeight: '500',
|
|
793
927
|
},
|
|
794
|
-
|
|
795
|
-
// Legacy compatibility styles
|
|
796
|
-
progressContainer: {
|
|
797
|
-
flexDirection: 'row',
|
|
798
|
-
alignItems: 'center',
|
|
799
|
-
justifyContent: 'center',
|
|
800
|
-
marginBottom: 32,
|
|
801
|
-
paddingHorizontal: 40,
|
|
802
|
-
},
|
|
803
|
-
progressDot: {
|
|
804
|
-
width: 12,
|
|
805
|
-
height: 12,
|
|
806
|
-
borderRadius: 6,
|
|
807
|
-
marginHorizontal: 4,
|
|
808
|
-
},
|
|
809
|
-
progressLine: {
|
|
810
|
-
flex: 1,
|
|
811
|
-
height: 2,
|
|
812
|
-
marginHorizontal: 8,
|
|
813
|
-
},
|
|
814
|
-
welcomeImageContainer: {
|
|
815
|
-
alignItems: 'center',
|
|
816
|
-
marginBottom: 32,
|
|
817
|
-
},
|
|
818
|
-
header: {
|
|
819
|
-
alignItems: 'center',
|
|
820
|
-
marginBottom: 16,
|
|
821
|
-
},
|
|
822
|
-
welcomeTitle: {
|
|
823
|
-
fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold',
|
|
824
|
-
fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
|
|
825
|
-
fontSize: 32,
|
|
826
|
-
textAlign: 'center',
|
|
827
|
-
marginBottom: 8,
|
|
828
|
-
},
|
|
829
|
-
welcomeText: {
|
|
830
|
-
fontSize: 16,
|
|
831
|
-
lineHeight: 24,
|
|
832
|
-
textAlign: 'center',
|
|
833
|
-
marginBottom: 32,
|
|
834
|
-
paddingHorizontal: 20,
|
|
835
|
-
},
|
|
836
|
-
userProfileContainer: {
|
|
837
|
-
alignItems: 'center',
|
|
838
|
-
marginBottom: 32,
|
|
839
|
-
paddingVertical: 20,
|
|
840
|
-
},
|
|
841
|
-
userAvatar: {
|
|
842
|
-
marginBottom: 16,
|
|
843
|
-
},
|
|
844
|
-
userDisplayName: {
|
|
845
|
-
fontSize: 24,
|
|
846
|
-
fontWeight: 'bold',
|
|
847
|
-
marginBottom: 4,
|
|
848
|
-
textAlign: 'center',
|
|
849
|
-
},
|
|
850
|
-
usernameSubtext: {
|
|
851
|
-
fontSize: 16,
|
|
852
|
-
textAlign: 'center',
|
|
853
|
-
},
|
|
854
|
-
navigationButtons: {
|
|
855
|
-
flexDirection: 'row',
|
|
856
|
-
justifyContent: 'center',
|
|
857
|
-
marginTop: 24,
|
|
858
|
-
},
|
|
859
|
-
backButton: {
|
|
860
|
-
flexDirection: 'row',
|
|
861
|
-
alignItems: 'center',
|
|
862
|
-
paddingVertical: 12,
|
|
863
|
-
paddingHorizontal: 20,
|
|
864
|
-
borderRadius: 12,
|
|
865
|
-
borderWidth: 1,
|
|
866
|
-
},
|
|
867
|
-
backButtonText: {
|
|
868
|
-
fontSize: 16,
|
|
869
|
-
fontWeight: '500',
|
|
870
|
-
marginLeft: 8,
|
|
871
|
-
},
|
|
872
928
|
});
|
|
873
929
|
|
|
874
930
|
export default SignInScreen;
|