@xouteiro/auth_npm 1.0.13 → 1.0.15
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/package.json +3 -2
- package/src/components/LoginOnly.jsx +129 -75
- package/src/components/LoginRegisterFlow.jsx +130 -256
- package/src/components/LogoutButton.jsx +72 -58
- package/dist/index.js +0 -2885
- package/dist/index.mjs +0 -2862
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xouteiro/auth_npm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "Authentication package for Supabase",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@supabase/auth-ui-react": "^0.4.7",
|
|
19
19
|
"@supabase/auth-ui-shared": "^0.1.8",
|
|
20
|
-
"@supabase/supabase-js": "^2.52.1"
|
|
20
|
+
"@supabase/supabase-js": "^2.52.1",
|
|
21
|
+
"react-native": "^0.81.4"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
24
|
"@types/react": "^19.1.9",
|
|
@@ -1,96 +1,150 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
TextInput,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
KeyboardAvoidingView,
|
|
9
|
+
Platform,
|
|
10
|
+
ScrollView,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
import { registerWithEmail } from '../register';
|
|
3
13
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
margin: '40px auto',
|
|
7
|
-
padding: 28,
|
|
8
|
-
borderRadius: 12,
|
|
9
|
-
boxShadow: '0 2px 12px rgba(0,0,0,0.08)',
|
|
10
|
-
background: '#fff',
|
|
11
|
-
fontFamily: 'sans-serif',
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const inputStyle = {
|
|
15
|
-
width: '100%',
|
|
16
|
-
padding: 10,
|
|
17
|
-
margin: '10px 0',
|
|
18
|
-
borderRadius: 6,
|
|
19
|
-
border: '1px solid #ddd',
|
|
20
|
-
fontSize: 16,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const buttonStyle = {
|
|
24
|
-
width: '100%',
|
|
25
|
-
padding: 12,
|
|
26
|
-
borderRadius: 6,
|
|
27
|
-
border: 'none',
|
|
28
|
-
background: '#0070f3',
|
|
29
|
-
color: '#fff',
|
|
30
|
-
fontWeight: 600,
|
|
31
|
-
fontSize: 16,
|
|
32
|
-
marginTop: 10,
|
|
33
|
-
cursor: 'pointer',
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const messageStyle = (success) => ({
|
|
37
|
-
margin: '12px 0',
|
|
38
|
-
color: success ? '#0a0' : '#d32f2f',
|
|
39
|
-
background: success ? '#eafbe7' : '#fdeaea',
|
|
40
|
-
padding: 8,
|
|
41
|
-
borderRadius: 6,
|
|
42
|
-
textAlign: 'center',
|
|
43
|
-
fontSize: 15,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export default function LoginOnly() {
|
|
47
|
-
const [identifier, setIdentifier] = useState('');
|
|
14
|
+
export default function RegisterOnly() {
|
|
15
|
+
const [email, setEmail] = useState('');
|
|
48
16
|
const [password, setPassword] = useState('');
|
|
17
|
+
const [username, setUsername] = useState('');
|
|
18
|
+
const [phone, setPhone] = useState('');
|
|
49
19
|
const [message, setMessage] = useState(null);
|
|
50
20
|
const [success, setSuccess] = useState(false);
|
|
51
21
|
|
|
52
|
-
const
|
|
53
|
-
e.preventDefault();
|
|
22
|
+
const handleRegister = async () => {
|
|
54
23
|
setMessage(null);
|
|
55
24
|
setSuccess(false);
|
|
56
|
-
const { error } = await
|
|
25
|
+
const { error } = await registerWithEmail(email, password, username, phone);
|
|
57
26
|
if (error) {
|
|
58
|
-
setMessage(error.message || '
|
|
27
|
+
setMessage(error.message || 'Registration failed');
|
|
59
28
|
setSuccess(false);
|
|
60
29
|
} else {
|
|
61
|
-
setMessage('
|
|
30
|
+
setMessage('Registration successful! Please check your email to confirm.');
|
|
62
31
|
setSuccess(true);
|
|
63
32
|
setTimeout(() => {
|
|
64
|
-
|
|
33
|
+
setEmail('');
|
|
65
34
|
setPassword('');
|
|
35
|
+
setUsername('');
|
|
36
|
+
setPhone('');
|
|
66
37
|
setMessage(null);
|
|
67
38
|
setSuccess(false);
|
|
68
|
-
},
|
|
39
|
+
}, 2000);
|
|
69
40
|
}
|
|
70
41
|
};
|
|
71
42
|
|
|
72
43
|
return (
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
44
|
+
<KeyboardAvoidingView
|
|
45
|
+
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
|
46
|
+
style={styles.container}
|
|
47
|
+
>
|
|
48
|
+
<ScrollView contentContainerStyle={styles.scroll}>
|
|
49
|
+
<View style={styles.box}>
|
|
50
|
+
<TextInput
|
|
51
|
+
style={styles.input}
|
|
52
|
+
placeholder="Email"
|
|
53
|
+
value={email}
|
|
54
|
+
onChangeText={setEmail}
|
|
55
|
+
autoCapitalize="none"
|
|
56
|
+
keyboardType="email-address"
|
|
57
|
+
/>
|
|
58
|
+
<TextInput
|
|
59
|
+
style={styles.input}
|
|
60
|
+
placeholder="Password"
|
|
61
|
+
value={password}
|
|
62
|
+
onChangeText={setPassword}
|
|
63
|
+
secureTextEntry
|
|
64
|
+
/>
|
|
65
|
+
<TextInput
|
|
66
|
+
style={styles.input}
|
|
67
|
+
placeholder="Username (optional)"
|
|
68
|
+
value={username}
|
|
69
|
+
onChangeText={setUsername}
|
|
70
|
+
/>
|
|
71
|
+
<TextInput
|
|
72
|
+
style={styles.input}
|
|
73
|
+
placeholder="Phone (optional)"
|
|
74
|
+
value={phone}
|
|
75
|
+
onChangeText={setPhone}
|
|
76
|
+
keyboardType="phone-pad"
|
|
77
|
+
/>
|
|
78
|
+
<TouchableOpacity style={styles.button} onPress={handleRegister}>
|
|
79
|
+
<Text style={styles.buttonText}>Register</Text>
|
|
80
|
+
</TouchableOpacity>
|
|
81
|
+
{message && (
|
|
82
|
+
<View style={[styles.message, success ? styles.success : styles.error]}>
|
|
83
|
+
<Text style={styles.messageText}>{message}</Text>
|
|
84
|
+
</View>
|
|
85
|
+
)}
|
|
86
|
+
</View>
|
|
87
|
+
</ScrollView>
|
|
88
|
+
</KeyboardAvoidingView>
|
|
95
89
|
);
|
|
96
|
-
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const styles = StyleSheet.create({
|
|
93
|
+
container: {
|
|
94
|
+
flex: 1,
|
|
95
|
+
backgroundColor: '#f5f5f5',
|
|
96
|
+
},
|
|
97
|
+
scroll: {
|
|
98
|
+
flexGrow: 1,
|
|
99
|
+
justifyContent: 'center',
|
|
100
|
+
paddingHorizontal: 24,
|
|
101
|
+
},
|
|
102
|
+
box: {
|
|
103
|
+
padding: 28,
|
|
104
|
+
borderRadius: 12,
|
|
105
|
+
backgroundColor: '#fff',
|
|
106
|
+
shadowColor: '#000',
|
|
107
|
+
shadowOpacity: 0.08,
|
|
108
|
+
shadowRadius: 12,
|
|
109
|
+
elevation: 4,
|
|
110
|
+
},
|
|
111
|
+
input: {
|
|
112
|
+
width: '100%',
|
|
113
|
+
padding: 10,
|
|
114
|
+
marginVertical: 10,
|
|
115
|
+
borderRadius: 6,
|
|
116
|
+
borderWidth: 1,
|
|
117
|
+
borderColor: '#ddd',
|
|
118
|
+
fontSize: 16,
|
|
119
|
+
},
|
|
120
|
+
button: {
|
|
121
|
+
width: '100%',
|
|
122
|
+
padding: 12,
|
|
123
|
+
borderRadius: 6,
|
|
124
|
+
backgroundColor: '#0070f3',
|
|
125
|
+
marginTop: 10,
|
|
126
|
+
alignItems: 'center',
|
|
127
|
+
},
|
|
128
|
+
buttonText: {
|
|
129
|
+
color: '#fff',
|
|
130
|
+
fontWeight: '600',
|
|
131
|
+
fontSize: 16,
|
|
132
|
+
},
|
|
133
|
+
message: {
|
|
134
|
+
marginTop: 12,
|
|
135
|
+
padding: 8,
|
|
136
|
+
borderRadius: 6,
|
|
137
|
+
alignItems: 'center',
|
|
138
|
+
},
|
|
139
|
+
success: {
|
|
140
|
+
backgroundColor: '#eafbe7',
|
|
141
|
+
},
|
|
142
|
+
error: {
|
|
143
|
+
backgroundColor: '#fdeaea',
|
|
144
|
+
},
|
|
145
|
+
messageText: {
|
|
146
|
+
fontSize: 15,
|
|
147
|
+
color: '#333',
|
|
148
|
+
textAlign: 'center',
|
|
149
|
+
},
|
|
150
|
+
});
|
|
@@ -1,156 +1,14 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
TextInput,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
} from 'react-native';
|
|
2
9
|
import { signInWithEmail } from '../login';
|
|
3
10
|
import { registerWithEmail } from '../register';
|
|
4
11
|
|
|
5
|
-
// SVG Eye Icon
|
|
6
|
-
const EyeIcon = ({ open }) => (
|
|
7
|
-
<svg
|
|
8
|
-
width="22"
|
|
9
|
-
height="22"
|
|
10
|
-
viewBox="0 0 22 22"
|
|
11
|
-
fill="none"
|
|
12
|
-
style={{ verticalAlign: 'middle' }}
|
|
13
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
-
>
|
|
15
|
-
{open ? (
|
|
16
|
-
<>
|
|
17
|
-
<path
|
|
18
|
-
d="M1 11C3.5 5.5 11 2 21 11C18.5 16.5 11 20 1 11Z"
|
|
19
|
-
stroke="#667eea"
|
|
20
|
-
strokeWidth="2"
|
|
21
|
-
fill="none"
|
|
22
|
-
/>
|
|
23
|
-
<circle
|
|
24
|
-
cx="11"
|
|
25
|
-
cy="11"
|
|
26
|
-
r="3"
|
|
27
|
-
stroke="#667eea"
|
|
28
|
-
strokeWidth="2"
|
|
29
|
-
fill="none"
|
|
30
|
-
/>
|
|
31
|
-
</>
|
|
32
|
-
) : (
|
|
33
|
-
<>
|
|
34
|
-
<path
|
|
35
|
-
d="M1 11C3.5 5.5 11 2 21 11C18.5 16.5 11 20 1 11Z"
|
|
36
|
-
stroke="#667eea"
|
|
37
|
-
strokeWidth="2"
|
|
38
|
-
fill="none"
|
|
39
|
-
/>
|
|
40
|
-
<circle
|
|
41
|
-
cx="11"
|
|
42
|
-
cy="11"
|
|
43
|
-
r="3"
|
|
44
|
-
stroke="#667eea"
|
|
45
|
-
strokeWidth="2"
|
|
46
|
-
fill="none"
|
|
47
|
-
/>
|
|
48
|
-
<line
|
|
49
|
-
x1="4"
|
|
50
|
-
y1="18"
|
|
51
|
-
x2="18"
|
|
52
|
-
y2="4"
|
|
53
|
-
stroke="#d32f2f"
|
|
54
|
-
strokeWidth="2"
|
|
55
|
-
/>
|
|
56
|
-
</>
|
|
57
|
-
)}
|
|
58
|
-
</svg>
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
const boxStyle = {
|
|
62
|
-
maxWidth: 400,
|
|
63
|
-
margin: '0 auto',
|
|
64
|
-
padding: 32,
|
|
65
|
-
borderRadius: 20,
|
|
66
|
-
boxShadow: '0 8px 32px rgba(102, 126, 234, 0.15)',
|
|
67
|
-
background: 'rgba(255, 255, 255, 0.98)',
|
|
68
|
-
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
69
|
-
backdropFilter: 'blur(10px)',
|
|
70
|
-
border: '1px solid rgba(255, 255, 255, 0.2)',
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const tabStyle = (active) => ({
|
|
74
|
-
flex: 1,
|
|
75
|
-
padding: 16,
|
|
76
|
-
border: 'none',
|
|
77
|
-
borderBottom: active ? '3px solid #667eea' : '3px solid rgba(255, 255, 255, 0.3)',
|
|
78
|
-
background: 'none',
|
|
79
|
-
fontWeight: active ? 700 : 500,
|
|
80
|
-
color: active ? '#667eea' : 'rgba(0, 0, 0, 0.6)',
|
|
81
|
-
cursor: 'pointer',
|
|
82
|
-
outline: 'none',
|
|
83
|
-
fontSize: 16,
|
|
84
|
-
transition: 'all 0.3s ease',
|
|
85
|
-
letterSpacing: '0.5px',
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const inputStyle = {
|
|
89
|
-
width: '100%',
|
|
90
|
-
padding: '16px 44px 16px 16px',
|
|
91
|
-
margin: '12px 0',
|
|
92
|
-
borderRadius: 12,
|
|
93
|
-
border: '2px solid rgba(102, 126, 234, 0.2)',
|
|
94
|
-
fontSize: 16,
|
|
95
|
-
background: 'rgba(248, 250, 252, 0.8)',
|
|
96
|
-
boxSizing: 'border-box',
|
|
97
|
-
outline: 'none',
|
|
98
|
-
transition: 'all 0.3s ease',
|
|
99
|
-
fontWeight: '500',
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const buttonStyle = {
|
|
103
|
-
width: '100%',
|
|
104
|
-
padding: 16,
|
|
105
|
-
borderRadius: 12,
|
|
106
|
-
border: 'none',
|
|
107
|
-
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
108
|
-
color: '#fff',
|
|
109
|
-
fontWeight: 700,
|
|
110
|
-
fontSize: 16,
|
|
111
|
-
marginTop: 20,
|
|
112
|
-
cursor: 'pointer',
|
|
113
|
-
letterSpacing: '0.5px',
|
|
114
|
-
boxShadow: '0 8px 24px rgba(102, 126, 234, 0.3)',
|
|
115
|
-
transition: 'all 0.3s ease',
|
|
116
|
-
transform: 'translateY(0)',
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
const toggleButtonStyle = {
|
|
120
|
-
position: 'absolute',
|
|
121
|
-
right: 12,
|
|
122
|
-
top: '50%',
|
|
123
|
-
transform: 'translateY(-50%)',
|
|
124
|
-
background: 'none',
|
|
125
|
-
border: 'none',
|
|
126
|
-
color: '#667eea',
|
|
127
|
-
cursor: 'pointer',
|
|
128
|
-
fontSize: 18,
|
|
129
|
-
padding: 4,
|
|
130
|
-
display: 'flex',
|
|
131
|
-
alignItems: 'center',
|
|
132
|
-
borderRadius: 6,
|
|
133
|
-
transition: 'all 0.2s ease',
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const inputWrapperStyle = {
|
|
137
|
-
position: 'relative',
|
|
138
|
-
width: '100%',
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const messageStyle = (success) => ({
|
|
142
|
-
margin: '16px 0 0 0',
|
|
143
|
-
color: success ? '#22c55e' : '#ef4444',
|
|
144
|
-
background: success ? 'rgba(34, 197, 94, 0.1)' : 'rgba(239, 68, 68, 0.1)',
|
|
145
|
-
padding: 12,
|
|
146
|
-
borderRadius: 12,
|
|
147
|
-
textAlign: 'center',
|
|
148
|
-
fontSize: 14,
|
|
149
|
-
fontWeight: 600,
|
|
150
|
-
letterSpacing: '0.5px',
|
|
151
|
-
border: success ? '1px solid rgba(34, 197, 94, 0.2)' : '1px solid rgba(239, 68, 68, 0.2)',
|
|
152
|
-
});
|
|
153
|
-
|
|
154
12
|
export default function LoginRegisterFlow({ onLoginSuccess, onRegisterSuccess }) {
|
|
155
13
|
const [mode, setMode] = useState('login');
|
|
156
14
|
const [identifier, setIdentifier] = useState('');
|
|
@@ -159,8 +17,6 @@ export default function LoginRegisterFlow({ onLoginSuccess, onRegisterSuccess })
|
|
|
159
17
|
const [displayName, setDisplayName] = useState('');
|
|
160
18
|
const [message, setMessage] = useState(null);
|
|
161
19
|
const [success, setSuccess] = useState(false);
|
|
162
|
-
const [showPassword, setShowPassword] = useState(false);
|
|
163
|
-
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
|
164
20
|
|
|
165
21
|
const resetFields = () => {
|
|
166
22
|
setIdentifier('');
|
|
@@ -169,14 +25,9 @@ export default function LoginRegisterFlow({ onLoginSuccess, onRegisterSuccess })
|
|
|
169
25
|
setDisplayName('');
|
|
170
26
|
setMessage(null);
|
|
171
27
|
setSuccess(false);
|
|
172
|
-
setShowPassword(false);
|
|
173
|
-
setShowConfirmPassword(false);
|
|
174
28
|
};
|
|
175
29
|
|
|
176
|
-
const handleLogin = async (
|
|
177
|
-
e.preventDefault();
|
|
178
|
-
setMessage(null);
|
|
179
|
-
setSuccess(false);
|
|
30
|
+
const handleLogin = async () => {
|
|
180
31
|
const { session, error } = await signInWithEmail(identifier, password);
|
|
181
32
|
if (error) {
|
|
182
33
|
setMessage(error.message || 'Login failed');
|
|
@@ -184,15 +35,12 @@ export default function LoginRegisterFlow({ onLoginSuccess, onRegisterSuccess })
|
|
|
184
35
|
} else {
|
|
185
36
|
setMessage('Login successful!');
|
|
186
37
|
setSuccess(true);
|
|
187
|
-
|
|
38
|
+
onLoginSuccess?.(session);
|
|
188
39
|
setTimeout(resetFields, 1200);
|
|
189
40
|
}
|
|
190
41
|
};
|
|
191
42
|
|
|
192
|
-
const handleRegister = async (
|
|
193
|
-
e.preventDefault();
|
|
194
|
-
setMessage(null);
|
|
195
|
-
setSuccess(false);
|
|
43
|
+
const handleRegister = async () => {
|
|
196
44
|
if (password !== confirmPassword) {
|
|
197
45
|
setMessage("Passwords do not match");
|
|
198
46
|
setSuccess(false);
|
|
@@ -203,116 +51,142 @@ export default function LoginRegisterFlow({ onLoginSuccess, onRegisterSuccess })
|
|
|
203
51
|
setMessage(error.message || 'Registration failed');
|
|
204
52
|
setSuccess(false);
|
|
205
53
|
} else {
|
|
206
|
-
setMessage('Registration successful! Please check your email
|
|
54
|
+
setMessage('Registration successful! Please check your email.');
|
|
207
55
|
setSuccess(true);
|
|
208
|
-
|
|
56
|
+
onRegisterSuccess?.(user);
|
|
209
57
|
setTimeout(resetFields, 2000);
|
|
210
58
|
}
|
|
211
59
|
};
|
|
212
60
|
|
|
213
61
|
return (
|
|
214
|
-
<
|
|
215
|
-
<
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
62
|
+
<View style={styles.container}>
|
|
63
|
+
<View style={styles.tabContainer}>
|
|
64
|
+
<TouchableOpacity onPress={() => setMode('login')} style={[styles.tab, mode === 'login' && styles.activeTab]}>
|
|
65
|
+
<Text style={styles.tabText}>Login</Text>
|
|
66
|
+
</TouchableOpacity>
|
|
67
|
+
<TouchableOpacity onPress={() => setMode('register')} style={[styles.tab, mode === 'register' && styles.activeTab]}>
|
|
68
|
+
<Text style={styles.tabText}>Register</Text>
|
|
69
|
+
</TouchableOpacity>
|
|
70
|
+
</View>
|
|
71
|
+
|
|
219
72
|
{mode === 'login' ? (
|
|
220
|
-
<
|
|
221
|
-
<
|
|
222
|
-
style={
|
|
223
|
-
type="text"
|
|
73
|
+
<View>
|
|
74
|
+
<TextInput
|
|
75
|
+
style={styles.input}
|
|
224
76
|
placeholder="Email"
|
|
225
77
|
value={identifier}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
78
|
+
onChangeText={setIdentifier}
|
|
79
|
+
/>
|
|
80
|
+
<TextInput
|
|
81
|
+
style={styles.input}
|
|
82
|
+
placeholder="Password"
|
|
83
|
+
value={password}
|
|
84
|
+
onChangeText={setPassword}
|
|
85
|
+
secureTextEntry
|
|
229
86
|
/>
|
|
230
|
-
<
|
|
231
|
-
<
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
placeholder="Password"
|
|
235
|
-
value={password}
|
|
236
|
-
onChange={e => setPassword(e.target.value)}
|
|
237
|
-
required
|
|
238
|
-
autoComplete="current-password"
|
|
239
|
-
/>
|
|
240
|
-
<button
|
|
241
|
-
type="button"
|
|
242
|
-
style={toggleButtonStyle}
|
|
243
|
-
tabIndex={-1}
|
|
244
|
-
aria-label={showPassword ? "Hide password" : "Show password"}
|
|
245
|
-
onClick={() => setShowPassword(v => !v)}
|
|
246
|
-
>
|
|
247
|
-
<EyeIcon open={showPassword} />
|
|
248
|
-
</button>
|
|
249
|
-
</div>
|
|
250
|
-
<button style={buttonStyle} type="submit">Login</button>
|
|
251
|
-
</form>
|
|
87
|
+
<TouchableOpacity style={styles.button} onPress={handleLogin}>
|
|
88
|
+
<Text style={styles.buttonText}>Login</Text>
|
|
89
|
+
</TouchableOpacity>
|
|
90
|
+
</View>
|
|
252
91
|
) : (
|
|
253
|
-
<
|
|
254
|
-
<
|
|
255
|
-
style={
|
|
256
|
-
type="text"
|
|
92
|
+
<View>
|
|
93
|
+
<TextInput
|
|
94
|
+
style={styles.input}
|
|
257
95
|
placeholder="Display Name"
|
|
258
96
|
value={displayName}
|
|
259
|
-
|
|
260
|
-
required
|
|
261
|
-
autoComplete="name"
|
|
97
|
+
onChangeText={setDisplayName}
|
|
262
98
|
/>
|
|
263
|
-
<
|
|
264
|
-
style={
|
|
265
|
-
type="text"
|
|
99
|
+
<TextInput
|
|
100
|
+
style={styles.input}
|
|
266
101
|
placeholder="Email"
|
|
267
102
|
value={identifier}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
103
|
+
onChangeText={setIdentifier}
|
|
104
|
+
/>
|
|
105
|
+
<TextInput
|
|
106
|
+
style={styles.input}
|
|
107
|
+
placeholder="Password"
|
|
108
|
+
value={password}
|
|
109
|
+
onChangeText={setPassword}
|
|
110
|
+
secureTextEntry
|
|
271
111
|
/>
|
|
272
|
-
<
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
<EyeIcon open={showPassword} />
|
|
290
|
-
</button>
|
|
291
|
-
</div>
|
|
292
|
-
<div style={inputWrapperStyle}>
|
|
293
|
-
<input
|
|
294
|
-
style={inputStyle}
|
|
295
|
-
type={showConfirmPassword ? "text" : "password"}
|
|
296
|
-
placeholder="Confirm Password"
|
|
297
|
-
value={confirmPassword}
|
|
298
|
-
onChange={e => setConfirmPassword(e.target.value)}
|
|
299
|
-
required
|
|
300
|
-
autoComplete="new-password"
|
|
301
|
-
/>
|
|
302
|
-
<button
|
|
303
|
-
type="button"
|
|
304
|
-
style={toggleButtonStyle}
|
|
305
|
-
tabIndex={-1}
|
|
306
|
-
aria-label={showConfirmPassword ? "Hide confirm password" : "Show confirm password"}
|
|
307
|
-
onClick={() => setShowConfirmPassword(v => !v)}
|
|
308
|
-
>
|
|
309
|
-
<EyeIcon open={showConfirmPassword} />
|
|
310
|
-
</button>
|
|
311
|
-
</div>
|
|
312
|
-
<button style={buttonStyle} type="submit">Register</button>
|
|
313
|
-
</form>
|
|
112
|
+
<TextInput
|
|
113
|
+
style={styles.input}
|
|
114
|
+
placeholder="Confirm Password"
|
|
115
|
+
value={confirmPassword}
|
|
116
|
+
onChangeText={setConfirmPassword}
|
|
117
|
+
secureTextEntry
|
|
118
|
+
/>
|
|
119
|
+
<TouchableOpacity style={styles.button} onPress={handleRegister}>
|
|
120
|
+
<Text style={styles.buttonText}>Register</Text>
|
|
121
|
+
</TouchableOpacity>
|
|
122
|
+
</View>
|
|
123
|
+
)}
|
|
124
|
+
|
|
125
|
+
{message && (
|
|
126
|
+
<View style={[styles.message, success ? styles.success : styles.error]}>
|
|
127
|
+
<Text style={styles.messageText}>{message}</Text>
|
|
128
|
+
</View>
|
|
314
129
|
)}
|
|
315
|
-
|
|
316
|
-
</div>
|
|
130
|
+
</View>
|
|
317
131
|
);
|
|
318
|
-
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const styles = StyleSheet.create({
|
|
135
|
+
container: {
|
|
136
|
+
padding: 24,
|
|
137
|
+
borderRadius: 12,
|
|
138
|
+
backgroundColor: '#fff',
|
|
139
|
+
margin: 16,
|
|
140
|
+
},
|
|
141
|
+
tabContainer: {
|
|
142
|
+
flexDirection: 'row',
|
|
143
|
+
marginBottom: 16,
|
|
144
|
+
},
|
|
145
|
+
tab: {
|
|
146
|
+
flex: 1,
|
|
147
|
+
padding: 12,
|
|
148
|
+
borderBottomWidth: 2,
|
|
149
|
+
borderColor: '#ccc',
|
|
150
|
+
alignItems: 'center',
|
|
151
|
+
},
|
|
152
|
+
activeTab: {
|
|
153
|
+
borderColor: '#667eea',
|
|
154
|
+
},
|
|
155
|
+
tabText: {
|
|
156
|
+
fontWeight: 'bold',
|
|
157
|
+
color: '#667eea',
|
|
158
|
+
},
|
|
159
|
+
input: {
|
|
160
|
+
borderWidth: 1,
|
|
161
|
+
borderColor: '#ccc',
|
|
162
|
+
borderRadius: 8,
|
|
163
|
+
padding: 12,
|
|
164
|
+
marginBottom: 12,
|
|
165
|
+
},
|
|
166
|
+
button: {
|
|
167
|
+
backgroundColor: '#667eea',
|
|
168
|
+
padding: 14,
|
|
169
|
+
borderRadius: 8,
|
|
170
|
+
alignItems: 'center',
|
|
171
|
+
marginTop: 8,
|
|
172
|
+
},
|
|
173
|
+
buttonText: {
|
|
174
|
+
color: '#fff',
|
|
175
|
+
fontWeight: 'bold',
|
|
176
|
+
},
|
|
177
|
+
message: {
|
|
178
|
+
padding: 12,
|
|
179
|
+
borderRadius: 8,
|
|
180
|
+
marginTop: 16,
|
|
181
|
+
},
|
|
182
|
+
success: {
|
|
183
|
+
backgroundColor: '#eafbe7',
|
|
184
|
+
},
|
|
185
|
+
error: {
|
|
186
|
+
backgroundColor: '#fdeaea',
|
|
187
|
+
},
|
|
188
|
+
messageText: {
|
|
189
|
+
textAlign: 'center',
|
|
190
|
+
color: '#333',
|
|
191
|
+
},
|
|
192
|
+
});
|