@umituz/react-native-auth 2.7.3 → 2.7.5
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 +33 -9
- package/src/application/ports/IAuthRepository.ts +11 -0
- package/src/infrastructure/adapters/StorageProviderAdapter.ts +26 -10
- package/src/infrastructure/adapters/UIProviderAdapter.ts +20 -9
- package/src/infrastructure/providers/FirebaseAuthProvider.ts +3 -2
- package/src/infrastructure/repositories/AuthRepository.ts +91 -0
- package/src/infrastructure/services/AuthPackage.ts +14 -6
- package/src/infrastructure/services/AuthService.ts +67 -119
- package/src/infrastructure/services/GuestModeService.ts +1 -6
- package/src/infrastructure/utils/AuthValidation.ts +15 -14
- package/src/infrastructure/utils/auth-tracker.util.ts +28 -0
- package/src/presentation/components/AccountActions.tsx +38 -50
- package/src/presentation/components/AuthBottomSheet.tsx +4 -4
- package/src/presentation/components/AuthDivider.tsx +0 -1
- package/src/presentation/components/AuthGradientBackground.tsx +1 -1
- package/src/presentation/components/AuthLegalLinks.tsx +7 -8
- package/src/presentation/components/EditProfileActions.tsx +53 -0
- package/src/presentation/components/EditProfileAvatar.tsx +33 -0
- package/src/presentation/components/EditProfileForm.tsx +55 -0
- package/src/presentation/components/LoginForm.tsx +1 -1
- package/src/presentation/components/PasswordMatchIndicator.tsx +6 -14
- package/src/presentation/components/PasswordStrengthIndicator.tsx +11 -17
- package/src/presentation/components/ProfileBenefitsList.tsx +47 -0
- package/src/presentation/components/ProfileSection.tsx +20 -85
- package/src/presentation/components/RegisterForm.tsx +6 -6
- package/src/presentation/components/SocialLoginButtons.tsx +11 -15
- package/src/presentation/hooks/mutations/useAuthMutations.ts +50 -0
- package/src/presentation/hooks/useAccountManagement.ts +2 -0
- package/src/presentation/hooks/useAuthActions.ts +19 -35
- package/src/presentation/hooks/useAuthState.ts +4 -1
- package/src/presentation/hooks/useLoginForm.ts +6 -8
- package/src/presentation/hooks/useProfileUpdate.ts +7 -7
- package/src/presentation/hooks/useRegisterForm.ts +16 -17
- package/src/presentation/hooks/useUserProfile.ts +3 -3
- package/src/presentation/navigation/AuthNavigator.tsx +10 -6
- package/src/presentation/screens/AccountScreen.tsx +9 -1
- package/src/presentation/screens/EditProfileScreen.tsx +40 -185
- package/src/presentation/screens/LoginScreen.tsx +4 -6
- package/src/presentation/screens/RegisterScreen.tsx +4 -6
- package/src/presentation/stores/authModalStore.ts +2 -1
- package/src/types/external.d.ts +31 -45
- package/src/infrastructure/services/AuthCoreService.ts +0 -138
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import React from "react";
|
|
7
7
|
import { View, StyleSheet, Linking } from "react-native";
|
|
8
8
|
import { AtomicButton, AtomicText } from "@umituz/react-native-design-system";
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
11
11
|
|
|
12
12
|
export interface AuthLegalLinksProps {
|
|
@@ -39,22 +39,21 @@ export const AuthLegalLinks: React.FC<AuthLegalLinksProps> = ({
|
|
|
39
39
|
onPrivacyPress,
|
|
40
40
|
prefixText,
|
|
41
41
|
}) => {
|
|
42
|
-
const tokens = useAppDesignTokens();
|
|
43
42
|
const { t } = useLocalization();
|
|
44
43
|
|
|
45
|
-
const handleTermsPress =
|
|
44
|
+
const handleTermsPress = () => {
|
|
46
45
|
if (onTermsPress) {
|
|
47
46
|
onTermsPress();
|
|
48
47
|
} else if (termsUrl) {
|
|
49
|
-
|
|
48
|
+
void Linking.openURL(termsUrl);
|
|
50
49
|
}
|
|
51
50
|
};
|
|
52
51
|
|
|
53
|
-
const handlePrivacyPress =
|
|
52
|
+
const handlePrivacyPress = () => {
|
|
54
53
|
if (onPrivacyPress) {
|
|
55
54
|
onPrivacyPress();
|
|
56
55
|
} else if (privacyUrl) {
|
|
57
|
-
|
|
56
|
+
void Linking.openURL(privacyUrl);
|
|
58
57
|
}
|
|
59
58
|
};
|
|
60
59
|
|
|
@@ -84,7 +83,7 @@ export const AuthLegalLinks: React.FC<AuthLegalLinksProps> = ({
|
|
|
84
83
|
style={styles.linkButton}
|
|
85
84
|
>
|
|
86
85
|
<AtomicText type="bodySmall" color="primary">
|
|
87
|
-
{t("auth.termsOfService")
|
|
86
|
+
{t("auth.termsOfService")}
|
|
88
87
|
</AtomicText>
|
|
89
88
|
</AtomicButton>
|
|
90
89
|
)}
|
|
@@ -100,7 +99,7 @@ export const AuthLegalLinks: React.FC<AuthLegalLinksProps> = ({
|
|
|
100
99
|
style={styles.linkButton}
|
|
101
100
|
>
|
|
102
101
|
<AtomicText type="bodySmall" color="primary">
|
|
103
|
-
{t("auth.privacyPolicy")
|
|
102
|
+
{t("auth.privacyPolicy")}
|
|
104
103
|
</AtomicText>
|
|
105
104
|
</AtomicButton>
|
|
106
105
|
)}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
|
+
import { AtomicButton } from "@umituz/react-native-design-system";
|
|
4
|
+
|
|
5
|
+
export interface EditProfileActionsProps {
|
|
6
|
+
isSaving?: boolean;
|
|
7
|
+
onSave: () => void;
|
|
8
|
+
onCancel?: () => void;
|
|
9
|
+
labels: {
|
|
10
|
+
saveButton: string;
|
|
11
|
+
cancelButton: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const EditProfileActions: React.FC<EditProfileActionsProps> = ({
|
|
16
|
+
isSaving,
|
|
17
|
+
onSave,
|
|
18
|
+
onCancel,
|
|
19
|
+
labels,
|
|
20
|
+
}) => {
|
|
21
|
+
return (
|
|
22
|
+
<View style={styles.actions}>
|
|
23
|
+
<AtomicButton
|
|
24
|
+
variant="primary"
|
|
25
|
+
onPress={onSave}
|
|
26
|
+
disabled={isSaving}
|
|
27
|
+
fullWidth
|
|
28
|
+
>
|
|
29
|
+
{labels.saveButton}
|
|
30
|
+
</AtomicButton>
|
|
31
|
+
|
|
32
|
+
{onCancel && (
|
|
33
|
+
<AtomicButton
|
|
34
|
+
variant="outline"
|
|
35
|
+
onPress={onCancel}
|
|
36
|
+
disabled={isSaving}
|
|
37
|
+
fullWidth
|
|
38
|
+
>
|
|
39
|
+
{labels.cancelButton}
|
|
40
|
+
</AtomicButton>
|
|
41
|
+
)}
|
|
42
|
+
</View>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const styles = StyleSheet.create({
|
|
48
|
+
actions: {
|
|
49
|
+
marginTop: 32,
|
|
50
|
+
gap: 12,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
|
+
import { AtomicAvatar } from "@umituz/react-native-design-system";
|
|
4
|
+
|
|
5
|
+
export interface EditProfileAvatarProps {
|
|
6
|
+
photoURL: string | null;
|
|
7
|
+
displayName: string;
|
|
8
|
+
onPress?: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const EditProfileAvatar: React.FC<EditProfileAvatarProps> = ({
|
|
12
|
+
photoURL,
|
|
13
|
+
displayName,
|
|
14
|
+
}) => {
|
|
15
|
+
return (
|
|
16
|
+
<View style={styles.avatarContainer}>
|
|
17
|
+
<AtomicAvatar
|
|
18
|
+
source={photoURL ? { uri: photoURL } : undefined}
|
|
19
|
+
name={displayName}
|
|
20
|
+
size="xl"
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
</View>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const styles = StyleSheet.create({
|
|
28
|
+
avatarContainer: {
|
|
29
|
+
alignItems: "center",
|
|
30
|
+
marginBottom: 24,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
|
+
import { AtomicInput } from "@umituz/react-native-design-system";
|
|
4
|
+
|
|
5
|
+
export interface EditProfileFormProps {
|
|
6
|
+
displayName: string;
|
|
7
|
+
email: string;
|
|
8
|
+
onChangeDisplayName: (value: string) => void;
|
|
9
|
+
onChangeEmail: (value: string) => void;
|
|
10
|
+
labels: {
|
|
11
|
+
displayNameLabel: string;
|
|
12
|
+
displayNamePlaceholder: string;
|
|
13
|
+
emailLabel: string;
|
|
14
|
+
emailPlaceholder: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const EditProfileForm: React.FC<EditProfileFormProps> = ({
|
|
19
|
+
displayName,
|
|
20
|
+
email,
|
|
21
|
+
onChangeDisplayName,
|
|
22
|
+
onChangeEmail,
|
|
23
|
+
labels,
|
|
24
|
+
}) => {
|
|
25
|
+
return (
|
|
26
|
+
<View>
|
|
27
|
+
<View style={styles.field}>
|
|
28
|
+
<AtomicInput
|
|
29
|
+
label={labels.displayNameLabel}
|
|
30
|
+
value={displayName}
|
|
31
|
+
onChangeText={onChangeDisplayName}
|
|
32
|
+
placeholder={labels.displayNamePlaceholder}
|
|
33
|
+
/>
|
|
34
|
+
</View>
|
|
35
|
+
|
|
36
|
+
<View style={styles.field}>
|
|
37
|
+
<AtomicInput
|
|
38
|
+
label={labels.emailLabel}
|
|
39
|
+
value={email}
|
|
40
|
+
onChangeText={onChangeEmail}
|
|
41
|
+
placeholder={labels.emailPlaceholder}
|
|
42
|
+
keyboardType="email-address"
|
|
43
|
+
autoCapitalize="none"
|
|
44
|
+
/>
|
|
45
|
+
</View>
|
|
46
|
+
</View>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const styles = StyleSheet.create({
|
|
51
|
+
field: {
|
|
52
|
+
marginBottom: 20,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
@@ -64,7 +64,7 @@ export const LoginForm: React.FC<LoginFormProps> = ({ onNavigateToRegister }) =>
|
|
|
64
64
|
<View style={styles.buttonContainer}>
|
|
65
65
|
<AtomicButton
|
|
66
66
|
variant="primary"
|
|
67
|
-
onPress={handleSignIn}
|
|
67
|
+
onPress={() => { void handleSignIn(); }}
|
|
68
68
|
disabled={loading || !email.trim() || !password.trim()}
|
|
69
69
|
fullWidth
|
|
70
70
|
style={styles.signInButton}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Password Match Indicator Component
|
|
3
|
-
* Shows whether passwords match
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import React from "react";
|
|
7
|
-
import { View,
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
|
+
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
4
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
10
5
|
|
|
11
6
|
export interface PasswordMatchIndicatorProps {
|
|
@@ -20,13 +15,13 @@ export const PasswordMatchIndicator: React.FC<PasswordMatchIndicatorProps> = ({
|
|
|
20
15
|
|
|
21
16
|
const color = isMatch ? tokens.colors.success : tokens.colors.error;
|
|
22
17
|
const text = isMatch
|
|
23
|
-
? t("auth.passwordsMatch"
|
|
24
|
-
: t("auth.passwordsDontMatch"
|
|
18
|
+
? t("auth.passwordsMatch")
|
|
19
|
+
: t("auth.passwordsDontMatch");
|
|
25
20
|
|
|
26
21
|
return (
|
|
27
22
|
<View style={styles.container}>
|
|
28
23
|
<View style={[styles.dot, { backgroundColor: color }]} />
|
|
29
|
-
<
|
|
24
|
+
<AtomicText type="labelSmall" style={{ color }}>{text}</AtomicText>
|
|
30
25
|
</View>
|
|
31
26
|
);
|
|
32
27
|
};
|
|
@@ -43,8 +38,5 @@ const styles = StyleSheet.create({
|
|
|
43
38
|
height: 6,
|
|
44
39
|
borderRadius: 3,
|
|
45
40
|
},
|
|
46
|
-
text: {
|
|
47
|
-
fontSize: 12,
|
|
48
|
-
fontWeight: "500",
|
|
49
|
-
},
|
|
50
41
|
});
|
|
42
|
+
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Password Strength Indicator Component
|
|
3
|
-
* Shows password requirements with visual feedback
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import React from "react";
|
|
7
|
-
import { View,
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
|
+
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
4
|
+
import { useLocalization } from "@umituz/react-native-localization";
|
|
9
5
|
import type { PasswordRequirements } from "../../infrastructure/utils/AuthValidation";
|
|
10
6
|
|
|
11
7
|
export interface PasswordStrengthIndicatorProps {
|
|
@@ -31,7 +27,7 @@ const RequirementDot: React.FC<RequirementDotProps> = ({
|
|
|
31
27
|
return (
|
|
32
28
|
<View style={styles.requirement}>
|
|
33
29
|
<View style={[styles.dot, { backgroundColor: color }]} />
|
|
34
|
-
<
|
|
30
|
+
<AtomicText type="labelSmall" style={{ color }}>{label}</AtomicText>
|
|
35
31
|
</View>
|
|
36
32
|
);
|
|
37
33
|
};
|
|
@@ -40,15 +36,16 @@ export const PasswordStrengthIndicator: React.FC<
|
|
|
40
36
|
PasswordStrengthIndicatorProps
|
|
41
37
|
> = ({ requirements, showLabels = true }) => {
|
|
42
38
|
const tokens = useAppDesignTokens();
|
|
39
|
+
const { t } = useLocalization();
|
|
43
40
|
const successColor = tokens.colors.success;
|
|
44
41
|
const pendingColor = tokens.colors.textTertiary;
|
|
45
42
|
|
|
46
43
|
const items = [
|
|
47
|
-
{ key: "minLength", label: "
|
|
48
|
-
{ key: "uppercase", label: "
|
|
49
|
-
{ key: "lowercase", label: "
|
|
50
|
-
{ key: "number", label: "
|
|
51
|
-
{ key: "special", label: "
|
|
44
|
+
{ key: "minLength", label: t("auth.passwordReq.minLength"), isValid: requirements.hasMinLength },
|
|
45
|
+
{ key: "uppercase", label: t("auth.passwordReq.uppercase"), isValid: requirements.hasUppercase },
|
|
46
|
+
{ key: "lowercase", label: t("auth.passwordReq.lowercase"), isValid: requirements.hasLowercase },
|
|
47
|
+
{ key: "number", label: t("auth.passwordReq.number"), isValid: requirements.hasNumber },
|
|
48
|
+
{ key: "special", label: t("auth.passwordReq.special"), isValid: requirements.hasSpecialChar },
|
|
52
49
|
];
|
|
53
50
|
|
|
54
51
|
if (!showLabels) {
|
|
@@ -111,8 +108,5 @@ const styles = StyleSheet.create({
|
|
|
111
108
|
height: 8,
|
|
112
109
|
borderRadius: 4,
|
|
113
110
|
},
|
|
114
|
-
label: {
|
|
115
|
-
fontSize: 11,
|
|
116
|
-
fontWeight: "500",
|
|
117
|
-
},
|
|
118
111
|
});
|
|
112
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile Benefits List Component
|
|
3
|
+
* Shows a list of benefits for anonymous users
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { View, StyleSheet } from "react-native";
|
|
8
|
+
import { AtomicText, AtomicIcon } from "@umituz/react-native-design-system";
|
|
9
|
+
|
|
10
|
+
export interface ProfileBenefitsListProps {
|
|
11
|
+
benefits: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ProfileBenefitsList: React.FC<ProfileBenefitsListProps> = ({ benefits }) => {
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<View style={styles.benefitsContainer}>
|
|
18
|
+
{benefits.map((benefit, index) => (
|
|
19
|
+
<View key={index} style={styles.benefitItem}>
|
|
20
|
+
<AtomicIcon name="check" size="sm" color="primary" />
|
|
21
|
+
<AtomicText
|
|
22
|
+
type="bodyMedium"
|
|
23
|
+
color="secondary"
|
|
24
|
+
style={styles.benefitText}
|
|
25
|
+
>
|
|
26
|
+
{benefit}
|
|
27
|
+
</AtomicText>
|
|
28
|
+
</View>
|
|
29
|
+
))}
|
|
30
|
+
</View>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const styles = StyleSheet.create({
|
|
35
|
+
benefitsContainer: {
|
|
36
|
+
marginBottom: 16,
|
|
37
|
+
gap: 8,
|
|
38
|
+
},
|
|
39
|
+
benefitItem: {
|
|
40
|
+
flexDirection: "row",
|
|
41
|
+
alignItems: "flex-start",
|
|
42
|
+
gap: 8,
|
|
43
|
+
},
|
|
44
|
+
benefitText: {
|
|
45
|
+
flex: 1,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Profile Section Component
|
|
3
3
|
* Generic user profile section for settings screens
|
|
4
|
-
* Shows user info, sign in CTA for anonymous, or account navigation for signed in users
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import React from "react";
|
|
8
|
-
import { View,
|
|
9
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
10
|
-
import {
|
|
7
|
+
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
8
|
+
import { useAppDesignTokens, AtomicText, AtomicIcon, Avatar } from "@umituz/react-native-design-system";
|
|
9
|
+
import { ProfileBenefitsList } from "./ProfileBenefitsList";
|
|
11
10
|
|
|
12
11
|
export interface ProfileSectionConfig {
|
|
13
|
-
displayName
|
|
12
|
+
displayName?: string;
|
|
14
13
|
userId?: string;
|
|
15
14
|
isAnonymous: boolean;
|
|
16
15
|
avatarUrl?: string;
|
|
17
16
|
accountSettingsRoute?: string;
|
|
18
|
-
benefits?: string[];
|
|
17
|
+
benefits?: string[];
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
export interface ProfileSectionProps {
|
|
@@ -43,8 +42,6 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
|
|
43
42
|
}
|
|
44
43
|
};
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
45
|
return (
|
|
49
46
|
<TouchableOpacity
|
|
50
47
|
style={[styles.container, { backgroundColor: tokens.colors.surface }]}
|
|
@@ -53,7 +50,6 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
|
|
53
50
|
disabled={!onPress && !onSignIn}
|
|
54
51
|
>
|
|
55
52
|
<View style={styles.content}>
|
|
56
|
-
{/* Avatar */}
|
|
57
53
|
<View style={styles.avatarContainer}>
|
|
58
54
|
<Avatar
|
|
59
55
|
uri={profile.avatarUrl}
|
|
@@ -63,72 +59,43 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
|
|
63
59
|
/>
|
|
64
60
|
</View>
|
|
65
61
|
|
|
66
|
-
{/* User Info */}
|
|
67
62
|
<View style={styles.info}>
|
|
68
|
-
<
|
|
69
|
-
|
|
63
|
+
<AtomicText
|
|
64
|
+
type="titleMedium"
|
|
65
|
+
color="textPrimary"
|
|
70
66
|
numberOfLines={1}
|
|
67
|
+
style={styles.displayName}
|
|
71
68
|
>
|
|
72
69
|
{profile.displayName}
|
|
73
|
-
</
|
|
70
|
+
</AtomicText>
|
|
74
71
|
{profile.userId && (
|
|
75
|
-
<
|
|
76
|
-
|
|
72
|
+
<AtomicText
|
|
73
|
+
type="bodySmall"
|
|
74
|
+
color="secondary"
|
|
77
75
|
numberOfLines={1}
|
|
78
76
|
>
|
|
79
77
|
{profile.userId}
|
|
80
|
-
</
|
|
78
|
+
</AtomicText>
|
|
81
79
|
)}
|
|
82
80
|
</View>
|
|
83
81
|
|
|
84
|
-
{/* Action Icon - only for authenticated users */}
|
|
85
82
|
{onPress && !profile.isAnonymous && (
|
|
86
|
-
<
|
|
87
|
-
›
|
|
88
|
-
</Text>
|
|
83
|
+
<AtomicIcon name="chevron-right" size="sm" color="secondary" />
|
|
89
84
|
)}
|
|
90
85
|
</View>
|
|
91
86
|
|
|
92
|
-
{/* Sign In CTA for Anonymous Users */}
|
|
93
87
|
{profile.isAnonymous && onSignIn && (
|
|
94
|
-
<View
|
|
95
|
-
|
|
96
|
-
styles.ctaContainer,
|
|
97
|
-
{ borderTopColor: tokens.colors.border },
|
|
98
|
-
]}
|
|
99
|
-
>
|
|
100
|
-
{/* Benefits List */}
|
|
101
|
-
{profile.benefits && profile.benefits.length > 0 && (
|
|
102
|
-
<View style={styles.benefitsContainer}>
|
|
103
|
-
{profile.benefits.map((benefit, index) => (
|
|
104
|
-
<View key={index} style={styles.benefitItem}>
|
|
105
|
-
<Text style={[styles.benefitBullet, { color: tokens.colors.primary }]}>
|
|
106
|
-
✓
|
|
107
|
-
</Text>
|
|
108
|
-
<Text style={[styles.benefitText, { color: tokens.colors.textSecondary }]}>
|
|
109
|
-
{benefit}
|
|
110
|
-
</Text>
|
|
111
|
-
</View>
|
|
112
|
-
))}
|
|
113
|
-
</View>
|
|
114
|
-
)}
|
|
88
|
+
<View style={[styles.ctaContainer, { borderTopColor: tokens.colors.border }]}>
|
|
89
|
+
{profile.benefits && <ProfileBenefitsList benefits={profile.benefits} />}
|
|
115
90
|
|
|
116
91
|
<TouchableOpacity
|
|
117
|
-
style={[
|
|
118
|
-
styles.ctaButton,
|
|
119
|
-
{ backgroundColor: tokens.colors.primary },
|
|
120
|
-
]}
|
|
92
|
+
style={[styles.ctaButton, { backgroundColor: tokens.colors.primary }]}
|
|
121
93
|
onPress={onSignIn}
|
|
122
94
|
activeOpacity={0.8}
|
|
123
95
|
>
|
|
124
|
-
<
|
|
125
|
-
style={[
|
|
126
|
-
styles.ctaText,
|
|
127
|
-
{ color: tokens.colors.onPrimary },
|
|
128
|
-
]}
|
|
129
|
-
>
|
|
96
|
+
<AtomicText type="labelLarge" style={{ color: tokens.colors.onPrimary }}>
|
|
130
97
|
{signInText}
|
|
131
|
-
</
|
|
98
|
+
</AtomicText>
|
|
132
99
|
</TouchableOpacity>
|
|
133
100
|
</View>
|
|
134
101
|
)}
|
|
@@ -153,48 +120,16 @@ const styles = StyleSheet.create({
|
|
|
153
120
|
flex: 1,
|
|
154
121
|
},
|
|
155
122
|
displayName: {
|
|
156
|
-
fontSize: 18,
|
|
157
|
-
fontWeight: "600",
|
|
158
123
|
marginBottom: 2,
|
|
159
124
|
},
|
|
160
|
-
userId: {
|
|
161
|
-
fontSize: 13,
|
|
162
|
-
},
|
|
163
|
-
chevron: {
|
|
164
|
-
fontSize: 24,
|
|
165
|
-
fontWeight: "400",
|
|
166
|
-
},
|
|
167
125
|
ctaContainer: {
|
|
168
126
|
marginTop: 12,
|
|
169
127
|
paddingTop: 12,
|
|
170
128
|
borderTopWidth: 1,
|
|
171
129
|
},
|
|
172
|
-
benefitsContainer: {
|
|
173
|
-
marginBottom: 16,
|
|
174
|
-
gap: 8,
|
|
175
|
-
},
|
|
176
|
-
benefitItem: {
|
|
177
|
-
flexDirection: "row",
|
|
178
|
-
alignItems: "flex-start",
|
|
179
|
-
gap: 8,
|
|
180
|
-
},
|
|
181
|
-
benefitBullet: {
|
|
182
|
-
fontSize: 16,
|
|
183
|
-
fontWeight: "600",
|
|
184
|
-
marginTop: 2,
|
|
185
|
-
},
|
|
186
|
-
benefitText: {
|
|
187
|
-
flex: 1,
|
|
188
|
-
fontSize: 14,
|
|
189
|
-
lineHeight: 20,
|
|
190
|
-
},
|
|
191
130
|
ctaButton: {
|
|
192
131
|
paddingVertical: 12,
|
|
193
132
|
borderRadius: 8,
|
|
194
133
|
alignItems: "center",
|
|
195
134
|
},
|
|
196
|
-
ctaText: {
|
|
197
|
-
fontSize: 15,
|
|
198
|
-
fontWeight: "600",
|
|
199
|
-
},
|
|
200
135
|
});
|
|
@@ -51,11 +51,11 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
|
|
51
51
|
<>
|
|
52
52
|
<View style={styles.inputContainer}>
|
|
53
53
|
<AtomicInput
|
|
54
|
-
label={t("auth.displayName")
|
|
54
|
+
label={t("auth.displayName")}
|
|
55
55
|
value={displayName}
|
|
56
56
|
onChangeText={handleDisplayNameChange}
|
|
57
57
|
placeholder={
|
|
58
|
-
t("auth.displayNamePlaceholder")
|
|
58
|
+
t("auth.displayNamePlaceholder")
|
|
59
59
|
}
|
|
60
60
|
autoCapitalize="words"
|
|
61
61
|
disabled={loading}
|
|
@@ -97,11 +97,11 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
|
|
97
97
|
|
|
98
98
|
<View style={styles.inputContainer}>
|
|
99
99
|
<AtomicInput
|
|
100
|
-
label={t("auth.confirmPassword")
|
|
100
|
+
label={t("auth.confirmPassword")}
|
|
101
101
|
value={confirmPassword}
|
|
102
102
|
onChangeText={handleConfirmPasswordChange}
|
|
103
103
|
placeholder={
|
|
104
|
-
t("auth.confirmPasswordPlaceholder")
|
|
104
|
+
t("auth.confirmPasswordPlaceholder")
|
|
105
105
|
}
|
|
106
106
|
secureTextEntry
|
|
107
107
|
autoCapitalize="none"
|
|
@@ -119,7 +119,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
|
|
119
119
|
<View style={styles.buttonContainer}>
|
|
120
120
|
<AtomicButton
|
|
121
121
|
variant="primary"
|
|
122
|
-
onPress={handleSignUp}
|
|
122
|
+
onPress={() => { void handleSignUp(); }}
|
|
123
123
|
disabled={
|
|
124
124
|
loading ||
|
|
125
125
|
!email.trim() ||
|
|
@@ -145,7 +145,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
|
|
145
145
|
privacyUrl={privacyUrl}
|
|
146
146
|
onTermsPress={onTermsPress}
|
|
147
147
|
onPrivacyPress={onPrivacyPress}
|
|
148
|
-
prefixText={t("auth.bySigningUp")
|
|
148
|
+
prefixText={t("auth.bySigningUp")}
|
|
149
149
|
/>
|
|
150
150
|
</>
|
|
151
151
|
);
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
|
-
Text,
|
|
5
4
|
TouchableOpacity,
|
|
6
5
|
StyleSheet,
|
|
7
6
|
Platform,
|
|
8
7
|
ActivityIndicator,
|
|
9
8
|
} from "react-native";
|
|
10
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
9
|
+
import { useAppDesignTokens, AtomicText, AtomicIcon } from "@umituz/react-native-design-system";
|
|
11
10
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
12
11
|
import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
|
|
13
|
-
import { AppleIconSvg, GoogleIconSvg } from "./icons";
|
|
14
12
|
|
|
15
13
|
export interface SocialLoginButtonsProps {
|
|
16
14
|
/** Enabled providers to display */
|
|
@@ -49,9 +47,9 @@ export const SocialLoginButtons: React.FC<SocialLoginButtonsProps> = ({
|
|
|
49
47
|
<View style={styles.container}>
|
|
50
48
|
<View style={styles.dividerContainer}>
|
|
51
49
|
<View style={[styles.divider, { backgroundColor: tokens.colors.border }]} />
|
|
52
|
-
<
|
|
50
|
+
<AtomicText type="bodySmall" color="secondary" style={styles.dividerText}>
|
|
53
51
|
{t("auth.orContinueWith")}
|
|
54
|
-
</
|
|
52
|
+
</AtomicText>
|
|
55
53
|
<View style={[styles.divider, { backgroundColor: tokens.colors.border }]} />
|
|
56
54
|
</View>
|
|
57
55
|
|
|
@@ -71,10 +69,10 @@ export const SocialLoginButtons: React.FC<SocialLoginButtonsProps> = ({
|
|
|
71
69
|
<ActivityIndicator size="small" color={tokens.colors.textPrimary} />
|
|
72
70
|
) : (
|
|
73
71
|
<>
|
|
74
|
-
<
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
</
|
|
72
|
+
<AtomicIcon name="google" size="sm" />
|
|
73
|
+
<AtomicText style={[styles.buttonText, { color: tokens.colors.textPrimary }]}>
|
|
74
|
+
{t("auth.google")}
|
|
75
|
+
</AtomicText>
|
|
78
76
|
</>
|
|
79
77
|
)}
|
|
80
78
|
</TouchableOpacity>
|
|
@@ -95,10 +93,10 @@ export const SocialLoginButtons: React.FC<SocialLoginButtonsProps> = ({
|
|
|
95
93
|
<ActivityIndicator size="small" color={tokens.colors.textPrimary} />
|
|
96
94
|
) : (
|
|
97
95
|
<>
|
|
98
|
-
<
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
</
|
|
96
|
+
<AtomicIcon name="apple" size="sm" color="primary" />
|
|
97
|
+
<AtomicText style={[styles.buttonText, { color: tokens.colors.textPrimary }]}>
|
|
98
|
+
{t("auth.apple")}
|
|
99
|
+
</AtomicText>
|
|
102
100
|
</>
|
|
103
101
|
)}
|
|
104
102
|
</TouchableOpacity>
|
|
@@ -108,7 +106,6 @@ export const SocialLoginButtons: React.FC<SocialLoginButtonsProps> = ({
|
|
|
108
106
|
);
|
|
109
107
|
};
|
|
110
108
|
|
|
111
|
-
|
|
112
109
|
const styles = StyleSheet.create({
|
|
113
110
|
container: {
|
|
114
111
|
marginTop: 24,
|
|
@@ -124,7 +121,6 @@ const styles = StyleSheet.create({
|
|
|
124
121
|
},
|
|
125
122
|
dividerText: {
|
|
126
123
|
marginHorizontal: 16,
|
|
127
|
-
fontSize: 14,
|
|
128
124
|
},
|
|
129
125
|
buttonsContainer: {
|
|
130
126
|
flexDirection: "row",
|