@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.
Files changed (154) hide show
  1. package/lib/commonjs/assets/icons/OxyServices.js +1 -1
  2. package/lib/commonjs/core/index.js +84 -2
  3. package/lib/commonjs/core/index.js.map +1 -1
  4. package/lib/commonjs/index.js +22 -22
  5. package/lib/commonjs/index.js.map +1 -1
  6. package/lib/commonjs/node/index.js +6 -6
  7. package/lib/commonjs/node/index.js.map +1 -1
  8. package/lib/commonjs/ui/components/Avatar.js +3 -3
  9. package/lib/commonjs/ui/components/Avatar.js.map +1 -1
  10. package/lib/commonjs/ui/components/FollowButton.js +3 -3
  11. package/lib/commonjs/ui/components/GroupedSection.js +1 -1
  12. package/lib/commonjs/ui/components/OxyLogo.js +1 -1
  13. package/lib/commonjs/ui/components/OxyProvider.js +13 -13
  14. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  15. package/lib/commonjs/ui/components/OxySignInButton.js +2 -2
  16. package/lib/commonjs/ui/components/ProfileCard.js +2 -2
  17. package/lib/commonjs/ui/components/Section.js +1 -1
  18. package/lib/commonjs/ui/components/SectionTitle.js +1 -1
  19. package/lib/commonjs/ui/components/icon/index.js +1 -1
  20. package/lib/commonjs/ui/components/index.js +12 -12
  21. package/lib/commonjs/ui/context/OxyContext.js +20 -4
  22. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  23. package/lib/commonjs/ui/index.js +11 -11
  24. package/lib/commonjs/ui/index.js.map +1 -1
  25. package/lib/commonjs/ui/navigation/OxyRouter.js +18 -18
  26. package/lib/commonjs/ui/screens/AccountCenterScreen.js +18 -18
  27. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  28. package/lib/commonjs/ui/screens/AccountManagementDemo.js +3 -3
  29. package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -1
  30. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +45 -27
  31. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  32. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +29 -22
  33. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  34. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +3 -3
  35. package/lib/commonjs/ui/screens/AppInfoScreen.js +6 -6
  36. package/lib/commonjs/ui/screens/BillingManagementScreen.js +3 -3
  37. package/lib/commonjs/ui/screens/FileManagementScreen.js +324 -306
  38. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +3 -3
  40. package/lib/commonjs/ui/screens/ProfileScreen.js +2 -2
  41. package/lib/commonjs/ui/screens/SessionManagementScreen.js +2 -2
  42. package/lib/commonjs/ui/screens/SignInScreen.js +358 -310
  43. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  44. package/lib/commonjs/ui/screens/SignUpScreen.js +483 -308
  45. package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
  46. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +3 -3
  47. package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js +51 -26
  48. package/lib/commonjs/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
  49. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +2 -2
  50. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +1 -1
  51. package/lib/commonjs/ui/styles/index.js +2 -2
  52. package/lib/commonjs/ui/styles/theme.js +1 -1
  53. package/lib/commonjs/utils/index.js +1 -1
  54. package/lib/module/assets/icons/OxyServices.js +1 -1
  55. package/lib/module/assets/icons/OxyServices.js.map +1 -1
  56. package/lib/module/core/index.js +84 -2
  57. package/lib/module/core/index.js.map +1 -1
  58. package/lib/module/index.js +10 -10
  59. package/lib/module/index.js.map +1 -1
  60. package/lib/module/node/index.js +4 -4
  61. package/lib/module/node/index.js.map +1 -1
  62. package/lib/module/ui/components/Avatar.js +2 -2
  63. package/lib/module/ui/components/Avatar.js.map +1 -1
  64. package/lib/module/ui/components/FollowButton.js +3 -3
  65. package/lib/module/ui/components/FollowButton.js.map +1 -1
  66. package/lib/module/ui/components/GroupedSection.js +1 -1
  67. package/lib/module/ui/components/GroupedSection.js.map +1 -1
  68. package/lib/module/ui/components/OxyLogo.js +1 -1
  69. package/lib/module/ui/components/OxyLogo.js.map +1 -1
  70. package/lib/module/ui/components/OxyProvider.js +10 -10
  71. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  72. package/lib/module/ui/components/OxySignInButton.js +2 -2
  73. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  74. package/lib/module/ui/components/ProfileCard.js +2 -2
  75. package/lib/module/ui/components/ProfileCard.js.map +1 -1
  76. package/lib/module/ui/components/Section.js +1 -1
  77. package/lib/module/ui/components/Section.js.map +1 -1
  78. package/lib/module/ui/components/SectionTitle.js +1 -1
  79. package/lib/module/ui/components/SectionTitle.js.map +1 -1
  80. package/lib/module/ui/components/icon/index.js +1 -1
  81. package/lib/module/ui/components/icon/index.js.map +1 -1
  82. package/lib/module/ui/components/index.js +12 -12
  83. package/lib/module/ui/components/index.js.map +1 -1
  84. package/lib/module/ui/context/OxyContext.js +20 -4
  85. package/lib/module/ui/context/OxyContext.js.map +1 -1
  86. package/lib/module/ui/index.js +10 -10
  87. package/lib/module/ui/index.js.map +1 -1
  88. package/lib/module/ui/navigation/OxyRouter.js +18 -18
  89. package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
  90. package/lib/module/ui/screens/AccountCenterScreen.js +5 -5
  91. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  92. package/lib/module/ui/screens/AccountManagementDemo.js +2 -2
  93. package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -1
  94. package/lib/module/ui/screens/AccountOverviewScreen.js +46 -28
  95. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  96. package/lib/module/ui/screens/AccountSettingsScreen.js +30 -23
  97. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  98. package/lib/module/ui/screens/AccountSwitcherScreen.js +3 -3
  99. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  100. package/lib/module/ui/screens/AppInfoScreen.js +6 -6
  101. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  102. package/lib/module/ui/screens/BillingManagementScreen.js +3 -3
  103. package/lib/module/ui/screens/BillingManagementScreen.js.map +1 -1
  104. package/lib/module/ui/screens/FileManagementScreen.js +325 -307
  105. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  106. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +3 -3
  107. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  108. package/lib/module/ui/screens/ProfileScreen.js +2 -2
  109. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  110. package/lib/module/ui/screens/SessionManagementScreen.js +2 -2
  111. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  112. package/lib/module/ui/screens/SignInScreen.js +358 -310
  113. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  114. package/lib/module/ui/screens/SignUpScreen.js +486 -309
  115. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  116. package/lib/module/ui/screens/karma/KarmaCenterScreen.js +3 -3
  117. package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  118. package/lib/module/ui/screens/karma/KarmaFAQScreen.js +52 -27
  119. package/lib/module/ui/screens/karma/KarmaFAQScreen.js.map +1 -1
  120. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +2 -2
  121. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  122. package/lib/module/ui/screens/karma/KarmaRulesScreen.js +1 -1
  123. package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  124. package/lib/module/ui/styles/index.js +2 -2
  125. package/lib/module/ui/styles/index.js.map +1 -1
  126. package/lib/module/ui/styles/theme.js +1 -1
  127. package/lib/module/ui/styles/theme.js.map +1 -1
  128. package/lib/module/utils/index.js +1 -1
  129. package/lib/module/utils/index.js.map +1 -1
  130. package/lib/typescript/core/index.d.ts +24 -0
  131. package/lib/typescript/core/index.d.ts.map +1 -1
  132. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  133. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  134. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts +2 -2
  135. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  136. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts +2 -2
  137. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  138. package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
  139. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  140. package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
  141. package/lib/typescript/ui/screens/karma/KarmaFAQScreen.d.ts +2 -2
  142. package/lib/typescript/ui/screens/karma/KarmaFAQScreen.d.ts.map +1 -1
  143. package/package.json +21 -5
  144. package/src/core/index.ts +68 -0
  145. package/src/ui/components/OxyProvider.tsx +5 -5
  146. package/src/ui/context/OxyContext.tsx +61 -41
  147. package/src/ui/screens/AccountOverviewScreen.tsx +44 -26
  148. package/src/ui/screens/AccountSettingsScreen.tsx +24 -18
  149. package/src/ui/screens/FileManagementScreen.tsx +246 -211
  150. package/src/ui/screens/SignInScreen.tsx +382 -326
  151. package/src/ui/screens/SignUpScreen.tsx +443 -273
  152. package/src/ui/screens/karma/KarmaFAQScreen.tsx +50 -29
  153. package/lib/commonjs/package.json +0 -1
  154. package/lib/module/package.json +0 -1
@@ -1,15 +1,13 @@
1
1
  "use strict";
2
2
 
3
- import React, { useState, useRef, useEffect } from 'react';
4
- import { View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator, Platform, Animated } from 'react-native';
5
- import { useOxy } from '../context/OxyContext';
6
- import { useThemeColors, createCommonStyles } from '../styles';
7
- import OxyLogo from '../components/OxyLogo';
8
- import Avatar from '../components/Avatar';
9
- import { BottomSheetScrollView } from '../components/bottomSheet';
3
+ import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
4
+ import { View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator, Platform, KeyboardAvoidingView, ScrollView, Animated, StatusBar } from 'react-native';
5
+ import { useOxy } from "../context/OxyContext.js";
6
+ import { useThemeColors, createCommonStyles } from "../styles/index.js";
7
+ import Avatar from "../components/Avatar.js";
10
8
  import { Ionicons } from '@expo/vector-icons';
11
9
  import Svg, { Path, Circle, Defs, LinearGradient, Stop } from 'react-native-svg';
12
- import { toast } from '../../lib/sonner';
10
+ import { toast } from "../../lib/sonner.js";
13
11
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
12
  const SignInScreen = ({
15
13
  navigate,
@@ -21,10 +19,16 @@ const SignInScreen = ({
21
19
  const [password, setPassword] = useState('');
22
20
  const [errorMessage, setErrorMessage] = useState('');
23
21
  const [userProfile, setUserProfile] = useState(null);
22
+ const [showPassword, setShowPassword] = useState(false);
24
23
 
25
24
  // Multi-step form states
26
25
  const [currentStep, setCurrentStep] = useState(0);
27
26
  const [isInputFocused, setIsInputFocused] = useState(false);
27
+ const [isValidating, setIsValidating] = useState(false);
28
+ const [validationStatus, setValidationStatus] = useState('idle');
29
+
30
+ // Cache for validation results to prevent repeated API calls
31
+ const validationCache = useRef(new Map());
28
32
  const fadeAnim = useRef(new Animated.Value(1)).current;
29
33
  const slideAnim = useRef(new Animated.Value(0)).current;
30
34
  const scaleAnim = useRef(new Animated.Value(1)).current;
@@ -36,13 +40,17 @@ const SignInScreen = ({
36
40
  isLoading,
37
41
  user,
38
42
  isAuthenticated,
39
- sessions
43
+ sessions,
44
+ oxyServices
40
45
  } = useOxy();
41
46
  const colors = useThemeColors(theme);
42
47
  const commonStyles = createCommonStyles(theme);
43
48
 
44
49
  // Check if this should be treated as "Add Account" mode
45
- const isAddAccountMode = user && isAuthenticated && sessions && sessions.length > 0;
50
+ const isAddAccountMode = useMemo(() => user && isAuthenticated && sessions && sessions.length > 0, [user, isAuthenticated, sessions]);
51
+
52
+ // Memoized styles to prevent rerenders
53
+ const styles = useMemo(() => createStyles(colors, theme), [colors, theme]);
46
54
 
47
55
  // Initialize logo animation
48
56
  useEffect(() => {
@@ -52,26 +60,153 @@ const SignInScreen = ({
52
60
  friction: 8,
53
61
  useNativeDriver: true
54
62
  }).start();
55
- }, []);
63
+ }, [logoAnim]);
56
64
 
57
65
  // Input focus animations
58
- const handleInputFocus = () => {
66
+ const handleInputFocus = useCallback(() => {
59
67
  setIsInputFocused(true);
60
68
  Animated.spring(inputScaleAnim, {
61
69
  toValue: 1.02,
62
70
  useNativeDriver: true
63
71
  }).start();
64
- };
65
- const handleInputBlur = () => {
72
+ }, [inputScaleAnim]);
73
+ const handleInputBlur = useCallback(() => {
66
74
  setIsInputFocused(false);
67
75
  Animated.spring(inputScaleAnim, {
68
76
  toValue: 1,
69
77
  useNativeDriver: true
70
78
  }).start();
71
- };
79
+ }, [inputScaleAnim]);
80
+
81
+ // Memoized input change handlers to prevent re-renders
82
+ const handleUsernameChange = useCallback(text => {
83
+ setUsername(text);
84
+ // Only clear error if we're changing from an invalid state
85
+ if (validationStatus === 'invalid') {
86
+ setErrorMessage('');
87
+ setValidationStatus('idle');
88
+ }
89
+ }, [validationStatus]);
90
+ const handlePasswordChange = useCallback(text => {
91
+ setPassword(text);
92
+ setErrorMessage(''); // Clear error when user types
93
+ }, []);
94
+
95
+ // Username validation using core services with caching
96
+ const validateUsername = useCallback(async usernameToValidate => {
97
+ if (!usernameToValidate || usernameToValidate.length < 3) {
98
+ setValidationStatus('invalid');
99
+ return false;
100
+ }
101
+
102
+ // Check cache first (cache valid for 5 minutes)
103
+ const cached = validationCache.current.get(usernameToValidate);
104
+ const now = Date.now();
105
+ if (cached && now - cached.timestamp < 5 * 60 * 1000) {
106
+ setUserProfile(cached.profile);
107
+ setValidationStatus('valid');
108
+ setErrorMessage('');
109
+ return true;
110
+ }
111
+ setIsValidating(true);
112
+ setValidationStatus('validating');
113
+ try {
114
+ // First check if username exists by trying to get profile
115
+ const profile = await oxyServices.getUserProfileByUsername(usernameToValidate);
116
+ if (profile) {
117
+ const profileData = {
118
+ displayName: profile.name?.full || profile.name?.first || profile.username,
119
+ name: profile.username,
120
+ avatar: profile.avatar,
121
+ id: profile.id
122
+ };
123
+ setUserProfile(profileData);
124
+ setValidationStatus('valid');
125
+ setErrorMessage(''); // Clear any previous errors
126
+
127
+ // Cache the result
128
+ validationCache.current.set(usernameToValidate, {
129
+ profile: profileData,
130
+ timestamp: now
131
+ });
132
+ return true;
133
+ } else {
134
+ setValidationStatus('invalid');
135
+ setErrorMessage('Username not found. Please check your username or sign up.');
136
+ return false;
137
+ }
138
+ } catch (error) {
139
+ // If user not found (404), username doesn't exist
140
+ if (error.status === 404 || error.code === 'USER_NOT_FOUND') {
141
+ setValidationStatus('invalid');
142
+ setErrorMessage('Username not found. Please check your username or sign up.');
143
+ return false;
144
+ }
145
+
146
+ // For other errors, show generic message
147
+ console.error('Username validation error:', error);
148
+ setValidationStatus('invalid');
149
+ setErrorMessage('Unable to validate username. Please try again.');
150
+ return false;
151
+ } finally {
152
+ setIsValidating(false);
153
+ }
154
+ }, [oxyServices]);
155
+
156
+ // Debounced username validation - increased debounce time and added better conditions
157
+ useEffect(() => {
158
+ if (!username || username.length < 3) {
159
+ setValidationStatus('idle');
160
+ setUserProfile(null);
161
+ setErrorMessage(''); // Clear error when input is too short
162
+ return;
163
+ }
164
+
165
+ // Only validate if we haven't already validated this exact username
166
+ if (validationStatus === 'valid' && userProfile?.name === username) {
167
+ return;
168
+ }
169
+ const timeoutId = setTimeout(() => {
170
+ validateUsername(username);
171
+ }, 800); // Increased debounce to 800ms
172
+
173
+ return () => clearTimeout(timeoutId);
174
+ }, [username, validateUsername, validationStatus, userProfile?.name]);
175
+
176
+ // Cleanup cache on unmount and limit cache size
177
+ useEffect(() => {
178
+ return () => {
179
+ // Clear cache on unmount
180
+ validationCache.current.clear();
181
+ };
182
+ }, []);
183
+
184
+ // Clean up old cache entries periodically (older than 10 minutes)
185
+ useEffect(() => {
186
+ const cleanupInterval = setInterval(() => {
187
+ const now = Date.now();
188
+ const maxAge = 10 * 60 * 1000; // 10 minutes
189
+
190
+ for (const [key, value] of validationCache.current.entries()) {
191
+ if (now - value.timestamp > maxAge) {
192
+ validationCache.current.delete(key);
193
+ }
194
+ }
195
+
196
+ // Limit cache size to 50 entries
197
+ if (validationCache.current.size > 50) {
198
+ const entries = Array.from(validationCache.current.entries());
199
+ entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
200
+ const toDelete = entries.slice(0, entries.length - 50);
201
+ toDelete.forEach(([key]) => validationCache.current.delete(key));
202
+ }
203
+ }, 5 * 60 * 1000); // Clean up every 5 minutes
204
+
205
+ return () => clearInterval(cleanupInterval);
206
+ }, []);
72
207
 
73
208
  // Animation functions
74
- const animateTransition = nextStep => {
209
+ const animateTransition = useCallback(nextStep => {
75
210
  // Scale down current content
76
211
  Animated.timing(scaleAnim, {
77
212
  toValue: 0.95,
@@ -108,8 +243,8 @@ const SignInScreen = ({
108
243
  useNativeDriver: true
109
244
  })]).start();
110
245
  });
111
- };
112
- const nextStep = () => {
246
+ }, [fadeAnim, slideAnim, scaleAnim]);
247
+ const nextStep = useCallback(() => {
113
248
  if (currentStep < 1) {
114
249
  // Animate progress bar
115
250
  Animated.timing(progressAnim, {
@@ -119,8 +254,8 @@ const SignInScreen = ({
119
254
  }).start();
120
255
  animateTransition(currentStep + 1);
121
256
  }
122
- };
123
- const prevStep = () => {
257
+ }, [currentStep, progressAnim, animateTransition]);
258
+ const prevStep = useCallback(() => {
124
259
  if (currentStep > 0) {
125
260
  // Animate progress bar
126
261
  Animated.timing(progressAnim, {
@@ -130,37 +265,31 @@ const SignInScreen = ({
130
265
  }).start();
131
266
  animateTransition(currentStep - 1);
132
267
  }
133
- };
134
-
135
- // Fetch user profile when username is entered
136
- useEffect(() => {
137
- const fetchUserProfile = async () => {
138
- if (username.length >= 3 && currentStep === 1) {
139
- try {
140
- // For now, we'll create a mock profile based on username
141
- // In a real app, you'd fetch this from your API
142
- setUserProfile({
143
- displayName: username,
144
- name: username,
145
- avatar: null // Could be fetched from API
146
- });
147
- } catch (error) {
148
- // If user not found, we'll show a generic avatar
149
- setUserProfile(null);
150
- }
151
- }
152
- };
153
- fetchUserProfile();
154
- }, [username, currentStep]);
155
- const handleUsernameNext = () => {
268
+ }, [currentStep, progressAnim, animateTransition]);
269
+ const handleUsernameNext = useCallback(() => {
156
270
  if (!username) {
157
271
  toast.error('Please enter your username');
158
272
  return;
159
273
  }
160
- setErrorMessage('');
161
- nextStep();
162
- };
163
- const handleLogin = async () => {
274
+ if (validationStatus === 'invalid') {
275
+ // Don't show toast if we already have an error message displayed
276
+ if (!errorMessage) {
277
+ toast.error('Please enter a valid username');
278
+ }
279
+ return;
280
+ }
281
+ if (validationStatus === 'validating') {
282
+ toast.error('Please wait while we validate your username');
283
+ return;
284
+ }
285
+ if (validationStatus === 'valid' && userProfile) {
286
+ setErrorMessage('');
287
+ nextStep();
288
+ } else {
289
+ toast.error('Please enter a valid username');
290
+ }
291
+ }, [username, validationStatus, userProfile, errorMessage, nextStep]);
292
+ const handleLogin = useCallback(async () => {
164
293
  if (!username || !password) {
165
294
  toast.error('Please enter both username and password');
166
295
  return;
@@ -172,10 +301,10 @@ const SignInScreen = ({
172
301
  } catch (error) {
173
302
  toast.error(error.message || 'Login failed');
174
303
  }
175
- };
304
+ }, [username, password, login]);
176
305
 
177
- // Step components
178
- const renderUsernameStep = () => /*#__PURE__*/_jsxs(Animated.View, {
306
+ // Memoized step components
307
+ const renderUsernameStep = useMemo(() => /*#__PURE__*/_jsxs(Animated.View, {
179
308
  style: [styles.stepContainer, {
180
309
  opacity: fadeAnim,
181
310
  transform: [{
@@ -326,15 +455,15 @@ const SignInScreen = ({
326
455
  }],
327
456
  children: errorMessage
328
457
  })]
329
- }) : null, /*#__PURE__*/_jsx(Animated.View, {
458
+ }) : null, /*#__PURE__*/_jsxs(Animated.View, {
330
459
  style: [styles.modernInputContainer, {
331
460
  transform: [{
332
461
  scale: inputScaleAnim
333
462
  }]
334
463
  }],
335
- children: /*#__PURE__*/_jsxs(View, {
464
+ children: [/*#__PURE__*/_jsxs(View, {
336
465
  style: [styles.inputWrapper, {
337
- borderColor: isInputFocused ? colors.primary : colors.border
466
+ borderColor: validationStatus === 'valid' ? colors.success : validationStatus === 'invalid' ? colors.error : isInputFocused ? colors.primary : colors.border
338
467
  }],
339
468
  children: [/*#__PURE__*/_jsx(Ionicons, {
340
469
  name: "person-outline",
@@ -348,31 +477,78 @@ const SignInScreen = ({
348
477
  placeholder: "Enter your username",
349
478
  placeholderTextColor: colors.placeholder,
350
479
  value: username,
351
- onChangeText: setUsername,
480
+ onChangeText: handleUsernameChange,
352
481
  onFocus: handleInputFocus,
353
482
  onBlur: handleInputBlur,
354
483
  autoCapitalize: "none",
355
484
  testID: "username-input"
485
+ }), validationStatus === 'validating' && /*#__PURE__*/_jsx(ActivityIndicator, {
486
+ size: "small",
487
+ color: colors.primary,
488
+ style: styles.validationIndicator
489
+ }), validationStatus === 'valid' && /*#__PURE__*/_jsx(Ionicons, {
490
+ name: "checkmark-circle",
491
+ size: 20,
492
+ color: colors.success,
493
+ style: styles.validationIndicator
494
+ }), validationStatus === 'invalid' && username.length >= 3 && /*#__PURE__*/_jsx(Ionicons, {
495
+ name: "close-circle",
496
+ size: 20,
497
+ color: colors.error,
498
+ style: styles.validationIndicator
356
499
  })]
357
- })
358
- }), /*#__PURE__*/_jsxs(TouchableOpacity, {
500
+ }), validationStatus === 'valid' && userProfile && /*#__PURE__*/_jsxs(View, {
501
+ style: [styles.validationSuccessCard, {
502
+ backgroundColor: colors.success + '15'
503
+ }],
504
+ children: [/*#__PURE__*/_jsx(Ionicons, {
505
+ name: "checkmark-circle",
506
+ size: 16,
507
+ color: colors.success
508
+ }), /*#__PURE__*/_jsxs(Text, {
509
+ style: [styles.validationText, {
510
+ color: colors.success
511
+ }],
512
+ children: ["Found user: ", userProfile.displayName]
513
+ })]
514
+ }), validationStatus === 'invalid' && username.length >= 3 && !errorMessage && /*#__PURE__*/_jsxs(View, {
515
+ style: [styles.validationErrorCard, {
516
+ backgroundColor: colors.error + '15'
517
+ }],
518
+ children: [/*#__PURE__*/_jsx(Ionicons, {
519
+ name: "alert-circle",
520
+ size: 16,
521
+ color: colors.error
522
+ }), /*#__PURE__*/_jsx(Text, {
523
+ style: [styles.validationText, {
524
+ color: colors.error
525
+ }],
526
+ children: "Username not found"
527
+ })]
528
+ })]
529
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
359
530
  style: [styles.modernButton, {
360
531
  backgroundColor: colors.primary,
361
- opacity: !username ? 0.5 : 1,
532
+ opacity: !username || validationStatus !== 'valid' ? 0.5 : 1,
362
533
  shadowColor: colors.primary
363
534
  }],
364
535
  onPress: handleUsernameNext,
365
- disabled: !username,
536
+ disabled: !username || validationStatus !== 'valid' || isValidating,
366
537
  testID: "username-next-button",
367
- children: [/*#__PURE__*/_jsx(Text, {
368
- style: styles.modernButtonText,
369
- children: "Continue"
370
- }), /*#__PURE__*/_jsx(Ionicons, {
371
- name: "arrow-forward",
372
- size: 20,
538
+ children: isValidating ? /*#__PURE__*/_jsx(ActivityIndicator, {
373
539
  color: "#FFFFFF",
374
- style: styles.buttonIcon
375
- })]
540
+ size: "small"
541
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
542
+ children: [/*#__PURE__*/_jsx(Text, {
543
+ style: styles.modernButtonText,
544
+ children: "Continue"
545
+ }), /*#__PURE__*/_jsx(Ionicons, {
546
+ name: "arrow-forward",
547
+ size: 20,
548
+ color: "#FFFFFF",
549
+ style: styles.buttonIcon
550
+ })]
551
+ })
376
552
  }), /*#__PURE__*/_jsxs(View, {
377
553
  style: styles.footerTextContainer,
378
554
  children: [/*#__PURE__*/_jsxs(Text, {
@@ -390,8 +566,8 @@ const SignInScreen = ({
390
566
  })
391
567
  })]
392
568
  })]
393
- });
394
- const renderPasswordStep = () => /*#__PURE__*/_jsxs(Animated.View, {
569
+ }), [fadeAnim, slideAnim, scaleAnim, colors, isAddAccountMode, user?.username, errorMessage, inputScaleAnim, isInputFocused, username, validationStatus, userProfile, isValidating, handleInputFocus, handleInputBlur, handleUsernameChange, handleUsernameNext, navigate, styles]);
570
+ const renderPasswordStep = useMemo(() => /*#__PURE__*/_jsxs(Animated.View, {
395
571
  style: [styles.stepContainer, {
396
572
  opacity: fadeAnim,
397
573
  transform: [{
@@ -480,21 +656,30 @@ const SignInScreen = ({
480
656
  placeholder: "Enter your password",
481
657
  placeholderTextColor: colors.placeholder,
482
658
  value: password,
483
- onChangeText: setPassword,
659
+ onChangeText: handlePasswordChange,
484
660
  onFocus: handleInputFocus,
485
661
  onBlur: handleInputBlur,
486
- secureTextEntry: true,
662
+ secureTextEntry: !showPassword,
663
+ autoCapitalize: "none",
487
664
  testID: "password-input"
665
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
666
+ style: styles.passwordToggle,
667
+ onPress: () => setShowPassword(!showPassword),
668
+ children: /*#__PURE__*/_jsx(Ionicons, {
669
+ name: showPassword ? "eye-off" : "eye",
670
+ size: 20,
671
+ color: colors.secondaryText
672
+ })
488
673
  })]
489
674
  })
490
675
  }), /*#__PURE__*/_jsx(TouchableOpacity, {
491
676
  style: [styles.modernButton, {
492
677
  backgroundColor: colors.primary,
493
- opacity: isLoading ? 0.8 : 1,
678
+ opacity: !password ? 0.5 : 1,
494
679
  shadowColor: colors.primary
495
680
  }],
496
681
  onPress: handleLogin,
497
- disabled: isLoading,
682
+ disabled: !password || isLoading,
498
683
  testID: "login-button",
499
684
  children: isLoading ? /*#__PURE__*/_jsx(ActivityIndicator, {
500
685
  color: "#FFFFFF",
@@ -502,9 +687,9 @@ const SignInScreen = ({
502
687
  }) : /*#__PURE__*/_jsxs(_Fragment, {
503
688
  children: [/*#__PURE__*/_jsx(Text, {
504
689
  style: styles.modernButtonText,
505
- children: isAddAccountMode ? 'Add Account' : 'Sign In'
690
+ children: "Sign In"
506
691
  }), /*#__PURE__*/_jsx(Ionicons, {
507
- name: "arrow-forward",
692
+ name: "log-in",
508
693
  size: 20,
509
694
  color: "#FFFFFF",
510
695
  style: styles.buttonIcon
@@ -518,8 +703,8 @@ const SignInScreen = ({
518
703
  }],
519
704
  onPress: prevStep,
520
705
  children: [/*#__PURE__*/_jsx(Ionicons, {
521
- name: "chevron-back",
522
- size: 20,
706
+ name: "arrow-back",
707
+ size: 18,
523
708
  color: colors.text
524
709
  }), /*#__PURE__*/_jsx(Text, {
525
710
  style: [styles.modernBackButtonText, {
@@ -532,216 +717,123 @@ const SignInScreen = ({
532
717
  style: styles.securityNotice,
533
718
  children: [/*#__PURE__*/_jsx(Ionicons, {
534
719
  name: "shield-checkmark",
535
- size: 16,
720
+ size: 14,
536
721
  color: colors.secondaryText
537
722
  }), /*#__PURE__*/_jsx(Text, {
538
723
  style: [styles.securityText, {
539
724
  color: colors.secondaryText
540
725
  }],
541
- children: "Your connection is secure and encrypted"
726
+ children: "Your data is encrypted and secure"
542
727
  })]
543
728
  })]
544
- });
545
- const renderCurrentStep = () => {
729
+ }), [fadeAnim, slideAnim, scaleAnim, colors, userProfile, username, theme, logoAnim, errorMessage, inputScaleAnim, isInputFocused, password, showPassword, handleInputFocus, handleInputBlur, handlePasswordChange, handleLogin, isLoading, prevStep, styles]);
730
+ const renderCurrentStep = useCallback(() => {
546
731
  switch (currentStep) {
547
732
  case 0:
548
- return renderUsernameStep();
733
+ return renderUsernameStep;
549
734
  case 1:
550
- return renderPasswordStep();
735
+ return renderPasswordStep;
551
736
  default:
552
- return renderUsernameStep();
737
+ return renderUsernameStep;
553
738
  }
554
- };
555
- return /*#__PURE__*/_jsxs(BottomSheetScrollView, {
556
- contentContainerStyle: commonStyles.scrollContainer,
557
- keyboardShouldPersistTaps: "handled",
558
- children: [/*#__PURE__*/_jsx(Animated.View, {
559
- style: [styles.logoContainer, {
560
- transform: [{
561
- scale: logoAnim
562
- }]
563
- }],
564
- children: /*#__PURE__*/_jsx(OxyLogo, {
565
- style: {
566
- marginBottom: 24
567
- },
568
- width: 50,
569
- height: 50
570
- })
571
- }), /*#__PURE__*/_jsxs(View, {
572
- style: styles.modernProgressContainer,
573
- children: [/*#__PURE__*/_jsx(View, {
574
- style: styles.progressTrack,
575
- children: /*#__PURE__*/_jsx(Animated.View, {
576
- style: [styles.progressFill, {
577
- backgroundColor: colors.primary,
578
- width: progressAnim.interpolate({
579
- inputRange: [0, 1],
580
- outputRange: ['50%', '100%']
581
- })
582
- }]
583
- })
584
- }), /*#__PURE__*/_jsxs(Text, {
585
- style: [styles.progressText, {
586
- color: colors.secondaryText
587
- }],
588
- children: ["Step ", currentStep + 1, " of 2"]
589
- })]
590
- }), renderCurrentStep()]
739
+ }, [currentStep, renderUsernameStep, renderPasswordStep]);
740
+ return /*#__PURE__*/_jsxs(KeyboardAvoidingView, {
741
+ style: [styles.container, {
742
+ backgroundColor: colors.background
743
+ }],
744
+ behavior: Platform.OS === 'ios' ? 'padding' : 'height',
745
+ children: [/*#__PURE__*/_jsx(StatusBar, {
746
+ barStyle: theme === 'dark' ? 'light-content' : 'dark-content',
747
+ backgroundColor: colors.background
748
+ }), /*#__PURE__*/_jsx(ScrollView, {
749
+ contentContainerStyle: styles.scrollContent,
750
+ showsVerticalScrollIndicator: false,
751
+ keyboardShouldPersistTaps: "handled",
752
+ children: renderCurrentStep()
753
+ })]
591
754
  });
592
755
  };
593
- const styles = StyleSheet.create({
594
- // Legacy styles (keeping for compatibility)
595
- title: {
596
- fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold',
597
- fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
598
- fontSize: 54,
599
- marginBottom: 24
600
- },
601
- formContainer: {
602
- width: '100%'
603
- },
604
- inputContainer: {
605
- marginBottom: 16
756
+
757
+ // Memoized styles creation
758
+ const createStyles = (colors, theme) => StyleSheet.create({
759
+ container: {
760
+ flex: 1
606
761
  },
607
- label: {
608
- fontSize: 14,
609
- fontWeight: '500',
610
- marginBottom: 8
762
+ scrollContent: {
763
+ flexGrow: 1,
764
+ paddingHorizontal: 24,
765
+ paddingTop: 40,
766
+ paddingBottom: 40
611
767
  },
612
- footerTextContainer: {
613
- flexDirection: 'row',
768
+ stepContainer: {
769
+ flex: 1,
614
770
  justifyContent: 'center',
615
- marginTop: 24
616
- },
617
- footerText: {
618
- fontSize: 14,
619
- lineHeight: 20
620
- },
621
- linkText: {
622
- fontSize: 14,
623
- lineHeight: 20,
624
- fontWeight: '600'
625
- },
626
- userInfoContainer: {
627
- padding: 20,
628
- marginVertical: 20,
629
- borderRadius: 35,
630
- alignItems: 'center'
631
- },
632
- userInfoText: {
633
- fontSize: 16,
634
- lineHeight: 24,
635
- textAlign: 'center'
636
- },
637
- actionButtonsContainer: {
638
- marginTop: 20
639
- },
640
- infoContainer: {
641
- padding: 16,
642
- marginVertical: 16,
643
- borderRadius: 8,
644
- alignItems: 'center'
645
- },
646
- infoText: {
647
- fontSize: 14,
648
- lineHeight: 20,
649
- textAlign: 'center'
650
- },
651
- // Modern UI Styles
652
- logoContainer: {
653
- alignItems: 'center',
654
- marginBottom: 16
655
- },
656
- modernProgressContainer: {
657
771
  alignItems: 'center',
658
- marginBottom: 40,
659
- paddingHorizontal: 20
660
- },
661
- progressTrack: {
662
- width: '100%',
663
- height: 4,
664
- backgroundColor: '#E5E5E5',
665
- borderRadius: 2,
666
- marginBottom: 8,
667
- overflow: 'hidden'
668
- },
669
- progressFill: {
670
- height: '100%',
671
- borderRadius: 2
772
+ minHeight: 600
672
773
  },
673
- progressText: {
674
- fontSize: 12,
675
- fontWeight: '500'
676
- },
677
- stepContainer: {
678
- width: '100%',
679
- minHeight: 450,
680
- paddingHorizontal: 20
681
- },
682
- // Modern Image Container
683
774
  modernImageContainer: {
684
775
  alignItems: 'center',
685
- marginBottom: 40,
686
- paddingVertical: 20
776
+ marginBottom: 40
687
777
  },
688
778
  modernHeader: {
689
- alignItems: 'center',
690
- marginBottom: 24
779
+ alignItems: 'flex-start',
780
+ width: '100%',
781
+ marginBottom: 32
691
782
  },
692
783
  modernTitle: {
693
784
  fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold',
694
785
  fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
695
- fontSize: 54,
696
- textAlign: 'center',
697
- marginBottom: 8,
698
- letterSpacing: -0.5
786
+ fontSize: 42,
787
+ lineHeight: 48,
788
+ marginBottom: 12,
789
+ textAlign: 'left',
790
+ letterSpacing: -1
699
791
  },
700
792
  modernSubtitle: {
701
- fontSize: 16,
702
- lineHeight: 22,
703
- textAlign: 'center',
793
+ fontSize: 18,
794
+ lineHeight: 24,
795
+ textAlign: 'left',
704
796
  opacity: 0.8
705
797
  },
706
- // Modern Cards
707
798
  modernInfoCard: {
708
799
  flexDirection: 'row',
709
800
  alignItems: 'center',
710
801
  padding: 16,
711
- marginVertical: 16,
712
- borderRadius: 12,
713
- gap: 12
802
+ borderRadius: 16,
803
+ marginBottom: 24,
804
+ gap: 12,
805
+ width: '100%'
714
806
  },
715
807
  modernInfoText: {
716
808
  fontSize: 14,
717
- lineHeight: 20,
718
809
  flex: 1
719
810
  },
720
811
  modernErrorCard: {
721
812
  flexDirection: 'row',
722
813
  alignItems: 'center',
723
814
  padding: 16,
724
- marginVertical: 16,
725
- borderRadius: 12,
726
- gap: 12
815
+ borderRadius: 16,
816
+ marginBottom: 24,
817
+ gap: 12,
818
+ width: '100%'
727
819
  },
728
820
  errorText: {
729
821
  fontSize: 14,
730
- lineHeight: 20,
822
+ fontWeight: '500',
731
823
  flex: 1
732
824
  },
733
- // Modern Input Styles
734
825
  modernInputContainer: {
826
+ width: '100%',
735
827
  marginBottom: 24
736
828
  },
737
829
  inputWrapper: {
738
830
  flexDirection: 'row',
739
831
  alignItems: 'center',
740
- borderWidth: 2,
832
+ height: 56,
741
833
  borderRadius: 16,
742
- paddingHorizontal: 16,
743
- paddingVertical: 4,
744
- backgroundColor: 'rgba(0,0,0,0.02)'
834
+ paddingHorizontal: 20,
835
+ borderWidth: 2,
836
+ backgroundColor: colors.inputBackground
745
837
  },
746
838
  inputIcon: {
747
839
  marginRight: 12
@@ -749,10 +841,34 @@ const styles = StyleSheet.create({
749
841
  modernInput: {
750
842
  flex: 1,
751
843
  fontSize: 16,
752
- paddingVertical: 16,
844
+ height: '100%'
845
+ },
846
+ passwordToggle: {
847
+ padding: 4
848
+ },
849
+ validationIndicator: {
850
+ marginLeft: 8
851
+ },
852
+ validationSuccessCard: {
853
+ flexDirection: 'row',
854
+ alignItems: 'center',
855
+ padding: 12,
856
+ borderRadius: 12,
857
+ marginTop: 8,
858
+ gap: 8
859
+ },
860
+ validationErrorCard: {
861
+ flexDirection: 'row',
862
+ alignItems: 'center',
863
+ padding: 12,
864
+ borderRadius: 12,
865
+ marginTop: 8,
866
+ gap: 8
867
+ },
868
+ validationText: {
869
+ fontSize: 12,
753
870
  fontWeight: '500'
754
871
  },
755
- // Modern Button Styles
756
872
  modernButton: {
757
873
  flexDirection: 'row',
758
874
  alignItems: 'center',
@@ -768,7 +884,8 @@ const styles = StyleSheet.create({
768
884
  shadowOpacity: 0.3,
769
885
  shadowRadius: 8,
770
886
  elevation: 6,
771
- gap: 8
887
+ gap: 8,
888
+ width: '100%'
772
889
  },
773
890
  modernButtonText: {
774
891
  color: '#FFFFFF',
@@ -785,6 +902,14 @@ const styles = StyleSheet.create({
785
902
  fontWeight: '600',
786
903
  textDecorationLine: 'underline'
787
904
  },
905
+ footerTextContainer: {
906
+ flexDirection: 'row',
907
+ justifyContent: 'center',
908
+ marginTop: 28
909
+ },
910
+ footerText: {
911
+ fontSize: 15
912
+ },
788
913
  // Modern User Profile Styles
789
914
  modernUserProfileContainer: {
790
915
  alignItems: 'center',
@@ -867,83 +992,6 @@ const styles = StyleSheet.create({
867
992
  securityText: {
868
993
  fontSize: 12,
869
994
  fontWeight: '500'
870
- },
871
- // Legacy compatibility styles
872
- progressContainer: {
873
- flexDirection: 'row',
874
- alignItems: 'center',
875
- justifyContent: 'center',
876
- marginBottom: 32,
877
- paddingHorizontal: 40
878
- },
879
- progressDot: {
880
- width: 12,
881
- height: 12,
882
- borderRadius: 6,
883
- marginHorizontal: 4
884
- },
885
- progressLine: {
886
- flex: 1,
887
- height: 2,
888
- marginHorizontal: 8
889
- },
890
- welcomeImageContainer: {
891
- alignItems: 'center',
892
- marginBottom: 32
893
- },
894
- header: {
895
- alignItems: 'center',
896
- marginBottom: 16
897
- },
898
- welcomeTitle: {
899
- fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold',
900
- fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
901
- fontSize: 32,
902
- textAlign: 'center',
903
- marginBottom: 8
904
- },
905
- welcomeText: {
906
- fontSize: 16,
907
- lineHeight: 24,
908
- textAlign: 'center',
909
- marginBottom: 32,
910
- paddingHorizontal: 20
911
- },
912
- userProfileContainer: {
913
- alignItems: 'center',
914
- marginBottom: 32,
915
- paddingVertical: 20
916
- },
917
- userAvatar: {
918
- marginBottom: 16
919
- },
920
- userDisplayName: {
921
- fontSize: 24,
922
- fontWeight: 'bold',
923
- marginBottom: 4,
924
- textAlign: 'center'
925
- },
926
- usernameSubtext: {
927
- fontSize: 16,
928
- textAlign: 'center'
929
- },
930
- navigationButtons: {
931
- flexDirection: 'row',
932
- justifyContent: 'center',
933
- marginTop: 24
934
- },
935
- backButton: {
936
- flexDirection: 'row',
937
- alignItems: 'center',
938
- paddingVertical: 12,
939
- paddingHorizontal: 20,
940
- borderRadius: 12,
941
- borderWidth: 1
942
- },
943
- backButtonText: {
944
- fontSize: 16,
945
- fontWeight: '500',
946
- marginLeft: 8
947
995
  }
948
996
  });
949
997
  export default SignInScreen;