@oxyhq/services 0.1.18 → 5.1.7

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 (183) hide show
  1. package/README.md +17 -138
  2. package/UI_COMPONENTS.md +142 -0
  3. package/lib/commonjs/assets/OxyLogo.svg +1 -0
  4. package/lib/commonjs/assets/assets/OxyLogo.svg +1 -0
  5. package/lib/commonjs/assets/assets/fonts/Phudu/Phudu-Black.ttf +0 -0
  6. package/lib/commonjs/assets/assets/fonts/Phudu/Phudu-Bold.ttf +0 -0
  7. package/lib/commonjs/assets/assets/fonts/Phudu/Phudu-ExtraBold.ttf +0 -0
  8. package/lib/commonjs/assets/assets/fonts/Phudu/Phudu-Light.ttf +0 -0
  9. package/lib/commonjs/assets/assets/fonts/Phudu/Phudu-Medium.ttf +0 -0
  10. package/lib/commonjs/assets/assets/fonts/Phudu/Phudu-Regular.ttf +0 -0
  11. package/lib/commonjs/assets/assets/fonts/Phudu/Phudu-SemiBold.ttf +0 -0
  12. package/lib/commonjs/assets/fonts/Phudu/Phudu-Black.ttf +0 -0
  13. package/lib/commonjs/assets/fonts/Phudu/Phudu-Bold.ttf +0 -0
  14. package/lib/commonjs/assets/fonts/Phudu/Phudu-ExtraBold.ttf +0 -0
  15. package/lib/commonjs/assets/fonts/Phudu/Phudu-Light.ttf +0 -0
  16. package/lib/commonjs/assets/fonts/Phudu/Phudu-Medium.ttf +0 -0
  17. package/lib/commonjs/assets/fonts/Phudu/Phudu-Regular.ttf +0 -0
  18. package/lib/commonjs/assets/fonts/Phudu/Phudu-SemiBold.ttf +0 -0
  19. package/lib/commonjs/core/index.js +783 -0
  20. package/lib/commonjs/core/index.js.map +1 -0
  21. package/lib/commonjs/index.js +128 -0
  22. package/lib/commonjs/index.js.map +1 -0
  23. package/lib/commonjs/models/interfaces.js +2 -0
  24. package/lib/commonjs/models/interfaces.js.map +1 -0
  25. package/lib/commonjs/package.json +1 -0
  26. package/lib/commonjs/ui/components/FontLoader.js +181 -0
  27. package/lib/commonjs/ui/components/FontLoader.js.map +1 -0
  28. package/lib/commonjs/ui/components/OxyLogo.js +56 -0
  29. package/lib/commonjs/ui/components/OxyLogo.js.map +1 -0
  30. package/lib/commonjs/ui/components/OxyProvider.js +464 -0
  31. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -0
  32. package/lib/commonjs/ui/components/OxySignInButton.js +178 -0
  33. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -0
  34. package/lib/commonjs/ui/components/bottomSheet/index.js +37 -0
  35. package/lib/commonjs/ui/components/bottomSheet/index.js.map +1 -0
  36. package/lib/commonjs/ui/context/OxyContext.js +296 -0
  37. package/lib/commonjs/ui/context/OxyContext.js.map +1 -0
  38. package/lib/commonjs/ui/index.js +136 -0
  39. package/lib/commonjs/ui/index.js.map +1 -0
  40. package/lib/commonjs/ui/navigation/OxyRouter.js +140 -0
  41. package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -0
  42. package/lib/commonjs/ui/navigation/types.js +6 -0
  43. package/lib/commonjs/ui/navigation/types.js.map +1 -0
  44. package/lib/commonjs/ui/screens/AccountCenterScreen.js +280 -0
  45. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -0
  46. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +578 -0
  47. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -0
  48. package/lib/commonjs/ui/screens/SignInScreen.js +230 -0
  49. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -0
  50. package/lib/commonjs/ui/screens/SignUpScreen.js +673 -0
  51. package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -0
  52. package/lib/commonjs/ui/styles/FONTS.md +126 -0
  53. package/lib/commonjs/ui/styles/fonts.js +84 -0
  54. package/lib/commonjs/ui/styles/fonts.js.map +1 -0
  55. package/lib/commonjs/ui/styles/index.js +28 -0
  56. package/lib/commonjs/ui/styles/index.js.map +1 -0
  57. package/lib/commonjs/ui/styles/theme.js +122 -0
  58. package/lib/commonjs/ui/styles/theme.js.map +1 -0
  59. package/lib/module/assets/OxyLogo.svg +1 -0
  60. package/lib/module/assets/assets/OxyLogo.svg +1 -0
  61. package/lib/module/assets/assets/fonts/Phudu/Phudu-Black.ttf +0 -0
  62. package/lib/module/assets/assets/fonts/Phudu/Phudu-Bold.ttf +0 -0
  63. package/lib/module/assets/assets/fonts/Phudu/Phudu-ExtraBold.ttf +0 -0
  64. package/lib/module/assets/assets/fonts/Phudu/Phudu-Light.ttf +0 -0
  65. package/lib/module/assets/assets/fonts/Phudu/Phudu-Medium.ttf +0 -0
  66. package/lib/module/assets/assets/fonts/Phudu/Phudu-Regular.ttf +0 -0
  67. package/lib/module/assets/assets/fonts/Phudu/Phudu-SemiBold.ttf +0 -0
  68. package/lib/module/assets/fonts/Phudu/Phudu-Black.ttf +0 -0
  69. package/lib/module/assets/fonts/Phudu/Phudu-Bold.ttf +0 -0
  70. package/lib/module/assets/fonts/Phudu/Phudu-ExtraBold.ttf +0 -0
  71. package/lib/module/assets/fonts/Phudu/Phudu-Light.ttf +0 -0
  72. package/lib/module/assets/fonts/Phudu/Phudu-Medium.ttf +0 -0
  73. package/lib/module/assets/fonts/Phudu/Phudu-Regular.ttf +0 -0
  74. package/lib/module/assets/fonts/Phudu/Phudu-SemiBold.ttf +0 -0
  75. package/lib/module/core/index.js +777 -0
  76. package/lib/module/core/index.js.map +1 -0
  77. package/lib/module/index.js +29 -0
  78. package/lib/module/index.js.map +1 -0
  79. package/lib/module/models/interfaces.js +2 -0
  80. package/lib/module/models/interfaces.js.map +1 -0
  81. package/lib/module/package.json +1 -0
  82. package/lib/module/ui/components/FontLoader.js +176 -0
  83. package/lib/module/ui/components/FontLoader.js.map +1 -0
  84. package/lib/module/ui/components/OxyLogo.js +49 -0
  85. package/lib/module/ui/components/OxyLogo.js.map +1 -0
  86. package/lib/module/ui/components/OxyProvider.js +458 -0
  87. package/lib/module/ui/components/OxyProvider.js.map +1 -0
  88. package/lib/module/ui/components/OxySignInButton.js +172 -0
  89. package/lib/module/ui/components/OxySignInButton.js.map +1 -0
  90. package/lib/module/ui/components/bottomSheet/index.js +8 -0
  91. package/lib/module/ui/components/bottomSheet/index.js.map +1 -0
  92. package/lib/module/ui/context/OxyContext.js +291 -0
  93. package/lib/module/ui/context/OxyContext.js.map +1 -0
  94. package/lib/module/ui/index.js +25 -0
  95. package/lib/module/ui/index.js.map +1 -0
  96. package/lib/module/ui/navigation/OxyRouter.js +133 -0
  97. package/lib/module/ui/navigation/OxyRouter.js.map +1 -0
  98. package/lib/module/ui/navigation/types.js +4 -0
  99. package/lib/module/ui/navigation/types.js.map +1 -0
  100. package/lib/module/ui/screens/AccountCenterScreen.js +275 -0
  101. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -0
  102. package/lib/module/ui/screens/AccountOverviewScreen.js +573 -0
  103. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -0
  104. package/lib/module/ui/screens/SignInScreen.js +224 -0
  105. package/lib/module/ui/screens/SignInScreen.js.map +1 -0
  106. package/lib/module/ui/screens/SignUpScreen.js +668 -0
  107. package/lib/module/ui/screens/SignUpScreen.js.map +1 -0
  108. package/lib/module/ui/styles/FONTS.md +126 -0
  109. package/lib/module/ui/styles/fonts.js +81 -0
  110. package/lib/module/ui/styles/fonts.js.map +1 -0
  111. package/lib/module/ui/styles/index.js +5 -0
  112. package/lib/module/ui/styles/index.js.map +1 -0
  113. package/lib/module/ui/styles/theme.js +115 -0
  114. package/lib/module/ui/styles/theme.js.map +1 -0
  115. package/lib/typescript/core/index.d.ts +304 -0
  116. package/lib/typescript/core/index.d.ts.map +1 -0
  117. package/lib/typescript/index.d.ts +11 -0
  118. package/lib/typescript/index.d.ts.map +1 -0
  119. package/lib/typescript/models/interfaces.d.ts +98 -0
  120. package/lib/typescript/models/interfaces.d.ts.map +1 -0
  121. package/lib/typescript/ui/components/FontLoader.d.ts +15 -0
  122. package/lib/typescript/ui/components/FontLoader.d.ts.map +1 -0
  123. package/lib/typescript/ui/components/OxyLogo.d.ts +29 -0
  124. package/lib/typescript/ui/components/OxyLogo.d.ts.map +1 -0
  125. package/lib/typescript/ui/components/OxyProvider.d.ts +12 -0
  126. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -0
  127. package/lib/typescript/ui/components/OxySignInButton.d.ts +65 -0
  128. package/lib/typescript/ui/components/OxySignInButton.d.ts.map +1 -0
  129. package/lib/typescript/ui/components/bottomSheet/index.d.ts +3 -0
  130. package/lib/typescript/ui/components/bottomSheet/index.d.ts.map +1 -0
  131. package/lib/typescript/ui/context/OxyContext.d.ts +26 -0
  132. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -0
  133. package/lib/typescript/ui/index.d.ts +15 -0
  134. package/lib/typescript/ui/index.d.ts.map +1 -0
  135. package/lib/typescript/ui/navigation/OxyRouter.d.ts +5 -0
  136. package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -0
  137. package/lib/typescript/ui/navigation/types.d.ts +107 -0
  138. package/lib/typescript/ui/navigation/types.d.ts.map +1 -0
  139. package/lib/typescript/ui/screens/AccountCenterScreen.d.ts +5 -0
  140. package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -0
  141. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts +5 -0
  142. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -0
  143. package/lib/typescript/ui/screens/SignInScreen.d.ts +5 -0
  144. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -0
  145. package/lib/typescript/ui/screens/SignUpScreen.d.ts +5 -0
  146. package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -0
  147. package/lib/typescript/ui/styles/fonts.d.ts +21 -0
  148. package/lib/typescript/ui/styles/fonts.d.ts.map +1 -0
  149. package/lib/typescript/ui/styles/index.d.ts +3 -0
  150. package/lib/typescript/ui/styles/index.d.ts.map +1 -0
  151. package/lib/typescript/ui/styles/theme.d.ts +69 -0
  152. package/lib/typescript/ui/styles/theme.d.ts.map +1 -0
  153. package/package.json +95 -51
  154. package/src/assets/OxyLogo.svg +1 -0
  155. package/src/assets/fonts/Phudu/Phudu-Black.ttf +0 -0
  156. package/src/assets/fonts/Phudu/Phudu-Bold.ttf +0 -0
  157. package/src/assets/fonts/Phudu/Phudu-ExtraBold.ttf +0 -0
  158. package/src/assets/fonts/Phudu/Phudu-Light.ttf +0 -0
  159. package/src/assets/fonts/Phudu/Phudu-Medium.ttf +0 -0
  160. package/src/assets/fonts/Phudu/Phudu-Regular.ttf +0 -0
  161. package/src/assets/fonts/Phudu/Phudu-SemiBold.ttf +0 -0
  162. package/src/core/index.ts +768 -0
  163. package/src/index.ts +46 -0
  164. package/src/models/interfaces.ts +128 -0
  165. package/src/ui/components/FontLoader.tsx +180 -0
  166. package/src/ui/components/OxyLogo.tsx +73 -0
  167. package/src/ui/components/OxyProvider.tsx +490 -0
  168. package/src/ui/components/OxySignInButton.tsx +207 -0
  169. package/src/ui/components/bottomSheet/index.tsx +12 -0
  170. package/src/ui/context/OxyContext.tsx +344 -0
  171. package/src/ui/index.ts +27 -0
  172. package/src/ui/navigation/OxyRouter.tsx +137 -0
  173. package/src/ui/navigation/types.ts +124 -0
  174. package/src/ui/screens/AccountCenterScreen.tsx +240 -0
  175. package/src/ui/screens/AccountOverviewScreen.tsx +519 -0
  176. package/src/ui/screens/SignInScreen.tsx +210 -0
  177. package/src/ui/screens/SignUpScreen.tsx +607 -0
  178. package/src/ui/styles/FONTS.md +126 -0
  179. package/src/ui/styles/fonts.ts +77 -0
  180. package/src/ui/styles/index.ts +2 -0
  181. package/src/ui/styles/theme.ts +142 -0
  182. package/dist/index.d.ts +0 -152
  183. package/dist/index.js +0 -1
@@ -0,0 +1,607 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ TextInput,
6
+ TouchableOpacity,
7
+ StyleSheet,
8
+ ActivityIndicator,
9
+ Platform,
10
+ KeyboardAvoidingView,
11
+ ScrollView,
12
+ TextStyle,
13
+ Animated,
14
+ Dimensions,
15
+ } from 'react-native';
16
+ import { BaseScreenProps } from '../navigation/types';
17
+ import { useOxy } from '../context/OxyContext';
18
+ import { fontFamilies } from '../styles/fonts';
19
+
20
+ const SignUpScreen: React.FC<BaseScreenProps> = ({
21
+ navigate,
22
+ goBack,
23
+ theme,
24
+ }) => {
25
+ // Form data states
26
+ const [username, setUsername] = useState('');
27
+ const [email, setEmail] = useState('');
28
+ const [password, setPassword] = useState('');
29
+ const [confirmPassword, setConfirmPassword] = useState('');
30
+ const [errorMessage, setErrorMessage] = useState('');
31
+
32
+ // Multi-step form states
33
+ const [currentStep, setCurrentStep] = useState(0);
34
+ const fadeAnim = useRef(new Animated.Value(1)).current;
35
+ const slideAnim = useRef(new Animated.Value(0)).current;
36
+ const heightAnim = useRef(new Animated.Value(400)).current; // Initial height value
37
+ const [containerHeight, setContainerHeight] = useState(400); // Default height
38
+
39
+ const { signUp, isLoading, user, isAuthenticated } = useOxy();
40
+
41
+ const isDarkTheme = theme === 'dark';
42
+ const textColor = isDarkTheme ? '#FFFFFF' : '#000000';
43
+ const backgroundColor = isDarkTheme ? '#121212' : '#FFFFFF';
44
+ const inputBackgroundColor = isDarkTheme ? '#333333' : '#F5F5F5';
45
+ const placeholderColor = isDarkTheme ? '#AAAAAA' : '#999999';
46
+ const primaryColor = '#d169e5';
47
+ const borderColor = isDarkTheme ? '#444444' : '#E0E0E0';
48
+
49
+ // If user is already authenticated, show user info and account center option
50
+ // Animation functions
51
+ const animateTransition = (nextStep: number) => {
52
+ // Fade out
53
+ Animated.timing(fadeAnim, {
54
+ toValue: 0,
55
+ duration: 250,
56
+ useNativeDriver: true,
57
+ }).start(() => {
58
+ setCurrentStep(nextStep);
59
+
60
+ // Reset slide position
61
+ slideAnim.setValue(-100);
62
+
63
+ // Fade in and slide
64
+ Animated.parallel([
65
+ Animated.timing(fadeAnim, {
66
+ toValue: 1,
67
+ duration: 250,
68
+ useNativeDriver: true,
69
+ }),
70
+ Animated.timing(slideAnim, {
71
+ toValue: 0,
72
+ duration: 300,
73
+ useNativeDriver: true,
74
+ })
75
+ ]).start();
76
+ });
77
+ };
78
+
79
+ const nextStep = () => {
80
+ if (currentStep < 3) {
81
+ animateTransition(currentStep + 1);
82
+ }
83
+ };
84
+
85
+ const prevStep = () => {
86
+ if (currentStep > 0) {
87
+ animateTransition(currentStep - 1);
88
+ }
89
+ };
90
+
91
+ if (user && isAuthenticated) {
92
+ return (
93
+ <View style={[styles.container, { backgroundColor, padding: 20 }]}>
94
+ <Text style={[
95
+ styles.title,
96
+ {
97
+ color: textColor,
98
+ textAlign: 'center'
99
+ }
100
+ ]}>Welcome, {user.username}!</Text>
101
+
102
+ <View style={styles.userInfoContainer}>
103
+ <Text style={[styles.userInfoText, { color: textColor }]}>
104
+ You are already signed in.
105
+ </Text>
106
+ {user.email && (
107
+ <Text style={[styles.userInfoText, { color: isDarkTheme ? '#BBBBBB' : '#666666' }]}>
108
+ Email: {user.email}
109
+ </Text>
110
+ )}
111
+ </View>
112
+
113
+ <View style={styles.actionButtonsContainer}>
114
+ <TouchableOpacity
115
+ style={[styles.button, { backgroundColor: primaryColor }]}
116
+ onPress={() => navigate('AccountCenter')}
117
+ >
118
+ <Text style={styles.buttonText}>Go to Account Center</Text>
119
+ </TouchableOpacity>
120
+ </View>
121
+ </View>
122
+ );
123
+ }
124
+
125
+ const validateEmail = (email: string) => {
126
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
127
+ return emailRegex.test(email);
128
+ };
129
+
130
+ const handleSignUp = async () => {
131
+ // Validate inputs
132
+ if (!username || !email || !password || !confirmPassword) {
133
+ setErrorMessage('Please fill in all fields');
134
+ return;
135
+ }
136
+
137
+ if (!validateEmail(email)) {
138
+ setErrorMessage('Please enter a valid email address');
139
+ return;
140
+ }
141
+
142
+ if (password !== confirmPassword) {
143
+ setErrorMessage('Passwords do not match');
144
+ return;
145
+ }
146
+
147
+ if (password.length < 8) {
148
+ setErrorMessage('Password must be at least 8 characters long');
149
+ return;
150
+ }
151
+
152
+ try {
153
+ setErrorMessage('');
154
+ await signUp(username, email, password);
155
+ // The authentication state change will be handled through context
156
+ } catch (error: any) {
157
+ setErrorMessage(error.message || 'Sign up failed');
158
+ }
159
+ };
160
+
161
+ // Step components
162
+ const renderWelcomeStep = () => (
163
+ <Animated.View style={[
164
+ styles.stepContainer,
165
+ { opacity: fadeAnim, transform: [{ translateX: slideAnim }] }
166
+ ]}>
167
+ <Text style={[styles.welcomeTitle, { color: textColor }]}>
168
+ Welcome to OxyHQ
169
+ </Text>
170
+
171
+ <View style={styles.welcomeImageContainer}>
172
+ {/* Placeholder for any welcome image or icon */}
173
+ <View style={[styles.welcomeImagePlaceholder, { backgroundColor: isDarkTheme ? '#333' : '#f0f0f0' }]} />
174
+ </View>
175
+
176
+ <Text style={[styles.welcomeText, { color: textColor }]}>
177
+ We're excited to have you join us. Let's get your account set up in just a few easy steps.
178
+ </Text>
179
+
180
+ <TouchableOpacity
181
+ style={[styles.button, { backgroundColor: primaryColor }]}
182
+ onPress={nextStep}
183
+ testID="welcome-next-button"
184
+ >
185
+ <Text style={styles.buttonText}>Get Started</Text>
186
+ </TouchableOpacity>
187
+
188
+ <View style={styles.footerTextContainer}>
189
+ <Text style={[styles.footerText, { color: textColor }]}>
190
+ Already have an account?{' '}
191
+ </Text>
192
+ <TouchableOpacity onPress={() => navigate('SignIn')}>
193
+ <Text style={[styles.linkText, { color: primaryColor }]}>Sign In</Text>
194
+ </TouchableOpacity>
195
+ </View>
196
+ </Animated.View>
197
+ );
198
+
199
+ const renderIdentityStep = () => (
200
+ <Animated.View style={[
201
+ styles.stepContainer,
202
+ { opacity: fadeAnim, transform: [{ translateX: slideAnim }] }
203
+ ]}>
204
+ <Text style={[styles.stepTitle, { color: textColor }]}>Who are you?</Text>
205
+
206
+ <View style={styles.inputContainer}>
207
+ <Text style={[styles.label, { color: textColor }]}>Username</Text>
208
+ <TextInput
209
+ style={[
210
+ styles.input,
211
+ { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
212
+ ]}
213
+ placeholder="Choose a username"
214
+ placeholderTextColor={placeholderColor}
215
+ value={username}
216
+ onChangeText={setUsername}
217
+ autoCapitalize="none"
218
+ testID="username-input"
219
+ />
220
+ </View>
221
+
222
+ <View style={styles.inputContainer}>
223
+ <Text style={[styles.label, { color: textColor }]}>Email</Text>
224
+ <TextInput
225
+ style={[
226
+ styles.input,
227
+ { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
228
+ ]}
229
+ placeholder="Enter your email"
230
+ placeholderTextColor={placeholderColor}
231
+ value={email}
232
+ onChangeText={setEmail}
233
+ autoCapitalize="none"
234
+ keyboardType="email-address"
235
+ testID="email-input"
236
+ />
237
+ </View>
238
+
239
+ <View style={styles.navigationButtons}>
240
+ <TouchableOpacity
241
+ style={[styles.navButton, styles.backButton]}
242
+ onPress={prevStep}
243
+ >
244
+ <Text style={[styles.navButtonText, { color: textColor }]}>Back</Text>
245
+ </TouchableOpacity>
246
+
247
+ <TouchableOpacity
248
+ style={[styles.navButton, styles.nextButton, { backgroundColor: primaryColor }]}
249
+ onPress={nextStep}
250
+ disabled={!username || !email || !validateEmail(email)}
251
+ >
252
+ <Text style={styles.navButtonText}>Next</Text>
253
+ </TouchableOpacity>
254
+ </View>
255
+ </Animated.View>
256
+ );
257
+
258
+ const renderSecurityStep = () => (
259
+ <Animated.View style={[
260
+ styles.stepContainer,
261
+ { opacity: fadeAnim, transform: [{ translateX: slideAnim }] }
262
+ ]}>
263
+ <Text style={[styles.stepTitle, { color: textColor }]}>Secure your account</Text>
264
+
265
+ <View style={styles.inputContainer}>
266
+ <Text style={[styles.label, { color: textColor }]}>Password</Text>
267
+ <TextInput
268
+ style={[
269
+ styles.input,
270
+ { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
271
+ ]}
272
+ placeholder="Create a password"
273
+ placeholderTextColor={placeholderColor}
274
+ value={password}
275
+ onChangeText={setPassword}
276
+ secureTextEntry
277
+ testID="password-input"
278
+ />
279
+ <Text style={[styles.passwordHint, { color: isDarkTheme ? '#AAAAAA' : '#666666' }]}>
280
+ Password must be at least 8 characters long
281
+ </Text>
282
+ </View>
283
+
284
+ <View style={styles.inputContainer}>
285
+ <Text style={[styles.label, { color: textColor }]}>Confirm Password</Text>
286
+ <TextInput
287
+ style={[
288
+ styles.input,
289
+ { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
290
+ ]}
291
+ placeholder="Confirm your password"
292
+ placeholderTextColor={placeholderColor}
293
+ value={confirmPassword}
294
+ onChangeText={setConfirmPassword}
295
+ secureTextEntry
296
+ testID="confirm-password-input"
297
+ />
298
+ </View>
299
+
300
+ <View style={styles.navigationButtons}>
301
+ <TouchableOpacity
302
+ style={[styles.navButton, styles.backButton]}
303
+ onPress={prevStep}
304
+ >
305
+ <Text style={[styles.navButtonText, { color: textColor }]}>Back</Text>
306
+ </TouchableOpacity>
307
+
308
+ <TouchableOpacity
309
+ style={[styles.navButton, styles.nextButton, { backgroundColor: primaryColor }]}
310
+ onPress={nextStep}
311
+ disabled={!password || password.length < 8 || password !== confirmPassword}
312
+ >
313
+ <Text style={styles.navButtonText}>Next</Text>
314
+ </TouchableOpacity>
315
+ </View>
316
+ </Animated.View>
317
+ );
318
+
319
+ const renderSummaryStep = () => (
320
+ <Animated.View style={[
321
+ styles.stepContainer,
322
+ { opacity: fadeAnim, transform: [{ translateX: slideAnim }] }
323
+ ]}>
324
+ <Text style={[styles.stepTitle, { color: textColor }]}>Ready to join</Text>
325
+
326
+ <View style={styles.summaryContainer}>
327
+ <View style={styles.summaryRow}>
328
+ <Text style={[styles.summaryLabel, { color: isDarkTheme ? '#AAAAAA' : '#666666' }]}>Username:</Text>
329
+ <Text style={[styles.summaryValue, { color: textColor }]}>{username}</Text>
330
+ </View>
331
+
332
+ <View style={styles.summaryRow}>
333
+ <Text style={[styles.summaryLabel, { color: isDarkTheme ? '#AAAAAA' : '#666666' }]}>Email:</Text>
334
+ <Text style={[styles.summaryValue, { color: textColor }]}>{email}</Text>
335
+ </View>
336
+ </View>
337
+
338
+ {errorMessage ? (
339
+ <View style={styles.errorContainer}>
340
+ <Text style={styles.errorText}>{errorMessage}</Text>
341
+ </View>
342
+ ) : null}
343
+
344
+ <View style={styles.navigationButtons}>
345
+ <TouchableOpacity
346
+ style={[styles.navButton, styles.backButton]}
347
+ onPress={prevStep}
348
+ >
349
+ <Text style={[styles.navButtonText, { color: textColor }]}>Back</Text>
350
+ </TouchableOpacity>
351
+
352
+ <TouchableOpacity
353
+ style={[styles.button, { opacity: isLoading ? 0.8 : 1 }]}
354
+ onPress={handleSignUp}
355
+ disabled={isLoading}
356
+ testID="signup-button"
357
+ >
358
+ {isLoading ? (
359
+ <ActivityIndicator color="#FFFFFF" size="small" />
360
+ ) : (
361
+ <Text style={styles.buttonText}>Create Account</Text>
362
+ )}
363
+ </TouchableOpacity>
364
+ </View>
365
+ </Animated.View>
366
+ );
367
+
368
+ // Progress indicators
369
+ const renderProgressIndicators = () => (
370
+ <View style={styles.progressContainer}>
371
+ {[0, 1, 2, 3].map((step) => (
372
+ <View
373
+ key={step}
374
+ style={[
375
+ styles.progressDot,
376
+ currentStep === step ?
377
+ { backgroundColor: primaryColor, width: 24 } :
378
+ { backgroundColor: isDarkTheme ? '#444444' : '#E0E0E0' }
379
+ ]}
380
+ />
381
+ ))}
382
+ </View>
383
+ );
384
+
385
+ // Render step based on current step value
386
+ const renderCurrentStep = () => {
387
+ switch (currentStep) {
388
+ case 0:
389
+ return renderWelcomeStep();
390
+ case 1:
391
+ return renderIdentityStep();
392
+ case 2:
393
+ return renderSecurityStep();
394
+ case 3:
395
+ return renderSummaryStep();
396
+ default:
397
+ return null;
398
+ }
399
+ };
400
+
401
+ return (
402
+ <KeyboardAvoidingView
403
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
404
+ style={[styles.container, { backgroundColor }]}
405
+ >
406
+ <ScrollView
407
+ contentContainerStyle={styles.scrollContainer}
408
+ keyboardShouldPersistTaps="handled"
409
+ >
410
+ <View style={styles.header}>
411
+ <Text style={[styles.title]}>Create Account</Text>
412
+ <View style={styles.placeholder} />
413
+ </View>
414
+
415
+ {currentStep > 0 && renderProgressIndicators()}
416
+
417
+ <View style={styles.formContainer}>
418
+ {renderCurrentStep()}
419
+ </View>
420
+ </ScrollView>
421
+ </KeyboardAvoidingView>
422
+ );
423
+ };
424
+
425
+ const styles = StyleSheet.create({
426
+ container: {
427
+ flex: 1,
428
+ },
429
+ scrollContainer: {
430
+ flexGrow: 1,
431
+ padding: 10,
432
+ },
433
+ header: {
434
+ flexDirection: 'row',
435
+ justifyContent: 'space-between',
436
+ marginBottom: 24,
437
+ },
438
+ title: {
439
+ fontFamily: Platform.OS === 'web'
440
+ ? 'Phudu' // Use CSS font name directly for web
441
+ : 'Phudu-Bold', // Use exact font name as registered with Font.loadAsync
442
+ fontWeight: Platform.OS === 'web' ? 'bold' : undefined, // Only apply fontWeight on web
443
+ fontSize: 54,
444
+ },
445
+ placeholder: {
446
+ width: 50, // To balance the header
447
+ },
448
+ formContainer: {
449
+ width: '100%',
450
+ },
451
+ stepContainer: {
452
+ width: '100%',
453
+ paddingVertical: 20,
454
+ },
455
+ inputContainer: {
456
+ marginBottom: 16,
457
+ },
458
+ label: {
459
+ fontSize: 14,
460
+ marginBottom: 8,
461
+ },
462
+ input: {
463
+ height: 48,
464
+ borderRadius: 35,
465
+ paddingHorizontal: 16,
466
+ borderWidth: 1,
467
+ fontSize: 16,
468
+ },
469
+ button: {
470
+ backgroundColor: '#d169e5',
471
+ height: 48,
472
+ borderRadius: 35,
473
+ alignItems: 'center',
474
+ justifyContent: 'center',
475
+ marginTop: 24,
476
+ },
477
+ buttonText: {
478
+ color: '#FFFFFF',
479
+ fontSize: 16,
480
+ fontWeight: '600',
481
+ },
482
+ footerTextContainer: {
483
+ flexDirection: 'row',
484
+ justifyContent: 'center',
485
+ marginTop: 24,
486
+ },
487
+ footerText: {
488
+ fontSize: 14,
489
+ },
490
+ linkText: {
491
+ fontSize: 14,
492
+ fontWeight: '600',
493
+ },
494
+ errorContainer: {
495
+ backgroundColor: '#FFEBEE',
496
+ padding: 12,
497
+ borderRadius: 35,
498
+ marginBottom: 16,
499
+ },
500
+ errorText: {
501
+ color: '#D32F2F',
502
+ fontSize: 14,
503
+ },
504
+ userInfoContainer: {
505
+ padding: 20,
506
+ marginVertical: 20,
507
+ backgroundColor: '#F5F5F5',
508
+ borderRadius: 35,
509
+ alignItems: 'center',
510
+ },
511
+ userInfoText: {
512
+ fontSize: 16,
513
+ marginBottom: 8,
514
+ textAlign: 'center',
515
+ },
516
+ actionButtonsContainer: {
517
+ marginTop: 24,
518
+ },
519
+ // Multi-step form styles
520
+ welcomeTitle: {
521
+ fontSize: 32,
522
+ fontWeight: 'bold',
523
+ textAlign: 'center',
524
+ marginBottom: 20,
525
+ },
526
+ welcomeText: {
527
+ fontSize: 16,
528
+ textAlign: 'center',
529
+ marginBottom: 30,
530
+ lineHeight: 24,
531
+ },
532
+ welcomeImageContainer: {
533
+ alignItems: 'center',
534
+ justifyContent: 'center',
535
+ marginVertical: 30,
536
+ },
537
+ welcomeImagePlaceholder: {
538
+ width: 120,
539
+ height: 120,
540
+ borderRadius: 60,
541
+ },
542
+ stepTitle: {
543
+ fontSize: 24,
544
+ fontWeight: '600',
545
+ marginBottom: 20,
546
+ },
547
+ navigationButtons: {
548
+ flexDirection: 'row',
549
+ justifyContent: 'space-between',
550
+ alignItems: 'center',
551
+ marginTop: 24,
552
+ },
553
+ navButton: {
554
+ borderRadius: 35,
555
+ height: 48,
556
+ alignItems: 'center',
557
+ justifyContent: 'center',
558
+ paddingHorizontal: 24,
559
+ },
560
+ backButton: {
561
+ backgroundColor: 'transparent',
562
+ },
563
+ nextButton: {
564
+ minWidth: 100,
565
+ },
566
+ navButtonText: {
567
+ fontSize: 16,
568
+ fontWeight: '600',
569
+ color: '#FFFFFF',
570
+ },
571
+ passwordHint: {
572
+ fontSize: 12,
573
+ marginTop: 4,
574
+ },
575
+ progressContainer: {
576
+ flexDirection: 'row',
577
+ justifyContent: 'center',
578
+ marginBottom: 20,
579
+ },
580
+ progressDot: {
581
+ height: 8,
582
+ width: 8,
583
+ borderRadius: 4,
584
+ marginHorizontal: 4,
585
+ },
586
+ summaryContainer: {
587
+ backgroundColor: '#F5F5F5',
588
+ borderRadius: 15,
589
+ padding: 16,
590
+ marginBottom: 24,
591
+ },
592
+ summaryRow: {
593
+ flexDirection: 'row',
594
+ marginBottom: 10,
595
+ },
596
+ summaryLabel: {
597
+ fontSize: 14,
598
+ width: 80,
599
+ },
600
+ summaryValue: {
601
+ fontSize: 14,
602
+ fontWeight: '500',
603
+ flex: 1,
604
+ },
605
+ });
606
+
607
+ export default SignUpScreen;