@oxyhq/services 5.11.10 → 5.11.12
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/ui/components/AnimationExample.js +1 -1
- package/lib/commonjs/ui/components/AnimationExample.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +1 -1
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/Header.js +2 -2
- package/lib/commonjs/ui/components/Header.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +3 -3
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/StepBasedScreen.README.md +337 -0
- package/lib/commonjs/ui/components/StepBasedScreen.js +361 -0
- package/lib/commonjs/ui/components/StepBasedScreen.js.map +1 -0
- package/lib/commonjs/ui/components/icon/OxyIcon.js +3 -3
- package/lib/commonjs/ui/components/icon/OxyIcon.js.map +1 -1
- package/lib/commonjs/ui/components/internal/PinInput.js +1 -1
- package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -1
- package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +7 -7
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js +1 -1
- package/lib/commonjs/ui/screens/PaymentGatewayScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +55 -55
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/RecoverAccountScreen.js +87 -219
- package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js +138 -235
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignUpScreen.js +139 -742
- package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +3 -3
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +2 -2
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/RecoverRequestStep.js +110 -0
- package/lib/commonjs/ui/screens/steps/RecoverRequestStep.js.map +1 -0
- package/lib/commonjs/ui/screens/steps/RecoverSuccessStep.js +138 -0
- package/lib/commonjs/ui/screens/steps/RecoverSuccessStep.js.map +1 -0
- package/lib/commonjs/ui/screens/steps/RecoverVerifyStep.js +141 -0
- package/lib/commonjs/ui/screens/steps/RecoverVerifyStep.js.map +1 -0
- package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js +165 -0
- package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js.map +1 -0
- package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js +150 -0
- package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js.map +1 -0
- package/lib/commonjs/ui/screens/steps/SignUpIdentityStep.js +171 -0
- package/lib/commonjs/ui/screens/steps/SignUpIdentityStep.js.map +1 -0
- package/lib/commonjs/ui/screens/steps/SignUpSecurityStep.js +163 -0
- package/lib/commonjs/ui/screens/steps/SignUpSecurityStep.js.map +1 -0
- package/lib/commonjs/ui/screens/steps/SignUpSummaryStep.js +170 -0
- package/lib/commonjs/ui/screens/steps/SignUpSummaryStep.js.map +1 -0
- package/lib/commonjs/ui/screens/steps/SignUpWelcomeStep.js +72 -0
- package/lib/commonjs/ui/screens/steps/SignUpWelcomeStep.js.map +1 -0
- package/lib/module/ui/components/AnimationExample.js +1 -1
- package/lib/module/ui/components/AnimationExample.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +1 -1
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/Header.js +2 -2
- package/lib/module/ui/components/Header.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +3 -3
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/Section.js.map +1 -1
- package/lib/module/ui/components/SectionTitle.js.map +1 -1
- package/lib/module/ui/components/StepBasedScreen.README.md +337 -0
- package/lib/module/ui/components/StepBasedScreen.js +356 -0
- package/lib/module/ui/components/StepBasedScreen.js.map +1 -0
- package/lib/module/ui/components/icon/FAIRWalletIcon.js.map +1 -1
- package/lib/module/ui/components/icon/OxyIcon.js +3 -3
- package/lib/module/ui/components/icon/OxyIcon.js.map +1 -1
- package/lib/module/ui/components/internal/PinInput.js +1 -1
- package/lib/module/ui/components/internal/PinInput.js.map +1 -1
- package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +7 -7
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/screens/PaymentGatewayScreen.js +1 -1
- package/lib/module/ui/screens/PaymentGatewayScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +55 -55
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/RecoverAccountScreen.js +91 -222
- package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +140 -237
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/SignUpScreen.js +141 -743
- package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/module/ui/screens/internal/SignInPasswordStep.js +3 -3
- package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
- package/lib/module/ui/screens/internal/SignInUsernameStep.js +2 -2
- package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -1
- package/lib/module/ui/screens/steps/RecoverRequestStep.js +105 -0
- package/lib/module/ui/screens/steps/RecoverRequestStep.js.map +1 -0
- package/lib/module/ui/screens/steps/RecoverSuccessStep.js +133 -0
- package/lib/module/ui/screens/steps/RecoverSuccessStep.js.map +1 -0
- package/lib/module/ui/screens/steps/RecoverVerifyStep.js +136 -0
- package/lib/module/ui/screens/steps/RecoverVerifyStep.js.map +1 -0
- package/lib/module/ui/screens/steps/SignInPasswordStep.js +160 -0
- package/lib/module/ui/screens/steps/SignInPasswordStep.js.map +1 -0
- package/lib/module/ui/screens/steps/SignInUsernameStep.js +145 -0
- package/lib/module/ui/screens/steps/SignInUsernameStep.js.map +1 -0
- package/lib/module/ui/screens/steps/SignUpIdentityStep.js +166 -0
- package/lib/module/ui/screens/steps/SignUpIdentityStep.js.map +1 -0
- package/lib/module/ui/screens/steps/SignUpSecurityStep.js +158 -0
- package/lib/module/ui/screens/steps/SignUpSecurityStep.js.map +1 -0
- package/lib/module/ui/screens/steps/SignUpSummaryStep.js +165 -0
- package/lib/module/ui/screens/steps/SignUpSummaryStep.js.map +1 -0
- package/lib/module/ui/screens/steps/SignUpWelcomeStep.js +67 -0
- package/lib/module/ui/screens/steps/SignUpWelcomeStep.js.map +1 -0
- package/lib/typescript/models/interfaces.d.ts +4 -3
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/ui/components/AnimationExample.d.ts +1 -1
- package/lib/typescript/ui/components/AnimationExample.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyPayButton.d.ts +2 -2
- package/lib/typescript/ui/components/OxyPayButton.d.ts.map +1 -1
- package/lib/typescript/ui/components/Section.d.ts +2 -1
- package/lib/typescript/ui/components/Section.d.ts.map +1 -1
- package/lib/typescript/ui/components/SectionTitle.d.ts +2 -1
- package/lib/typescript/ui/components/SectionTitle.d.ts.map +1 -1
- package/lib/typescript/ui/components/StepBasedScreen.d.ts +24 -0
- package/lib/typescript/ui/components/StepBasedScreen.d.ts.map +1 -0
- package/lib/typescript/ui/components/icon/FAIRWalletIcon.d.ts +2 -1
- package/lib/typescript/ui/components/icon/FAIRWalletIcon.d.ts.map +1 -1
- package/lib/typescript/ui/components/icon/OxyIcon.d.ts +1 -1
- package/lib/typescript/ui/components/icon/OxyIcon.d.ts.map +1 -1
- package/lib/typescript/ui/components/internal/PinInput.d.ts +9 -1
- package/lib/typescript/ui/components/internal/PinInput.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +2 -1
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts +2 -2
- package/lib/typescript/ui/screens/PaymentGatewayScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts +2 -9
- package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignUpScreen.d.ts +1 -1
- package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/RecoverRequestStep.d.ts +21 -0
- package/lib/typescript/ui/screens/steps/RecoverRequestStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/steps/RecoverSuccessStep.d.ts +18 -0
- package/lib/typescript/ui/screens/steps/RecoverSuccessStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/steps/RecoverVerifyStep.d.ts +24 -0
- package/lib/typescript/ui/screens/steps/RecoverVerifyStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts +27 -0
- package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/steps/SignInUsernameStep.d.ts +27 -0
- package/lib/typescript/ui/screens/steps/SignInUsernameStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/steps/SignUpIdentityStep.d.ts +25 -0
- package/lib/typescript/ui/screens/steps/SignUpIdentityStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/steps/SignUpSecurityStep.d.ts +26 -0
- package/lib/typescript/ui/screens/steps/SignUpSecurityStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/steps/SignUpSummaryStep.d.ts +16 -0
- package/lib/typescript/ui/screens/steps/SignUpSummaryStep.d.ts.map +1 -0
- package/lib/typescript/ui/screens/steps/SignUpWelcomeStep.d.ts +13 -0
- package/lib/typescript/ui/screens/steps/SignUpWelcomeStep.d.ts.map +1 -0
- package/package.json +2 -3
- package/src/models/interfaces.ts +5 -3
- package/src/ui/components/AnimationExample.tsx +9 -8
- package/src/ui/components/FollowButton.tsx +2 -2
- package/src/ui/components/Header.tsx +2 -2
- package/src/ui/components/OxyPayButton.tsx +2 -2
- package/src/ui/components/OxyProvider.tsx +4 -4
- package/src/ui/components/Section.tsx +7 -7
- package/src/ui/components/SectionTitle.tsx +2 -2
- package/src/ui/components/StepBasedScreen.README.md +337 -0
- package/src/ui/components/StepBasedScreen.tsx +417 -0
- package/src/ui/components/icon/FAIRWalletIcon.tsx +2 -2
- package/src/ui/components/icon/OxyIcon.tsx +10 -11
- package/src/ui/components/internal/PinInput.tsx +13 -4
- package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +1 -1
- package/src/ui/context/OxyContext.tsx +12 -11
- package/src/ui/screens/PaymentGatewayScreen.tsx +3 -3
- package/src/ui/screens/ProfileScreen.tsx +54 -54
- package/src/ui/screens/RecoverAccountScreen.tsx +98 -211
- package/src/ui/screens/SignInScreen.tsx +148 -271
- package/src/ui/screens/SignUpScreen.tsx +146 -748
- package/src/ui/screens/internal/SignInPasswordStep.tsx +3 -3
- package/src/ui/screens/internal/SignInUsernameStep.tsx +2 -2
- package/src/ui/screens/steps/RecoverRequestStep.tsx +130 -0
- package/src/ui/screens/steps/RecoverSuccessStep.tsx +131 -0
- package/src/ui/screens/steps/RecoverVerifyStep.tsx +153 -0
- package/src/ui/screens/steps/SignInPasswordStep.tsx +172 -0
- package/src/ui/screens/steps/SignInUsernameStep.tsx +176 -0
- package/src/ui/screens/steps/SignUpIdentityStep.tsx +204 -0
- package/src/ui/screens/steps/SignUpSecurityStep.tsx +191 -0
- package/src/ui/screens/steps/SignUpSummaryStep.tsx +130 -0
- package/src/ui/screens/steps/SignUpWelcomeStep.tsx +65 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useRef, useEffect } from 'react';
|
|
3
|
+
import { View, Text } from 'react-native';
|
|
4
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
5
|
+
import HighFive from '../../../assets/illustrations/HighFive';
|
|
6
|
+
import GroupedPillButtons from '../../components/internal/GroupedPillButtons';
|
|
7
|
+
import TextField from '../../components/internal/TextField';
|
|
8
|
+
|
|
9
|
+
interface SignInUsernameStepProps {
|
|
10
|
+
// Common props from StepBasedScreen
|
|
11
|
+
colors: any;
|
|
12
|
+
styles: any;
|
|
13
|
+
theme: string;
|
|
14
|
+
navigate: (screen: string, props?: Record<string, any>) => void;
|
|
15
|
+
|
|
16
|
+
// Step navigation
|
|
17
|
+
nextStep: () => void;
|
|
18
|
+
prevStep: () => void;
|
|
19
|
+
currentStep: number;
|
|
20
|
+
totalSteps: number;
|
|
21
|
+
|
|
22
|
+
// Data management
|
|
23
|
+
stepData?: any;
|
|
24
|
+
updateStepData: (data: any) => void;
|
|
25
|
+
allStepData: any[];
|
|
26
|
+
|
|
27
|
+
// Form state
|
|
28
|
+
username: string;
|
|
29
|
+
setUsername: (username: string) => void;
|
|
30
|
+
errorMessage: string;
|
|
31
|
+
setErrorMessage: (message: string) => void;
|
|
32
|
+
validationStatus: 'idle' | 'validating' | 'valid' | 'invalid';
|
|
33
|
+
userProfile: any;
|
|
34
|
+
isValidating: boolean;
|
|
35
|
+
|
|
36
|
+
// Add account mode
|
|
37
|
+
isAddAccountMode?: boolean;
|
|
38
|
+
user?: any;
|
|
39
|
+
|
|
40
|
+
// Validation function
|
|
41
|
+
validateUsername: (username: string) => Promise<boolean>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const SignInUsernameStep: React.FC<SignInUsernameStepProps> = ({
|
|
45
|
+
colors,
|
|
46
|
+
styles,
|
|
47
|
+
navigate,
|
|
48
|
+
nextStep,
|
|
49
|
+
username,
|
|
50
|
+
setUsername,
|
|
51
|
+
errorMessage,
|
|
52
|
+
setErrorMessage,
|
|
53
|
+
validationStatus,
|
|
54
|
+
userProfile,
|
|
55
|
+
isValidating,
|
|
56
|
+
isAddAccountMode,
|
|
57
|
+
user,
|
|
58
|
+
validateUsername,
|
|
59
|
+
}) => {
|
|
60
|
+
const inputRef = useRef<any>(null);
|
|
61
|
+
|
|
62
|
+
// Monitor username prop changes
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
console.log('👀 SignInUsernameStep username prop changed:', username);
|
|
65
|
+
}, [username]);
|
|
66
|
+
|
|
67
|
+
const handleUsernameChange = (text: string) => {
|
|
68
|
+
console.log('📝 Username input changed:', text);
|
|
69
|
+
setUsername(text);
|
|
70
|
+
if (errorMessage) setErrorMessage('');
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const handleContinue = async () => {
|
|
74
|
+
console.log('🚀 Continue button pressed, username:', username);
|
|
75
|
+
|
|
76
|
+
const trimmedUsername = username?.trim() || '';
|
|
77
|
+
|
|
78
|
+
if (!trimmedUsername) {
|
|
79
|
+
console.log('❌ Username is empty');
|
|
80
|
+
setErrorMessage('Please enter your username.');
|
|
81
|
+
setTimeout(() => inputRef.current?.focus(), 0);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (trimmedUsername.length < 2) {
|
|
86
|
+
console.log('❌ Username too short');
|
|
87
|
+
setErrorMessage('Username must be at least 3 characters.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log('🔍 Starting username validation...');
|
|
92
|
+
try {
|
|
93
|
+
// Validate the username before proceeding
|
|
94
|
+
const isValid = await validateUsername(trimmedUsername);
|
|
95
|
+
console.log('📊 Validation result:', isValid);
|
|
96
|
+
|
|
97
|
+
if (isValid) {
|
|
98
|
+
console.log('✅ Validation passed, proceeding to next step');
|
|
99
|
+
nextStep();
|
|
100
|
+
} else {
|
|
101
|
+
console.log('❌ Validation failed, staying on current step');
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error('🚨 Error during validation:', error);
|
|
105
|
+
setErrorMessage('Unable to validate username. Please try again.');
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<>
|
|
111
|
+
<HighFive width={100} height={100} />
|
|
112
|
+
<View style={styles.modernHeader}>
|
|
113
|
+
<Text style={[styles.modernTitle, { color: colors.text }]}>
|
|
114
|
+
{isAddAccountMode ? 'Add Another Account' : 'Sign In'}
|
|
115
|
+
</Text>
|
|
116
|
+
<Text style={[styles.modernSubtitle, { color: colors.secondaryText }]}>
|
|
117
|
+
{isAddAccountMode
|
|
118
|
+
? 'Sign in with another account'
|
|
119
|
+
: 'Sign in to continue your journey'
|
|
120
|
+
}
|
|
121
|
+
</Text>
|
|
122
|
+
</View>
|
|
123
|
+
|
|
124
|
+
{isAddAccountMode && user && (
|
|
125
|
+
<View style={[styles.modernInfoCard, { backgroundColor: colors.inputBackground }]}>
|
|
126
|
+
<Ionicons name="information-circle" size={20} color={colors.primary} />
|
|
127
|
+
<Text style={[styles.modernInfoText, { color: colors.text }]}>
|
|
128
|
+
Currently signed in as <Text style={{ fontWeight: 'bold' }}>{user.username}</Text>
|
|
129
|
+
</Text>
|
|
130
|
+
</View>
|
|
131
|
+
)}
|
|
132
|
+
|
|
133
|
+
<View style={styles.modernInputContainer}>
|
|
134
|
+
<TextField
|
|
135
|
+
ref={inputRef}
|
|
136
|
+
label="Username"
|
|
137
|
+
leading={<Ionicons name="person-outline" size={24} color={colors.secondaryText} />}
|
|
138
|
+
value={username}
|
|
139
|
+
onChangeText={handleUsernameChange}
|
|
140
|
+
autoCapitalize="none"
|
|
141
|
+
autoCorrect={false}
|
|
142
|
+
testID="username-input"
|
|
143
|
+
variant="filled"
|
|
144
|
+
error={validationStatus === 'invalid' ? errorMessage : undefined}
|
|
145
|
+
loading={validationStatus === 'validating'}
|
|
146
|
+
success={validationStatus === 'valid'}
|
|
147
|
+
onSubmitEditing={() => handleContinue()}
|
|
148
|
+
autoFocus
|
|
149
|
+
/>
|
|
150
|
+
</View>
|
|
151
|
+
|
|
152
|
+
<GroupedPillButtons
|
|
153
|
+
buttons={[
|
|
154
|
+
{
|
|
155
|
+
text: 'Sign Up',
|
|
156
|
+
onPress: () => navigate('SignUp'),
|
|
157
|
+
icon: 'person-add',
|
|
158
|
+
variant: 'transparent',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
text: 'Continue',
|
|
162
|
+
onPress: handleContinue,
|
|
163
|
+
icon: 'arrow-forward',
|
|
164
|
+
variant: 'primary',
|
|
165
|
+
loading: isValidating,
|
|
166
|
+
disabled: !username || username.trim().length < 2 || isValidating,
|
|
167
|
+
testID: 'username-next-button',
|
|
168
|
+
},
|
|
169
|
+
]}
|
|
170
|
+
colors={colors}
|
|
171
|
+
/>
|
|
172
|
+
</>
|
|
173
|
+
);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export default SignInUsernameStep;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useRef, useState, useEffect, useCallback } from 'react';
|
|
3
|
+
import { View, Text } from 'react-native';
|
|
4
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
5
|
+
import GroupedPillButtons from '../../components/internal/GroupedPillButtons';
|
|
6
|
+
import TextField from '../../components/internal/TextField';
|
|
7
|
+
|
|
8
|
+
interface SignUpIdentityStepProps {
|
|
9
|
+
// Common props from StepBasedScreen
|
|
10
|
+
colors: any;
|
|
11
|
+
styles: any;
|
|
12
|
+
theme: string;
|
|
13
|
+
navigate: (screen: string, props?: Record<string, any>) => void;
|
|
14
|
+
|
|
15
|
+
// Step navigation
|
|
16
|
+
nextStep: () => void;
|
|
17
|
+
prevStep: () => void;
|
|
18
|
+
currentStep: number;
|
|
19
|
+
totalSteps: number;
|
|
20
|
+
|
|
21
|
+
// Data management
|
|
22
|
+
stepData?: any;
|
|
23
|
+
updateStepData: (data: any) => void;
|
|
24
|
+
|
|
25
|
+
// Form state
|
|
26
|
+
username: string;
|
|
27
|
+
email: string;
|
|
28
|
+
setUsername: (username: string) => void;
|
|
29
|
+
setEmail: (email: string) => void;
|
|
30
|
+
validationState: any;
|
|
31
|
+
setValidationState: (state: any) => void;
|
|
32
|
+
setErrorMessage: (message: string) => void;
|
|
33
|
+
|
|
34
|
+
// Validation
|
|
35
|
+
validateEmail: (email: string) => boolean;
|
|
36
|
+
validateUsername: (username: string) => Promise<boolean>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const SignUpIdentityStep: React.FC<SignUpIdentityStepProps> = ({
|
|
40
|
+
colors,
|
|
41
|
+
styles,
|
|
42
|
+
navigate,
|
|
43
|
+
nextStep,
|
|
44
|
+
prevStep,
|
|
45
|
+
username,
|
|
46
|
+
email,
|
|
47
|
+
setUsername,
|
|
48
|
+
setEmail,
|
|
49
|
+
validationState,
|
|
50
|
+
setValidationState,
|
|
51
|
+
setErrorMessage,
|
|
52
|
+
validateEmail,
|
|
53
|
+
validateUsername,
|
|
54
|
+
}) => {
|
|
55
|
+
const usernameRef = useRef<any>(null);
|
|
56
|
+
const validationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
57
|
+
|
|
58
|
+
// Debounced username validation
|
|
59
|
+
const debouncedValidateUsername = useCallback((usernameToValidate: string) => {
|
|
60
|
+
if (validationTimeoutRef.current) {
|
|
61
|
+
clearTimeout(validationTimeoutRef.current);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
validationTimeoutRef.current = setTimeout(async () => {
|
|
65
|
+
if (usernameToValidate.trim().length >= 3) {
|
|
66
|
+
await validateUsername(usernameToValidate.trim());
|
|
67
|
+
}
|
|
68
|
+
}, 500);
|
|
69
|
+
}, [validateUsername]);
|
|
70
|
+
|
|
71
|
+
// Cleanup timeout on unmount
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
return () => {
|
|
74
|
+
if (validationTimeoutRef.current) {
|
|
75
|
+
clearTimeout(validationTimeoutRef.current);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}, []);
|
|
79
|
+
|
|
80
|
+
const handleUsernameChange = (text: string) => {
|
|
81
|
+
setUsername(text);
|
|
82
|
+
setErrorMessage('');
|
|
83
|
+
// Reset validation state when user types
|
|
84
|
+
if (validationState.status !== 'idle') {
|
|
85
|
+
setValidationState({ status: 'idle', message: '' });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Trigger debounced validation
|
|
89
|
+
debouncedValidateUsername(text);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const handleEmailChange = (text: string) => {
|
|
93
|
+
setEmail(text);
|
|
94
|
+
setErrorMessage('');
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const handleNext = async () => {
|
|
98
|
+
if (!username.trim()) {
|
|
99
|
+
setErrorMessage('Please enter a username');
|
|
100
|
+
setTimeout(() => usernameRef.current?.focus(), 0);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (username.trim().length < 3) {
|
|
105
|
+
setErrorMessage('Username must be at least 3 characters');
|
|
106
|
+
setTimeout(() => usernameRef.current?.focus(), 0);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!email.trim()) {
|
|
111
|
+
setErrorMessage('Please enter an email address');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!validateEmail(email)) {
|
|
116
|
+
setErrorMessage('Please enter a valid email address');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Validate username availability
|
|
121
|
+
const isUsernameValid = await validateUsername(username.trim());
|
|
122
|
+
if (!isUsernameValid) {
|
|
123
|
+
setTimeout(() => usernameRef.current?.focus(), 0);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
nextStep();
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const emailError = email && !validateEmail(email) ? 'Please enter a valid email address' : undefined;
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<>
|
|
134
|
+
<View style={styles.modernHeader}>
|
|
135
|
+
<Text style={[styles.modernTitle, { color: colors.text }]}>
|
|
136
|
+
Who are you?
|
|
137
|
+
</Text>
|
|
138
|
+
<Text style={[styles.modernSubtitle, { color: colors.secondaryText }]}>
|
|
139
|
+
Choose your username and enter your email
|
|
140
|
+
</Text>
|
|
141
|
+
</View>
|
|
142
|
+
|
|
143
|
+
<View style={styles.modernInputContainer}>
|
|
144
|
+
<TextField
|
|
145
|
+
ref={usernameRef}
|
|
146
|
+
label="Username"
|
|
147
|
+
leading={<Ionicons name="person-outline" size={24} color={colors.secondaryText} />}
|
|
148
|
+
value={username}
|
|
149
|
+
onChangeText={handleUsernameChange}
|
|
150
|
+
autoCapitalize="none"
|
|
151
|
+
autoCorrect={false}
|
|
152
|
+
testID="signup-username-input"
|
|
153
|
+
variant="filled"
|
|
154
|
+
error={validationState.status === 'invalid' ? validationState.message : undefined}
|
|
155
|
+
loading={validationState.status === 'validating'}
|
|
156
|
+
success={validationState.status === 'valid'}
|
|
157
|
+
onSubmitEditing={handleNext}
|
|
158
|
+
autoFocus
|
|
159
|
+
/>
|
|
160
|
+
|
|
161
|
+
<TextField
|
|
162
|
+
label="Email"
|
|
163
|
+
leading={<Ionicons name="mail-outline" size={24} color={colors.secondaryText} />}
|
|
164
|
+
value={email}
|
|
165
|
+
onChangeText={handleEmailChange}
|
|
166
|
+
keyboardType="email-address"
|
|
167
|
+
autoCapitalize="none"
|
|
168
|
+
autoCorrect={false}
|
|
169
|
+
testID="signup-email-input"
|
|
170
|
+
variant="filled"
|
|
171
|
+
error={emailError}
|
|
172
|
+
onSubmitEditing={handleNext}
|
|
173
|
+
/>
|
|
174
|
+
</View>
|
|
175
|
+
|
|
176
|
+
<GroupedPillButtons
|
|
177
|
+
buttons={[
|
|
178
|
+
{
|
|
179
|
+
text: 'Back',
|
|
180
|
+
onPress: prevStep,
|
|
181
|
+
icon: 'arrow-back',
|
|
182
|
+
variant: 'transparent',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
text: 'Next',
|
|
186
|
+
onPress: handleNext,
|
|
187
|
+
icon: 'arrow-forward',
|
|
188
|
+
variant: 'primary',
|
|
189
|
+
loading: validationState.status === 'validating',
|
|
190
|
+
disabled: !username.trim() ||
|
|
191
|
+
username.trim().length < 3 ||
|
|
192
|
+
!email.trim() ||
|
|
193
|
+
!validateEmail(email) ||
|
|
194
|
+
validationState.status === 'validating' ||
|
|
195
|
+
validationState.status === 'invalid',
|
|
196
|
+
},
|
|
197
|
+
]}
|
|
198
|
+
colors={colors}
|
|
199
|
+
/>
|
|
200
|
+
</>
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export default SignUpIdentityStep;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { View, Text, TouchableOpacity } from 'react-native';
|
|
4
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
5
|
+
import GroupedPillButtons from '../../components/internal/GroupedPillButtons';
|
|
6
|
+
import TextField from '../../components/internal/TextField';
|
|
7
|
+
|
|
8
|
+
interface SignUpSecurityStepProps {
|
|
9
|
+
// Common props from StepBasedScreen
|
|
10
|
+
colors: any;
|
|
11
|
+
styles: any;
|
|
12
|
+
theme: string;
|
|
13
|
+
navigate: (screen: string, props?: Record<string, any>) => void;
|
|
14
|
+
|
|
15
|
+
// Step navigation
|
|
16
|
+
nextStep: () => void;
|
|
17
|
+
prevStep: () => void;
|
|
18
|
+
currentStep: number;
|
|
19
|
+
totalSteps: number;
|
|
20
|
+
|
|
21
|
+
// Data management
|
|
22
|
+
stepData?: any;
|
|
23
|
+
updateStepData: (data: any) => void;
|
|
24
|
+
|
|
25
|
+
// Form state
|
|
26
|
+
password: string;
|
|
27
|
+
confirmPassword: string;
|
|
28
|
+
setPassword: (password: string) => void;
|
|
29
|
+
setConfirmPassword: (confirmPassword: string) => void;
|
|
30
|
+
showPassword: boolean;
|
|
31
|
+
showConfirmPassword: boolean;
|
|
32
|
+
setShowPassword: (show: boolean) => void;
|
|
33
|
+
setShowConfirmPassword: (show: boolean) => void;
|
|
34
|
+
setErrorMessage: (message: string) => void;
|
|
35
|
+
|
|
36
|
+
// Validation
|
|
37
|
+
validatePassword: (password: string) => boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const SignUpSecurityStep: React.FC<SignUpSecurityStepProps> = ({
|
|
41
|
+
colors,
|
|
42
|
+
styles,
|
|
43
|
+
nextStep,
|
|
44
|
+
prevStep,
|
|
45
|
+
password,
|
|
46
|
+
confirmPassword,
|
|
47
|
+
setPassword,
|
|
48
|
+
setConfirmPassword,
|
|
49
|
+
showPassword,
|
|
50
|
+
showConfirmPassword,
|
|
51
|
+
setShowPassword,
|
|
52
|
+
setShowConfirmPassword,
|
|
53
|
+
setErrorMessage,
|
|
54
|
+
validatePassword,
|
|
55
|
+
}) => {
|
|
56
|
+
const passwordRef = useRef<any>(null);
|
|
57
|
+
|
|
58
|
+
const handlePasswordChange = (text: string) => {
|
|
59
|
+
setPassword(text);
|
|
60
|
+
setErrorMessage('');
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleConfirmPasswordChange = (text: string) => {
|
|
64
|
+
setConfirmPassword(text);
|
|
65
|
+
setErrorMessage('');
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleNext = () => {
|
|
69
|
+
if (!password) {
|
|
70
|
+
setErrorMessage('Please enter a password');
|
|
71
|
+
setTimeout(() => passwordRef.current?.focus(), 0);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!validatePassword(password)) {
|
|
76
|
+
setErrorMessage('Password must be at least 8 characters long');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!confirmPassword) {
|
|
81
|
+
setErrorMessage('Please confirm your password');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (password !== confirmPassword) {
|
|
86
|
+
setErrorMessage('Passwords do not match');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
nextStep();
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const passwordError = password && !validatePassword(password) ? 'Password must be at least 8 characters long' : undefined;
|
|
94
|
+
const confirmPasswordError = confirmPassword && password !== confirmPassword ? 'Passwords do not match' : undefined;
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<>
|
|
98
|
+
<View style={styles.modernHeader}>
|
|
99
|
+
<Text style={[styles.modernTitle, { color: colors.text }]}>
|
|
100
|
+
Secure Your Account
|
|
101
|
+
</Text>
|
|
102
|
+
<Text style={[styles.modernSubtitle, { color: colors.secondaryText }]}>
|
|
103
|
+
Create a strong password to protect your account
|
|
104
|
+
</Text>
|
|
105
|
+
</View>
|
|
106
|
+
|
|
107
|
+
<View style={styles.modernInputContainer}>
|
|
108
|
+
<TextField
|
|
109
|
+
ref={passwordRef}
|
|
110
|
+
label="Password"
|
|
111
|
+
leading={<Ionicons name="lock-closed-outline" size={24} color={colors.secondaryText} />}
|
|
112
|
+
trailing={
|
|
113
|
+
<TouchableOpacity
|
|
114
|
+
onPress={() => setShowPassword(!showPassword)}
|
|
115
|
+
style={{ padding: 4 }}
|
|
116
|
+
>
|
|
117
|
+
<Ionicons
|
|
118
|
+
name={showPassword ? "eye-off-outline" : "eye-outline"}
|
|
119
|
+
size={20}
|
|
120
|
+
color={colors.secondaryText}
|
|
121
|
+
/>
|
|
122
|
+
</TouchableOpacity>
|
|
123
|
+
}
|
|
124
|
+
value={password}
|
|
125
|
+
onChangeText={handlePasswordChange}
|
|
126
|
+
secureTextEntry={!showPassword}
|
|
127
|
+
autoCapitalize="none"
|
|
128
|
+
autoCorrect={false}
|
|
129
|
+
testID="signup-password-input"
|
|
130
|
+
variant="filled"
|
|
131
|
+
error={passwordError}
|
|
132
|
+
onSubmitEditing={handleNext}
|
|
133
|
+
autoFocus
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<TextField
|
|
137
|
+
label="Confirm Password"
|
|
138
|
+
leading={<Ionicons name="lock-closed-outline" size={24} color={colors.secondaryText} />}
|
|
139
|
+
trailing={
|
|
140
|
+
<TouchableOpacity
|
|
141
|
+
onPress={() => setShowConfirmPassword(!showConfirmPassword)}
|
|
142
|
+
style={{ padding: 4 }}
|
|
143
|
+
>
|
|
144
|
+
<Ionicons
|
|
145
|
+
name={showConfirmPassword ? "eye-off-outline" : "eye-outline"}
|
|
146
|
+
size={20}
|
|
147
|
+
color={colors.secondaryText}
|
|
148
|
+
/>
|
|
149
|
+
</TouchableOpacity>
|
|
150
|
+
}
|
|
151
|
+
value={confirmPassword}
|
|
152
|
+
onChangeText={handleConfirmPasswordChange}
|
|
153
|
+
secureTextEntry={!showConfirmPassword}
|
|
154
|
+
autoCapitalize="none"
|
|
155
|
+
autoCorrect={false}
|
|
156
|
+
testID="signup-confirm-password-input"
|
|
157
|
+
variant="filled"
|
|
158
|
+
error={confirmPasswordError}
|
|
159
|
+
onSubmitEditing={handleNext}
|
|
160
|
+
/>
|
|
161
|
+
|
|
162
|
+
<View style={{ marginTop: 16 }}>
|
|
163
|
+
<Text style={[styles.footerText, { color: colors.secondaryText, fontSize: 12 }]}>
|
|
164
|
+
Password must be at least 8 characters long
|
|
165
|
+
</Text>
|
|
166
|
+
</View>
|
|
167
|
+
</View>
|
|
168
|
+
|
|
169
|
+
<GroupedPillButtons
|
|
170
|
+
buttons={[
|
|
171
|
+
{
|
|
172
|
+
text: 'Back',
|
|
173
|
+
onPress: prevStep,
|
|
174
|
+
icon: 'arrow-back',
|
|
175
|
+
variant: 'transparent',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
text: 'Next',
|
|
179
|
+
onPress: handleNext,
|
|
180
|
+
icon: 'arrow-forward',
|
|
181
|
+
variant: 'primary',
|
|
182
|
+
disabled: !password || !confirmPassword || password !== confirmPassword,
|
|
183
|
+
},
|
|
184
|
+
]}
|
|
185
|
+
colors={colors}
|
|
186
|
+
/>
|
|
187
|
+
</>
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export default SignUpSecurityStep;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { View, Text } from 'react-native';
|
|
3
|
+
import { Ionicons } from '@expo/vector-icons';
|
|
4
|
+
import GroupedPillButtons from '../../components/internal/GroupedPillButtons';
|
|
5
|
+
|
|
6
|
+
interface SignUpSummaryStepProps {
|
|
7
|
+
// Common props from StepBasedScreen
|
|
8
|
+
colors: any;
|
|
9
|
+
styles: any;
|
|
10
|
+
theme: string;
|
|
11
|
+
navigate: (screen: string, props?: Record<string, any>) => void;
|
|
12
|
+
|
|
13
|
+
// Step navigation
|
|
14
|
+
nextStep: () => void;
|
|
15
|
+
prevStep: () => void;
|
|
16
|
+
currentStep: number;
|
|
17
|
+
totalSteps: number;
|
|
18
|
+
|
|
19
|
+
// Data management
|
|
20
|
+
allStepData: any[];
|
|
21
|
+
|
|
22
|
+
// Form state
|
|
23
|
+
isLoading: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const SignUpSummaryStep: React.FC<SignUpSummaryStepProps> = ({
|
|
27
|
+
colors,
|
|
28
|
+
styles,
|
|
29
|
+
nextStep,
|
|
30
|
+
prevStep,
|
|
31
|
+
allStepData,
|
|
32
|
+
isLoading,
|
|
33
|
+
}) => {
|
|
34
|
+
// Extract data from previous steps
|
|
35
|
+
const identityData = allStepData[1] || {}; // Step 2 (index 1)
|
|
36
|
+
const securityData = allStepData[2] || {}; // Step 3 (index 2)
|
|
37
|
+
|
|
38
|
+
const { username = '', email = '' } = identityData;
|
|
39
|
+
const { password = '' } = securityData;
|
|
40
|
+
|
|
41
|
+
// Check if all required data is available
|
|
42
|
+
const hasValidData = username && email && password;
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<View style={styles.modernHeader}>
|
|
49
|
+
<Text style={[styles.modernTitle, { color: colors.text }]}>
|
|
50
|
+
Almost There!
|
|
51
|
+
</Text>
|
|
52
|
+
<Text style={[styles.modernSubtitle, { color: colors.secondaryText }]}>
|
|
53
|
+
Review your information and create your account
|
|
54
|
+
</Text>
|
|
55
|
+
</View>
|
|
56
|
+
|
|
57
|
+
<View style={[styles.modernInputContainer, { marginBottom: 32 }]}>
|
|
58
|
+
<View style={{
|
|
59
|
+
backgroundColor: colors.inputBackground,
|
|
60
|
+
borderRadius: 16,
|
|
61
|
+
padding: 20,
|
|
62
|
+
borderWidth: 1,
|
|
63
|
+
borderColor: colors.border,
|
|
64
|
+
}}>
|
|
65
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 16 }}>
|
|
66
|
+
<Ionicons name="person-outline" size={20} color={colors.secondaryText} style={{ marginRight: 12 }} />
|
|
67
|
+
<View style={{ flex: 1 }}>
|
|
68
|
+
<Text style={[styles.footerText, { color: colors.secondaryText, fontSize: 12, marginBottom: 4 }]}>
|
|
69
|
+
Username
|
|
70
|
+
</Text>
|
|
71
|
+
<Text style={[styles.modernInput, { color: colors.text, fontSize: 16 }]}>
|
|
72
|
+
@{username || 'Not set'}
|
|
73
|
+
</Text>
|
|
74
|
+
</View>
|
|
75
|
+
</View>
|
|
76
|
+
|
|
77
|
+
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
|
78
|
+
<Ionicons name="mail-outline" size={20} color={colors.secondaryText} style={{ marginRight: 12 }} />
|
|
79
|
+
<View style={{ flex: 1 }}>
|
|
80
|
+
<Text style={[styles.footerText, { color: colors.secondaryText, fontSize: 12, marginBottom: 4 }]}>
|
|
81
|
+
Email
|
|
82
|
+
</Text>
|
|
83
|
+
<Text style={[styles.modernInput, { color: colors.text, fontSize: 16 }]}>
|
|
84
|
+
{email || 'Not set'}
|
|
85
|
+
</Text>
|
|
86
|
+
</View>
|
|
87
|
+
</View>
|
|
88
|
+
</View>
|
|
89
|
+
|
|
90
|
+
<View style={{
|
|
91
|
+
flexDirection: 'row',
|
|
92
|
+
alignItems: 'center',
|
|
93
|
+
marginTop: 16,
|
|
94
|
+
padding: 12,
|
|
95
|
+
backgroundColor: colors.success + '10',
|
|
96
|
+
borderRadius: 8,
|
|
97
|
+
borderWidth: 1,
|
|
98
|
+
borderColor: colors.success + '30',
|
|
99
|
+
}}>
|
|
100
|
+
<Ionicons name="checkmark-circle" size={20} color={colors.success} style={{ marginRight: 8 }} />
|
|
101
|
+
<Text style={[styles.footerText, { color: colors.success, fontSize: 14, flex: 1 }]}>
|
|
102
|
+
By creating an account, you agree to our Terms of Service and Privacy Policy
|
|
103
|
+
</Text>
|
|
104
|
+
</View>
|
|
105
|
+
</View>
|
|
106
|
+
|
|
107
|
+
<GroupedPillButtons
|
|
108
|
+
buttons={[
|
|
109
|
+
{
|
|
110
|
+
text: 'Back',
|
|
111
|
+
onPress: prevStep,
|
|
112
|
+
icon: 'arrow-back',
|
|
113
|
+
variant: 'transparent',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
text: 'Create Account',
|
|
117
|
+
onPress: nextStep,
|
|
118
|
+
icon: 'checkmark-circle',
|
|
119
|
+
variant: 'primary',
|
|
120
|
+
loading: isLoading,
|
|
121
|
+
disabled: !hasValidData,
|
|
122
|
+
},
|
|
123
|
+
]}
|
|
124
|
+
colors={colors}
|
|
125
|
+
/>
|
|
126
|
+
</>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export default SignUpSummaryStep;
|