@umituz/react-native-auth 3.5.9 → 3.5.10
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 +1 -1
- package/src/infrastructure/utils/AuthValidation.ts +10 -4
- package/src/presentation/components/AccountActions.tsx +7 -7
- package/src/presentation/components/AuthBottomSheet.tsx +3 -3
- package/src/presentation/components/AuthHeader.tsx +2 -2
- package/src/presentation/components/AuthLegalLinks.tsx +2 -2
- package/src/presentation/components/AuthLink.tsx +1 -1
- package/src/presentation/components/PasswordStrengthIndicator.tsx +31 -19
- package/src/presentation/components/ProfileBenefitsList.tsx +1 -1
- package/src/presentation/components/ProfileSection.tsx +5 -6
- package/src/presentation/components/AuthDivider.tsx +0 -63
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.10",
|
|
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",
|
|
@@ -43,15 +43,21 @@ export function validatePasswordForRegister(
|
|
|
43
43
|
config: PasswordConfig,
|
|
44
44
|
validationConfig: ValidationConfig = DEFAULT_VAL_CONFIG
|
|
45
45
|
): PasswordStrengthResult {
|
|
46
|
+
/*
|
|
47
|
+
* Check for strict presence of characters regardless of configuration
|
|
48
|
+
* This ensures the UI reflects actual password content
|
|
49
|
+
*/
|
|
46
50
|
const req: PasswordRequirements = {
|
|
47
51
|
hasMinLength: password.length >= config.minLength,
|
|
48
|
-
hasUppercase:
|
|
49
|
-
hasLowercase:
|
|
50
|
-
hasNumber:
|
|
51
|
-
hasSpecialChar:
|
|
52
|
+
hasUppercase: validationConfig.uppercaseRegex.test(password),
|
|
53
|
+
hasLowercase: validationConfig.lowercaseRegex.test(password),
|
|
54
|
+
hasNumber: validationConfig.numberRegex.test(password),
|
|
55
|
+
hasSpecialChar: validationConfig.specialCharRegex.test(password),
|
|
52
56
|
};
|
|
53
57
|
|
|
54
58
|
if (!password) return { isValid: false, error: "auth.validation.passwordRequired", requirements: req };
|
|
59
|
+
|
|
60
|
+
// Validation checks based on configuration
|
|
55
61
|
if (!req.hasMinLength) return { isValid: false, error: "auth.validation.passwordTooShort", requirements: req };
|
|
56
62
|
if (config.requireUppercase && !req.hasUppercase) return { isValid: false, error: "auth.validation.passwordRequireUppercase", requirements: req };
|
|
57
63
|
if (config.requireLowercase && !req.hasLowercase) return { isValid: false, error: "auth.validation.passwordRequireLowercase", requirements: req };
|
|
@@ -93,11 +93,11 @@ export const AccountActions: React.FC<AccountActionsProps> = ({ config }) => {
|
|
|
93
93
|
onPress={onChangePassword}
|
|
94
94
|
activeOpacity={0.7}
|
|
95
95
|
>
|
|
96
|
-
<AtomicIcon name="key-outline" size="md"
|
|
97
|
-
<AtomicText style={
|
|
96
|
+
<AtomicIcon name="key-outline" size="md" color="textPrimary" />
|
|
97
|
+
<AtomicText style={styles.actionText} color="textPrimary">
|
|
98
98
|
{changePasswordText}
|
|
99
99
|
</AtomicText>
|
|
100
|
-
<AtomicIcon name="chevron-forward" size="sm" color="
|
|
100
|
+
<AtomicIcon name="chevron-forward" size="sm" color="textSecondary" />
|
|
101
101
|
</TouchableOpacity>
|
|
102
102
|
)}
|
|
103
103
|
|
|
@@ -108,10 +108,10 @@ export const AccountActions: React.FC<AccountActionsProps> = ({ config }) => {
|
|
|
108
108
|
activeOpacity={0.7}
|
|
109
109
|
>
|
|
110
110
|
<AtomicIcon name="log-out-outline" size="md" color="error" />
|
|
111
|
-
<AtomicText style={
|
|
111
|
+
<AtomicText style={styles.actionText} color="error">
|
|
112
112
|
{logoutText}
|
|
113
113
|
</AtomicText>
|
|
114
|
-
<AtomicIcon name="chevron-forward" size="sm" color="
|
|
114
|
+
<AtomicIcon name="chevron-forward" size="sm" color="textSecondary" />
|
|
115
115
|
</TouchableOpacity>
|
|
116
116
|
|
|
117
117
|
{/* Delete Account */}
|
|
@@ -121,10 +121,10 @@ export const AccountActions: React.FC<AccountActionsProps> = ({ config }) => {
|
|
|
121
121
|
activeOpacity={0.7}
|
|
122
122
|
>
|
|
123
123
|
<AtomicIcon name="trash-outline" size="md" color="error" />
|
|
124
|
-
<AtomicText style={
|
|
124
|
+
<AtomicText style={styles.actionText} color="error">
|
|
125
125
|
{deleteAccountText}
|
|
126
126
|
</AtomicText>
|
|
127
|
-
<AtomicIcon name="chevron-forward" size="sm" color="
|
|
127
|
+
<AtomicIcon name="chevron-forward" size="sm" color="textSecondary" />
|
|
128
128
|
</TouchableOpacity>
|
|
129
129
|
</View>
|
|
130
130
|
);
|
|
@@ -93,7 +93,7 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
93
93
|
accessibilityLabel={t("common.close")}
|
|
94
94
|
accessibilityRole="button"
|
|
95
95
|
>
|
|
96
|
-
<AtomicIcon name="close" size="md" color="
|
|
96
|
+
<AtomicIcon name="close" size="md" color="textSecondary" />
|
|
97
97
|
</TouchableOpacity>
|
|
98
98
|
|
|
99
99
|
<ScrollView
|
|
@@ -103,10 +103,10 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
103
103
|
>
|
|
104
104
|
|
|
105
105
|
<View style={styles.header}>
|
|
106
|
-
<AtomicText type="headlineLarge" color="
|
|
106
|
+
<AtomicText type="headlineLarge" color="textPrimary" style={styles.title}>
|
|
107
107
|
{mode === "login" ? t("auth.signIn") : t("auth.createAccount")}
|
|
108
108
|
</AtomicText>
|
|
109
|
-
<AtomicText type="bodyLarge" color="
|
|
109
|
+
<AtomicText type="bodyLarge" color="textSecondary" style={styles.subtitle}>
|
|
110
110
|
{mode === "login" ? t("auth.signInSubtitle") : t("auth.createAccountSubtitle")}
|
|
111
111
|
</AtomicText>
|
|
112
112
|
</View>
|
|
@@ -21,7 +21,7 @@ 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
|
-
color="
|
|
24
|
+
color="textPrimary"
|
|
25
25
|
style={{ textAlign: "center" }}
|
|
26
26
|
>
|
|
27
27
|
{title}
|
|
@@ -29,7 +29,7 @@ export const AuthHeader: React.FC<AuthHeaderProps> = ({ title, subtitle }) => {
|
|
|
29
29
|
{(subtitle || t("auth.subtitle")) && (
|
|
30
30
|
<AtomicText
|
|
31
31
|
type="bodyMedium"
|
|
32
|
-
color="
|
|
32
|
+
color="textSecondary"
|
|
33
33
|
style={{
|
|
34
34
|
textAlign: "center",
|
|
35
35
|
marginTop: tokens.spacing.xs,
|
|
@@ -69,7 +69,7 @@ export const AuthLegalLinks: React.FC<AuthLegalLinksProps> = ({
|
|
|
69
69
|
{prefixText && (
|
|
70
70
|
<AtomicText
|
|
71
71
|
type="bodySmall"
|
|
72
|
-
color="
|
|
72
|
+
color="textSecondary"
|
|
73
73
|
style={styles.prefixText}
|
|
74
74
|
>
|
|
75
75
|
{prefixText}
|
|
@@ -86,7 +86,7 @@ export const AuthLegalLinks: React.FC<AuthLegalLinksProps> = ({
|
|
|
86
86
|
/>
|
|
87
87
|
)}
|
|
88
88
|
{hasTerms && hasPrivacy && (
|
|
89
|
-
<AtomicText type="bodySmall" color="
|
|
89
|
+
<AtomicText type="bodySmall" color="textSecondary" style={styles.separator}>
|
|
90
90
|
{" • "}
|
|
91
91
|
</AtomicText>
|
|
92
92
|
)}
|
|
@@ -26,7 +26,7 @@ export const AuthLink: React.FC<AuthLinkProps> = ({
|
|
|
26
26
|
<View style={[styles.container, { marginTop: tokens.spacing.xs, paddingTop: tokens.spacing.xs }]}>
|
|
27
27
|
<AtomicText
|
|
28
28
|
type="bodyMedium"
|
|
29
|
-
|
|
29
|
+
color="textSecondary"
|
|
30
30
|
>
|
|
31
31
|
{text}{" "}
|
|
32
32
|
</AtomicText>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View, StyleSheet } from "react-native";
|
|
3
|
-
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
3
|
+
import { useAppDesignTokens, AtomicText, type ColorVariant } from "@umituz/react-native-design-system";
|
|
4
4
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
5
5
|
import type { PasswordRequirements } from "../../infrastructure/utils/AuthValidation";
|
|
6
6
|
|
|
@@ -12,8 +12,8 @@ export interface PasswordStrengthIndicatorProps {
|
|
|
12
12
|
interface RequirementDotProps {
|
|
13
13
|
label: string;
|
|
14
14
|
isValid: boolean;
|
|
15
|
-
successColor:
|
|
16
|
-
pendingColor:
|
|
15
|
+
successColor: ColorVariant;
|
|
16
|
+
pendingColor: ColorVariant;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const RequirementDot: React.FC<RequirementDotProps> = ({
|
|
@@ -22,12 +22,20 @@ const RequirementDot: React.FC<RequirementDotProps> = ({
|
|
|
22
22
|
successColor,
|
|
23
23
|
pendingColor,
|
|
24
24
|
}) => {
|
|
25
|
-
const
|
|
25
|
+
const tokens = useAppDesignTokens();
|
|
26
|
+
const colorKey = isValid ? successColor : pendingColor;
|
|
27
|
+
|
|
28
|
+
// Resolve the color value from the token key for the View background
|
|
29
|
+
// We use type assertion since we know these are valid specific keys passed from parent
|
|
30
|
+
// but tokens.colors index signature might be limited
|
|
31
|
+
const dotColor = (tokens.colors as Record<string, string>)[colorKey] || tokens.colors.textTertiary;
|
|
26
32
|
|
|
27
33
|
return (
|
|
28
34
|
<View style={styles.requirement}>
|
|
29
|
-
<View style={[styles.dot, { backgroundColor:
|
|
30
|
-
<AtomicText type="labelSmall"
|
|
35
|
+
<View style={[styles.dot, { backgroundColor: dotColor }]} />
|
|
36
|
+
<AtomicText type="labelSmall" color={colorKey}>
|
|
37
|
+
{label}
|
|
38
|
+
</AtomicText>
|
|
31
39
|
</View>
|
|
32
40
|
);
|
|
33
41
|
};
|
|
@@ -37,8 +45,9 @@ export const PasswordStrengthIndicator: React.FC<
|
|
|
37
45
|
> = ({ requirements, showLabels = true }) => {
|
|
38
46
|
const tokens = useAppDesignTokens();
|
|
39
47
|
const { t } = useLocalization();
|
|
40
|
-
|
|
41
|
-
const
|
|
48
|
+
|
|
49
|
+
const successColor: ColorVariant = "success";
|
|
50
|
+
const pendingColor: ColorVariant = "textTertiary";
|
|
42
51
|
|
|
43
52
|
const items = [
|
|
44
53
|
{ key: "minLength", label: t("auth.passwordReq.minLength"), isValid: requirements.hasMinLength },
|
|
@@ -51,17 +60,20 @@ export const PasswordStrengthIndicator: React.FC<
|
|
|
51
60
|
if (!showLabels) {
|
|
52
61
|
return (
|
|
53
62
|
<View style={styles.dotsOnly}>
|
|
54
|
-
{items.map((item) =>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
{items.map((item) => {
|
|
64
|
+
const colorKey = item.isValid ? successColor : pendingColor;
|
|
65
|
+
const dotColor = (tokens.colors as Record<string, string>)[colorKey] || tokens.colors.textTertiary;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<View
|
|
69
|
+
key={item.key}
|
|
70
|
+
style={[
|
|
71
|
+
styles.dotOnly,
|
|
72
|
+
{ backgroundColor: dotColor },
|
|
73
|
+
]}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
})}
|
|
65
77
|
</View>
|
|
66
78
|
);
|
|
67
79
|
}
|
|
@@ -20,7 +20,7 @@ export const ProfileBenefitsList: React.FC<ProfileBenefitsListProps> = ({ benefi
|
|
|
20
20
|
<AtomicIcon name="checkmark-circle" size="sm" color="primary" />
|
|
21
21
|
<AtomicText
|
|
22
22
|
type="bodyMedium"
|
|
23
|
-
color="
|
|
23
|
+
color="textSecondary"
|
|
24
24
|
style={styles.benefitText}
|
|
25
25
|
>
|
|
26
26
|
{benefit}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
7
|
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
8
|
-
import { useAppDesignTokens, AtomicText, AtomicIcon,
|
|
8
|
+
import { useAppDesignTokens, AtomicText, AtomicIcon, AtomicAvatar } from "@umituz/react-native-design-system";
|
|
9
9
|
import { ProfileBenefitsList } from "./ProfileBenefitsList";
|
|
10
10
|
|
|
11
11
|
export interface ProfileSectionConfig {
|
|
@@ -51,11 +51,10 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
|
|
51
51
|
>
|
|
52
52
|
<View style={styles.content}>
|
|
53
53
|
<View style={styles.avatarContainer}>
|
|
54
|
-
<
|
|
55
|
-
|
|
54
|
+
<AtomicAvatar
|
|
55
|
+
source={profile.avatarUrl ? { uri: profile.avatarUrl } : undefined}
|
|
56
56
|
name={profile.displayName || (profile.isAnonymous ? anonymousText : signInText)}
|
|
57
57
|
size="md"
|
|
58
|
-
shape="circle"
|
|
59
58
|
/>
|
|
60
59
|
</View>
|
|
61
60
|
|
|
@@ -71,7 +70,7 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
|
|
71
70
|
{profile.userId && (
|
|
72
71
|
<AtomicText
|
|
73
72
|
type="bodySmall"
|
|
74
|
-
color="
|
|
73
|
+
color="textSecondary"
|
|
75
74
|
numberOfLines={1}
|
|
76
75
|
>
|
|
77
76
|
{profile.userId}
|
|
@@ -80,7 +79,7 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
|
|
80
79
|
</View>
|
|
81
80
|
|
|
82
81
|
{onPress && !profile.isAnonymous && (
|
|
83
|
-
<AtomicIcon name="chevron-forward" size="sm" color="
|
|
82
|
+
<AtomicIcon name="chevron-forward" size="sm" color="textSecondary" />
|
|
84
83
|
)}
|
|
85
84
|
</View>
|
|
86
85
|
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth Divider Component
|
|
3
|
-
* Divider with "OR" text for auth screens
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { View, StyleSheet } from "react-native";
|
|
8
|
-
import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
9
|
-
import { useLocalization } from "@umituz/react-native-localization";
|
|
10
|
-
|
|
11
|
-
export const AuthDivider: React.FC = () => {
|
|
12
|
-
const tokens = useAppDesignTokens();
|
|
13
|
-
const { t } = useLocalization();
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<View style={[styles.divider, { marginVertical: tokens.spacing.md }]}>
|
|
17
|
-
<View
|
|
18
|
-
style={[
|
|
19
|
-
styles.dividerLine,
|
|
20
|
-
{ backgroundColor: tokens.colors.borderLight },
|
|
21
|
-
]}
|
|
22
|
-
/>
|
|
23
|
-
<AtomicText
|
|
24
|
-
style={{
|
|
25
|
-
color: tokens.colors.textSecondary,
|
|
26
|
-
marginHorizontal: tokens.spacing.sm,
|
|
27
|
-
textTransform: "uppercase",
|
|
28
|
-
letterSpacing: 0.5,
|
|
29
|
-
}}
|
|
30
|
-
>
|
|
31
|
-
{t("general.or")}
|
|
32
|
-
</AtomicText>
|
|
33
|
-
<View
|
|
34
|
-
style={[
|
|
35
|
-
styles.dividerLine,
|
|
36
|
-
{ backgroundColor: tokens.colors.borderLight },
|
|
37
|
-
]}
|
|
38
|
-
/>
|
|
39
|
-
</View>
|
|
40
|
-
);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const styles = StyleSheet.create({
|
|
44
|
-
divider: {
|
|
45
|
-
flexDirection: "row",
|
|
46
|
-
alignItems: "center",
|
|
47
|
-
},
|
|
48
|
-
dividerLine: {
|
|
49
|
-
flex: 1,
|
|
50
|
-
height: 1,
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|