@umituz/react-native-auth 3.5.5 → 3.5.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.
- package/package.json +2 -2
- package/src/index.ts +0 -1
- package/src/presentation/components/AuthContainer.tsx +6 -8
- package/src/presentation/components/AuthErrorDisplay.tsx +2 -2
- package/src/presentation/components/AuthHeader.tsx +3 -2
- package/src/presentation/components/AuthLegalLinks.tsx +6 -10
- package/src/presentation/components/LoginForm.tsx +44 -48
- package/src/presentation/components/RegisterForm.tsx +93 -98
- package/src/presentation/components/SocialLoginButtons.tsx +24 -76
- package/src/presentation/hooks/useLoginForm.ts +6 -0
- package/src/presentation/hooks/useRegisterForm.ts +6 -0
- package/src/presentation/screens/LoginScreen.tsx +3 -4
- package/src/presentation/screens/RegisterScreen.tsx +3 -4
- package/src/presentation/components/AuthBackground.tsx +0 -21
- package/src/presentation/components/AuthFormCard.tsx +0 -51
- package/src/presentation/components/icons/AppleIconSvg.tsx +0 -24
- package/src/presentation/components/icons/GoogleIconSvg.tsx +0 -32
- package/src/presentation/components/icons/index.ts +0 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.7",
|
|
4
4
|
"description": "Authentication service for React Native apps - Secure, type-safe, and production-ready. Provider-agnostic design with dependency injection, configurable validation, and comprehensive error handling.",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@types/react": "~19.1.0",
|
|
62
62
|
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
63
63
|
"@typescript-eslint/parser": "^7.0.0",
|
|
64
|
-
"@umituz/react-native-design-system": "^2.8.
|
|
64
|
+
"@umituz/react-native-design-system": "^2.8.39",
|
|
65
65
|
"@umituz/react-native-design-system-theme": "*",
|
|
66
66
|
"eslint": "^8.57.0",
|
|
67
67
|
"expo-apple-authentication": "^6.0.0",
|
package/src/index.ts
CHANGED
|
@@ -170,7 +170,6 @@ export type {
|
|
|
170
170
|
// COMPONENTS
|
|
171
171
|
export { AuthContainer } from './presentation/components/AuthContainer';
|
|
172
172
|
export { AuthHeader } from './presentation/components/AuthHeader';
|
|
173
|
-
export { AuthFormCard } from './presentation/components/AuthFormCard';
|
|
174
173
|
export { LoginForm } from './presentation/components/LoginForm';
|
|
175
174
|
export { RegisterForm } from './presentation/components/RegisterForm';
|
|
176
175
|
export { AuthLegalLinks } from './presentation/components/AuthLegalLinks';
|
|
@@ -8,11 +8,8 @@ import {
|
|
|
8
8
|
View,
|
|
9
9
|
StyleSheet,
|
|
10
10
|
ScrollView,
|
|
11
|
-
KeyboardAvoidingView,
|
|
12
11
|
} from "react-native";
|
|
13
|
-
import { useSafeAreaInsets } from "react-native-
|
|
14
|
-
import { useResponsive } from "@umituz/react-native-design-system";
|
|
15
|
-
import { AuthBackground } from "./AuthBackground";
|
|
12
|
+
import { useResponsive, useSafeAreaInsets, AtomicKeyboardAvoidingView, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
16
13
|
|
|
17
14
|
/** Layout constants for auth screens */
|
|
18
15
|
const AUTH_LAYOUT = {
|
|
@@ -29,17 +26,18 @@ export const AuthContainer: React.FC<AuthContainerProps> = ({ children }) => {
|
|
|
29
26
|
const insets = useSafeAreaInsets();
|
|
30
27
|
const { spacingMultiplier } = useResponsive();
|
|
31
28
|
|
|
29
|
+
const tokens = useAppDesignTokens();
|
|
30
|
+
|
|
32
31
|
const dynamicStyles = useMemo(() => ({
|
|
33
32
|
paddingTop: insets.top + (AUTH_LAYOUT.VERTICAL_PADDING * spacingMultiplier),
|
|
34
33
|
paddingBottom: insets.bottom + (AUTH_LAYOUT.VERTICAL_PADDING * spacingMultiplier),
|
|
35
34
|
}), [insets.top, insets.bottom, spacingMultiplier]);
|
|
36
35
|
|
|
37
36
|
return (
|
|
38
|
-
<
|
|
39
|
-
style={styles.container}
|
|
37
|
+
<AtomicKeyboardAvoidingView
|
|
38
|
+
style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
|
|
40
39
|
behavior="padding"
|
|
41
40
|
>
|
|
42
|
-
<AuthBackground />
|
|
43
41
|
<ScrollView
|
|
44
42
|
contentContainerStyle={[styles.scrollContent, dynamicStyles]}
|
|
45
43
|
keyboardShouldPersistTaps="handled"
|
|
@@ -47,7 +45,7 @@ export const AuthContainer: React.FC<AuthContainerProps> = ({ children }) => {
|
|
|
47
45
|
>
|
|
48
46
|
<View style={styles.content}>{children}</View>
|
|
49
47
|
</ScrollView>
|
|
50
|
-
</
|
|
48
|
+
</AtomicKeyboardAvoidingView>
|
|
51
49
|
);
|
|
52
50
|
};
|
|
53
51
|
|
|
@@ -32,7 +32,8 @@ export const AuthErrorDisplay: React.FC<AuthErrorDisplayProps> = ({
|
|
|
32
32
|
>
|
|
33
33
|
<AtomicText
|
|
34
34
|
type="bodyMedium"
|
|
35
|
-
|
|
35
|
+
color="error"
|
|
36
|
+
style={styles.errorText}
|
|
36
37
|
>
|
|
37
38
|
{error}
|
|
38
39
|
</AtomicText>
|
|
@@ -48,7 +49,6 @@ const styles = StyleSheet.create({
|
|
|
48
49
|
borderWidth: 1,
|
|
49
50
|
},
|
|
50
51
|
errorText: {
|
|
51
|
-
fontSize: 14,
|
|
52
52
|
textAlign: "center",
|
|
53
53
|
fontWeight: "500",
|
|
54
54
|
},
|
|
@@ -21,15 +21,16 @@ export const AuthHeader: React.FC<AuthHeaderProps> = ({ title, subtitle }) => {
|
|
|
21
21
|
<View style={[styles.header, { marginBottom: tokens.spacing.xl, paddingHorizontal: tokens.spacing.md }]}>
|
|
22
22
|
<AtomicText
|
|
23
23
|
type="headlineLarge"
|
|
24
|
-
|
|
24
|
+
color="onPrimary"
|
|
25
|
+
style={{ textAlign: "center" }}
|
|
25
26
|
>
|
|
26
27
|
{title}
|
|
27
28
|
</AtomicText>
|
|
28
29
|
{(subtitle || t("auth.subtitle")) && (
|
|
29
30
|
<AtomicText
|
|
30
31
|
type="bodyMedium"
|
|
32
|
+
color="textInverse"
|
|
31
33
|
style={{
|
|
32
|
-
color: tokens.colors.textInverse,
|
|
33
34
|
textAlign: "center",
|
|
34
35
|
marginTop: tokens.spacing.xs,
|
|
35
36
|
}}
|
|
@@ -79,13 +79,11 @@ export const AuthLegalLinks: React.FC<AuthLegalLinksProps> = ({
|
|
|
79
79
|
{hasTerms && (
|
|
80
80
|
<AtomicButton
|
|
81
81
|
variant="text"
|
|
82
|
+
size="sm"
|
|
82
83
|
onPress={handleTermsPress}
|
|
83
84
|
style={styles.linkButton}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
{t("auth.termsOfService")}
|
|
87
|
-
</AtomicText>
|
|
88
|
-
</AtomicButton>
|
|
85
|
+
title={t("auth.termsOfService")}
|
|
86
|
+
/>
|
|
89
87
|
)}
|
|
90
88
|
{hasTerms && hasPrivacy && (
|
|
91
89
|
<AtomicText type="bodySmall" color="secondary" style={styles.separator}>
|
|
@@ -95,13 +93,11 @@ export const AuthLegalLinks: React.FC<AuthLegalLinksProps> = ({
|
|
|
95
93
|
{hasPrivacy && (
|
|
96
94
|
<AtomicButton
|
|
97
95
|
variant="text"
|
|
96
|
+
size="sm"
|
|
98
97
|
onPress={handlePrivacyPress}
|
|
99
98
|
style={styles.linkButton}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{t("auth.privacyPolicy")}
|
|
103
|
-
</AtomicText>
|
|
104
|
-
</AtomicButton>
|
|
99
|
+
title={t("auth.privacyPolicy")}
|
|
100
|
+
/>
|
|
105
101
|
)}
|
|
106
102
|
</View>
|
|
107
103
|
</View>
|
|
@@ -49,54 +49,52 @@ export const LoginForm: React.FC<LoginFormProps> = ({ onNavigateToRegister }) =>
|
|
|
49
49
|
|
|
50
50
|
return (
|
|
51
51
|
<>
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
52
|
+
<AtomicInput
|
|
53
|
+
label={t("auth.email")}
|
|
54
|
+
value={email}
|
|
55
|
+
onChangeText={handleEmailChange}
|
|
56
|
+
placeholder={t("auth.emailPlaceholder")}
|
|
57
|
+
keyboardType="email-address"
|
|
58
|
+
autoCapitalize="none"
|
|
59
|
+
disabled={loading}
|
|
60
|
+
state={emailError ? "error" : "default"}
|
|
61
|
+
helperText={emailError || undefined}
|
|
62
|
+
returnKeyType="next"
|
|
63
|
+
onSubmitEditing={() => passwordRef.current?.focus()}
|
|
64
|
+
blurOnSubmit={false}
|
|
65
|
+
textContentType="emailAddress"
|
|
66
|
+
style={styles.input}
|
|
67
|
+
/>
|
|
68
68
|
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
69
|
+
<AtomicInput
|
|
70
|
+
ref={passwordRef}
|
|
71
|
+
label={t("auth.password")}
|
|
72
|
+
value={password}
|
|
73
|
+
onChangeText={handlePasswordChange}
|
|
74
|
+
placeholder={t("auth.passwordPlaceholder")}
|
|
75
|
+
secureTextEntry
|
|
76
|
+
showPasswordToggle
|
|
77
|
+
autoCapitalize="none"
|
|
78
|
+
disabled={loading}
|
|
79
|
+
state={passwordError ? "error" : "default"}
|
|
80
|
+
helperText={passwordError || undefined}
|
|
81
|
+
returnKeyType="done"
|
|
82
|
+
onSubmitEditing={() => { void handleSignIn(); }}
|
|
83
|
+
textContentType="password"
|
|
84
|
+
style={styles.input}
|
|
85
|
+
/>
|
|
86
86
|
|
|
87
87
|
<AuthErrorDisplay error={displayError} />
|
|
88
88
|
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
</AtomicButton>
|
|
99
|
-
</View>
|
|
89
|
+
<AtomicButton
|
|
90
|
+
variant="primary"
|
|
91
|
+
onPress={() => { void handleSignIn(); }}
|
|
92
|
+
disabled={loading || !email.trim() || !password.trim()}
|
|
93
|
+
fullWidth
|
|
94
|
+
style={styles.signInButton}
|
|
95
|
+
>
|
|
96
|
+
{t("auth.signIn")}
|
|
97
|
+
</AtomicButton>
|
|
100
98
|
|
|
101
99
|
<AuthLink
|
|
102
100
|
text={t("auth.dontHaveAccount")}
|
|
@@ -109,13 +107,11 @@ export const LoginForm: React.FC<LoginFormProps> = ({ onNavigateToRegister }) =>
|
|
|
109
107
|
};
|
|
110
108
|
|
|
111
109
|
const styles = StyleSheet.create({
|
|
112
|
-
|
|
110
|
+
input: {
|
|
113
111
|
marginBottom: 20,
|
|
114
112
|
},
|
|
115
|
-
buttonContainer: {
|
|
116
|
-
marginBottom: 16,
|
|
117
|
-
},
|
|
118
113
|
signInButton: {
|
|
119
114
|
minHeight: 52,
|
|
115
|
+
marginBottom: 16,
|
|
120
116
|
},
|
|
121
117
|
});
|
|
@@ -53,105 +53,102 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
|
|
53
53
|
|
|
54
54
|
return (
|
|
55
55
|
<>
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
</View>
|
|
56
|
+
<AtomicInput
|
|
57
|
+
label={t("auth.displayName")}
|
|
58
|
+
value={displayName}
|
|
59
|
+
onChangeText={handleDisplayNameChange}
|
|
60
|
+
placeholder={
|
|
61
|
+
t("auth.displayNamePlaceholder")
|
|
62
|
+
}
|
|
63
|
+
autoCapitalize="words"
|
|
64
|
+
disabled={loading}
|
|
65
|
+
state={fieldErrors.displayName ? "error" : "default"}
|
|
66
|
+
helperText={fieldErrors.displayName || undefined}
|
|
67
|
+
returnKeyType="next"
|
|
68
|
+
onSubmitEditing={() => emailRef.current?.focus()}
|
|
69
|
+
blurOnSubmit={false}
|
|
70
|
+
style={styles.input}
|
|
71
|
+
/>
|
|
73
72
|
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
73
|
+
<AtomicInput
|
|
74
|
+
ref={emailRef}
|
|
75
|
+
label={t("auth.email")}
|
|
76
|
+
value={email}
|
|
77
|
+
onChangeText={handleEmailChange}
|
|
78
|
+
placeholder={t("auth.emailPlaceholder")}
|
|
79
|
+
keyboardType="email-address"
|
|
80
|
+
autoCapitalize="none"
|
|
81
|
+
disabled={loading}
|
|
82
|
+
state={fieldErrors.email ? "error" : "default"}
|
|
83
|
+
helperText={fieldErrors.email || undefined}
|
|
84
|
+
returnKeyType="next"
|
|
85
|
+
onSubmitEditing={() => passwordRef.current?.focus()}
|
|
86
|
+
blurOnSubmit={false}
|
|
87
|
+
textContentType="emailAddress"
|
|
88
|
+
style={styles.input}
|
|
89
|
+
/>
|
|
91
90
|
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
91
|
+
<AtomicInput
|
|
92
|
+
ref={passwordRef}
|
|
93
|
+
label={t("auth.password")}
|
|
94
|
+
value={password}
|
|
95
|
+
onChangeText={handlePasswordChange}
|
|
96
|
+
placeholder={t("auth.passwordPlaceholder")}
|
|
97
|
+
secureTextEntry
|
|
98
|
+
showPasswordToggle
|
|
99
|
+
autoCapitalize="none"
|
|
100
|
+
disabled={loading}
|
|
101
|
+
state={fieldErrors.password ? "error" : "default"}
|
|
102
|
+
helperText={fieldErrors.password || undefined}
|
|
103
|
+
returnKeyType="next"
|
|
104
|
+
onSubmitEditing={() => confirmPasswordRef.current?.focus()}
|
|
105
|
+
blurOnSubmit={false}
|
|
106
|
+
textContentType="newPassword"
|
|
107
|
+
style={styles.input}
|
|
108
|
+
/>
|
|
109
|
+
{password.length > 0 && (
|
|
110
|
+
<PasswordStrengthIndicator requirements={passwordRequirements} />
|
|
111
|
+
)}
|
|
113
112
|
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
113
|
+
<AtomicInput
|
|
114
|
+
ref={confirmPasswordRef}
|
|
115
|
+
label={t("auth.confirmPassword")}
|
|
116
|
+
value={confirmPassword}
|
|
117
|
+
onChangeText={handleConfirmPasswordChange}
|
|
118
|
+
placeholder={
|
|
119
|
+
t("auth.confirmPasswordPlaceholder")
|
|
120
|
+
}
|
|
121
|
+
secureTextEntry
|
|
122
|
+
showPasswordToggle
|
|
123
|
+
autoCapitalize="none"
|
|
124
|
+
disabled={loading}
|
|
125
|
+
state={fieldErrors.confirmPassword ? "error" : "default"}
|
|
126
|
+
helperText={fieldErrors.confirmPassword || undefined}
|
|
127
|
+
returnKeyType="done"
|
|
128
|
+
onSubmitEditing={() => { void handleSignUp(); }}
|
|
129
|
+
textContentType="newPassword"
|
|
130
|
+
style={styles.input}
|
|
131
|
+
/>
|
|
132
|
+
{confirmPassword.length > 0 && (
|
|
133
|
+
<PasswordMatchIndicator isMatch={passwordsMatch} />
|
|
134
|
+
)}
|
|
136
135
|
|
|
137
136
|
<AuthErrorDisplay error={displayError} />
|
|
138
137
|
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
</AtomicButton>
|
|
154
|
-
</View>
|
|
138
|
+
<AtomicButton
|
|
139
|
+
variant="primary"
|
|
140
|
+
onPress={() => { void handleSignUp(); }}
|
|
141
|
+
disabled={
|
|
142
|
+
loading ||
|
|
143
|
+
!email.trim() ||
|
|
144
|
+
!password.trim() ||
|
|
145
|
+
!confirmPassword.trim()
|
|
146
|
+
}
|
|
147
|
+
fullWidth
|
|
148
|
+
style={styles.signUpButton}
|
|
149
|
+
>
|
|
150
|
+
{t("auth.signUp")}
|
|
151
|
+
</AtomicButton>
|
|
155
152
|
|
|
156
153
|
<AuthLink
|
|
157
154
|
text={t("auth.alreadyHaveAccount")}
|
|
@@ -172,15 +169,13 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
|
|
172
169
|
};
|
|
173
170
|
|
|
174
171
|
const styles = StyleSheet.create({
|
|
175
|
-
|
|
172
|
+
input: {
|
|
176
173
|
marginBottom: 20,
|
|
177
174
|
},
|
|
178
|
-
buttonContainer: {
|
|
179
|
-
marginBottom: 16,
|
|
180
|
-
marginTop: 8,
|
|
181
|
-
},
|
|
182
175
|
signUpButton: {
|
|
183
176
|
minHeight: 52,
|
|
177
|
+
marginBottom: 16,
|
|
178
|
+
marginTop: 8,
|
|
184
179
|
},
|
|
185
180
|
});
|
|
186
181
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
|
-
TouchableOpacity,
|
|
5
4
|
StyleSheet,
|
|
6
5
|
Platform,
|
|
7
6
|
} from "react-native";
|
|
8
|
-
import { useAppDesignTokens,
|
|
7
|
+
import { useAppDesignTokens, Divider, AtomicButton } from "@umituz/react-native-design-system";
|
|
9
8
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
10
9
|
import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
|
|
11
10
|
|
|
@@ -45,61 +44,35 @@ export const SocialLoginButtons: React.FC<SocialLoginButtonsProps> = ({
|
|
|
45
44
|
|
|
46
45
|
return (
|
|
47
46
|
<View style={styles.container}>
|
|
48
|
-
<
|
|
49
|
-
<View style={[styles.divider, { backgroundColor: tokens.colors.border }]} />
|
|
50
|
-
<AtomicText type="bodySmall" color="secondary" style={styles.dividerText}>
|
|
51
|
-
{t("auth.orContinueWith")}
|
|
52
|
-
</AtomicText>
|
|
53
|
-
<View style={[styles.divider, { backgroundColor: tokens.colors.border }]} />
|
|
54
|
-
</View>
|
|
47
|
+
<Divider text={t("auth.orContinueWith")} spacing="large" />
|
|
55
48
|
|
|
56
49
|
<View style={styles.buttonsContainer}>
|
|
57
50
|
{showGoogle && (
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
activeOpacity={0.7}
|
|
51
|
+
<AtomicButton
|
|
52
|
+
variant="outline"
|
|
53
|
+
onPress={onGooglePress || (() => {})}
|
|
54
|
+
loading={googleLoading}
|
|
55
|
+
disabled={disabled}
|
|
56
|
+
icon="logo-google"
|
|
57
|
+
fullWidth
|
|
58
|
+
style={styles.socialButton}
|
|
67
59
|
>
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
) : (
|
|
71
|
-
<>
|
|
72
|
-
<AtomicIcon name="logo-google" size="sm" />
|
|
73
|
-
<AtomicText style={[styles.buttonText, { color: tokens.colors.textPrimary }]}>
|
|
74
|
-
{t("auth.google")}
|
|
75
|
-
</AtomicText>
|
|
76
|
-
</>
|
|
77
|
-
)}
|
|
78
|
-
</TouchableOpacity>
|
|
60
|
+
{t("auth.google")}
|
|
61
|
+
</AtomicButton>
|
|
79
62
|
)}
|
|
80
63
|
|
|
81
64
|
{showApple && (
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
activeOpacity={0.7}
|
|
65
|
+
<AtomicButton
|
|
66
|
+
variant="outline"
|
|
67
|
+
onPress={onApplePress || (() => {})}
|
|
68
|
+
loading={appleLoading}
|
|
69
|
+
disabled={disabled}
|
|
70
|
+
icon="logo-apple"
|
|
71
|
+
fullWidth
|
|
72
|
+
style={styles.socialButton}
|
|
91
73
|
>
|
|
92
|
-
{
|
|
93
|
-
|
|
94
|
-
) : (
|
|
95
|
-
<>
|
|
96
|
-
<AtomicIcon name="logo-apple" size="sm" color="onSurface" />
|
|
97
|
-
<AtomicText style={[styles.buttonText, { color: tokens.colors.textPrimary }]}>
|
|
98
|
-
{t("auth.apple")}
|
|
99
|
-
</AtomicText>
|
|
100
|
-
</>
|
|
101
|
-
)}
|
|
102
|
-
</TouchableOpacity>
|
|
74
|
+
{t("auth.apple")}
|
|
75
|
+
</AtomicButton>
|
|
103
76
|
)}
|
|
104
77
|
</View>
|
|
105
78
|
</View>
|
|
@@ -110,38 +83,13 @@ const styles = StyleSheet.create({
|
|
|
110
83
|
container: {
|
|
111
84
|
marginTop: 24,
|
|
112
85
|
},
|
|
113
|
-
dividerContainer: {
|
|
114
|
-
flexDirection: "row",
|
|
115
|
-
alignItems: "center",
|
|
116
|
-
marginBottom: 20,
|
|
117
|
-
},
|
|
118
|
-
divider: {
|
|
119
|
-
flex: 1,
|
|
120
|
-
height: 1,
|
|
121
|
-
},
|
|
122
|
-
dividerText: {
|
|
123
|
-
marginHorizontal: 16,
|
|
124
|
-
},
|
|
125
86
|
buttonsContainer: {
|
|
126
|
-
flexDirection:
|
|
87
|
+
flexDirection: 'row',
|
|
88
|
+
marginTop: 8,
|
|
127
89
|
gap: 12,
|
|
128
90
|
},
|
|
129
91
|
socialButton: {
|
|
130
92
|
flex: 1,
|
|
131
|
-
flexDirection: "row",
|
|
132
|
-
alignItems: "center",
|
|
133
|
-
justifyContent: "center",
|
|
134
|
-
paddingVertical: 14,
|
|
135
|
-
borderRadius: 12,
|
|
136
|
-
borderWidth: 1,
|
|
137
|
-
gap: 8,
|
|
138
|
-
},
|
|
139
|
-
disabledButton: {
|
|
140
|
-
opacity: 0.5,
|
|
141
|
-
},
|
|
142
|
-
buttonText: {
|
|
143
|
-
fontSize: 16,
|
|
144
|
-
fontWeight: "600",
|
|
145
93
|
},
|
|
146
94
|
});
|
|
147
95
|
|
|
@@ -8,6 +8,7 @@ import { useLocalization } from "@umituz/react-native-localization";
|
|
|
8
8
|
import { useAuth } from "./useAuth";
|
|
9
9
|
import { getAuthErrorLocalizationKey } from "../utils/getAuthErrorMessage";
|
|
10
10
|
import { validateEmail, validatePasswordForLogin } from "../../infrastructure/utils/AuthValidation";
|
|
11
|
+
import { AlertService, alertService } from "@umituz/react-native-design-system";
|
|
11
12
|
|
|
12
13
|
export interface UseLoginFormResult {
|
|
13
14
|
email: string;
|
|
@@ -74,6 +75,11 @@ export function useLoginForm(): UseLoginFormResult {
|
|
|
74
75
|
|
|
75
76
|
try {
|
|
76
77
|
await signIn(email.trim(), password);
|
|
78
|
+
|
|
79
|
+
alertService.success(
|
|
80
|
+
t("auth.successTitle"),
|
|
81
|
+
t("auth.signInSuccess")
|
|
82
|
+
);
|
|
77
83
|
} catch (err: unknown) {
|
|
78
84
|
const localizationKey = getAuthErrorLocalizationKey(err);
|
|
79
85
|
const errorMessage = t(localizationKey);
|
|
@@ -14,6 +14,7 @@ import { DEFAULT_PASSWORD_CONFIG } from "../../domain/value-objects/AuthConfig";
|
|
|
14
14
|
import { useAuth } from "./useAuth";
|
|
15
15
|
import { getAuthErrorLocalizationKey } from "../utils/getAuthErrorMessage";
|
|
16
16
|
import type { PasswordRequirements } from "../../infrastructure/utils/AuthValidation";
|
|
17
|
+
import { AlertService, alertService } from "@umituz/react-native-design-system";
|
|
17
18
|
|
|
18
19
|
export interface UseRegisterFormResult {
|
|
19
20
|
displayName: string;
|
|
@@ -154,6 +155,11 @@ export function useRegisterForm(): UseRegisterFormResult {
|
|
|
154
155
|
password,
|
|
155
156
|
displayName.trim() || undefined,
|
|
156
157
|
);
|
|
158
|
+
|
|
159
|
+
alertService.success(
|
|
160
|
+
t("auth.successTitle"),
|
|
161
|
+
t("auth.signUpSuccess")
|
|
162
|
+
);
|
|
157
163
|
} catch (err: unknown) {
|
|
158
164
|
const localizationKey = getAuthErrorLocalizationKey(err);
|
|
159
165
|
const errorMessage = t(localizationKey);
|
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
7
|
+
import { useAppNavigation, AtomicCard } from "@umituz/react-native-design-system";
|
|
8
8
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
9
9
|
import type { AuthStackParamList } from "../navigation/AuthNavigator";
|
|
10
10
|
import { AuthContainer } from "../components/AuthContainer";
|
|
11
11
|
import { AuthHeader } from "../components/AuthHeader";
|
|
12
|
-
import { AuthFormCard } from "../components/AuthFormCard";
|
|
13
12
|
import { LoginForm } from "../components/LoginForm";
|
|
14
13
|
|
|
15
14
|
export const LoginScreen: React.FC = () => {
|
|
@@ -23,9 +22,9 @@ export const LoginScreen: React.FC = () => {
|
|
|
23
22
|
return (
|
|
24
23
|
<AuthContainer>
|
|
25
24
|
<AuthHeader title={t("auth.title")} />
|
|
26
|
-
<
|
|
25
|
+
<AtomicCard variant="elevated" padding="large">
|
|
27
26
|
<LoginForm onNavigateToRegister={handleNavigateToRegister} />
|
|
28
|
-
</
|
|
27
|
+
</AtomicCard>
|
|
29
28
|
</AuthContainer>
|
|
30
29
|
);
|
|
31
30
|
};
|
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
7
|
+
import { useAppNavigation, AtomicCard } from "@umituz/react-native-design-system";
|
|
8
8
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
9
9
|
import type { AuthStackParamList } from "../navigation/AuthNavigator";
|
|
10
10
|
import { AuthContainer } from "../components/AuthContainer";
|
|
11
11
|
import { AuthHeader } from "../components/AuthHeader";
|
|
12
|
-
import { AuthFormCard } from "../components/AuthFormCard";
|
|
13
12
|
import { RegisterForm } from "../components/RegisterForm";
|
|
14
13
|
|
|
15
14
|
export interface RegisterScreenProps {
|
|
@@ -35,7 +34,7 @@ export const RegisterScreen: React.FC<RegisterScreenProps> = ({
|
|
|
35
34
|
return (
|
|
36
35
|
<AuthContainer>
|
|
37
36
|
<AuthHeader title={t("auth.createAccount")} />
|
|
38
|
-
<
|
|
37
|
+
<AtomicCard variant="elevated" padding="large">
|
|
39
38
|
<RegisterForm
|
|
40
39
|
onNavigateToLogin={handleNavigateToLogin}
|
|
41
40
|
termsUrl={termsUrl}
|
|
@@ -43,7 +42,7 @@ export const RegisterScreen: React.FC<RegisterScreenProps> = ({
|
|
|
43
42
|
onTermsPress={onTermsPress}
|
|
44
43
|
onPrivacyPress={onPrivacyPress}
|
|
45
44
|
/>
|
|
46
|
-
</
|
|
45
|
+
</AtomicCard>
|
|
47
46
|
</AuthContainer>
|
|
48
47
|
);
|
|
49
48
|
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth Background Component
|
|
3
|
-
* Standard background for auth screens
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { StyleSheet, View } from "react-native";
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
9
|
-
|
|
10
|
-
export const AuthBackground: React.FC = () => {
|
|
11
|
-
const tokens = useAppDesignTokens();
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<View
|
|
15
|
-
style={[
|
|
16
|
-
StyleSheet.absoluteFill,
|
|
17
|
-
{ backgroundColor: tokens.colors.backgroundPrimary }
|
|
18
|
-
]}
|
|
19
|
-
/>
|
|
20
|
-
);
|
|
21
|
-
};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth Form Card Component
|
|
3
|
-
* Reusable card container for auth forms
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { View, StyleSheet } from "react-native";
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
9
|
-
|
|
10
|
-
interface AuthFormCardProps {
|
|
11
|
-
children: React.ReactNode;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const AuthFormCard: React.FC<AuthFormCardProps> = ({ children }) => {
|
|
15
|
-
const tokens = useAppDesignTokens();
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<View
|
|
19
|
-
style={[
|
|
20
|
-
styles.formCard,
|
|
21
|
-
{
|
|
22
|
-
backgroundColor: tokens.colors.surface,
|
|
23
|
-
borderRadius: tokens.borders.radius.xl,
|
|
24
|
-
padding: tokens.spacing.lg,
|
|
25
|
-
},
|
|
26
|
-
]}
|
|
27
|
-
>
|
|
28
|
-
<View style={styles.form}>{children}</View>
|
|
29
|
-
</View>
|
|
30
|
-
);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const styles = StyleSheet.create({
|
|
34
|
-
formCard: {
|
|
35
|
-
width: "100%",
|
|
36
|
-
},
|
|
37
|
-
form: {
|
|
38
|
-
width: "100%",
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Apple Icon SVG
|
|
3
|
-
* Dark mode compatible Apple logo
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import Svg, { Path } from "react-native-svg";
|
|
8
|
-
|
|
9
|
-
export interface AppleIconSvgProps {
|
|
10
|
-
size?: number;
|
|
11
|
-
color: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const AppleIconSvg: React.FC<AppleIconSvgProps> = ({
|
|
15
|
-
size = 20,
|
|
16
|
-
color,
|
|
17
|
-
}) => (
|
|
18
|
-
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
19
|
-
<Path
|
|
20
|
-
d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.53 4.09l-.02-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"
|
|
21
|
-
fill={color}
|
|
22
|
-
/>
|
|
23
|
-
</Svg>
|
|
24
|
-
);
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Google Icon SVG
|
|
3
|
-
* Standard Google logo with color
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import Svg, { Path } from "react-native-svg";
|
|
8
|
-
|
|
9
|
-
export interface GoogleIconSvgProps {
|
|
10
|
-
size?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const GoogleIconSvg: React.FC<GoogleIconSvgProps> = ({ size = 20 }) => (
|
|
14
|
-
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
15
|
-
<Path
|
|
16
|
-
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
17
|
-
fill="#4285F4"
|
|
18
|
-
/>
|
|
19
|
-
<Path
|
|
20
|
-
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
21
|
-
fill="#34A853"
|
|
22
|
-
/>
|
|
23
|
-
<Path
|
|
24
|
-
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
25
|
-
fill="#FBBC05"
|
|
26
|
-
/>
|
|
27
|
-
<Path
|
|
28
|
-
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
29
|
-
fill="#EA4335"
|
|
30
|
-
/>
|
|
31
|
-
</Svg>
|
|
32
|
-
);
|