@umituz/react-native-auth 2.6.17 → 2.6.19
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.19",
|
|
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",
|
package/src/index.ts
CHANGED
|
@@ -170,8 +170,6 @@ export { ProfileSection } from './presentation/components/ProfileSection';
|
|
|
170
170
|
export type { ProfileSectionConfig, ProfileSectionProps } from './presentation/components/ProfileSection';
|
|
171
171
|
export { AccountActions } from './presentation/components/AccountActions';
|
|
172
172
|
export type { AccountActionsConfig, AccountActionsProps } from './presentation/components/AccountActions';
|
|
173
|
-
export { UserProfileHeader } from './presentation/components/UserProfileHeader';
|
|
174
|
-
export type { UserProfileHeaderProps } from './presentation/components/UserProfileHeader';
|
|
175
173
|
|
|
176
174
|
// =============================================================================
|
|
177
175
|
// PRESENTATION LAYER - Stores
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth Error Display Component
|
|
3
|
-
* Displays authentication errors
|
|
3
|
+
* Displays authentication errors using design system tokens
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { View,
|
|
7
|
+
import { View, StyleSheet } from "react-native";
|
|
8
|
+
import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
8
9
|
|
|
9
10
|
interface AuthErrorDisplayProps {
|
|
10
11
|
error: string | null;
|
|
@@ -13,13 +14,28 @@ interface AuthErrorDisplayProps {
|
|
|
13
14
|
export const AuthErrorDisplay: React.FC<AuthErrorDisplayProps> = ({
|
|
14
15
|
error,
|
|
15
16
|
}) => {
|
|
17
|
+
const tokens = useAppDesignTokens();
|
|
18
|
+
|
|
16
19
|
if (!error) {
|
|
17
20
|
return null;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
return (
|
|
21
|
-
<View
|
|
22
|
-
|
|
24
|
+
<View
|
|
25
|
+
style={[
|
|
26
|
+
styles.errorContainer,
|
|
27
|
+
{
|
|
28
|
+
backgroundColor: tokens.colors.errorLight,
|
|
29
|
+
borderColor: tokens.colors.error,
|
|
30
|
+
},
|
|
31
|
+
]}
|
|
32
|
+
>
|
|
33
|
+
<AtomicText
|
|
34
|
+
type="body"
|
|
35
|
+
style={[styles.errorText, { color: tokens.colors.error }]}
|
|
36
|
+
>
|
|
37
|
+
{error}
|
|
38
|
+
</AtomicText>
|
|
23
39
|
</View>
|
|
24
40
|
);
|
|
25
41
|
};
|
|
@@ -29,12 +45,9 @@ const styles = StyleSheet.create({
|
|
|
29
45
|
marginBottom: 16,
|
|
30
46
|
padding: 14,
|
|
31
47
|
borderRadius: 12,
|
|
32
|
-
backgroundColor: "rgba(255, 59, 48, 0.1)",
|
|
33
48
|
borderWidth: 1,
|
|
34
|
-
borderColor: "rgba(255, 59, 48, 0.2)",
|
|
35
49
|
},
|
|
36
50
|
errorText: {
|
|
37
|
-
color: "#FF3B30",
|
|
38
51
|
fontSize: 14,
|
|
39
52
|
textAlign: "center",
|
|
40
53
|
fontWeight: "500",
|
|
@@ -38,9 +38,24 @@ export function useAuthState(): UseAuthStateResult {
|
|
|
38
38
|
|
|
39
39
|
// Memoize user to prevent new object reference on every render
|
|
40
40
|
const user = useMemo(() => {
|
|
41
|
+
// If no Firebase user, return null
|
|
42
|
+
if (!firebaseUser) return null;
|
|
43
|
+
|
|
44
|
+
// If Firebase user exists and is NOT anonymous, always return the user
|
|
45
|
+
// This ensures real authenticated users (email, Google, Apple) always have user object
|
|
46
|
+
// even if isGuest was previously true (which gets reset by useEffect below)
|
|
47
|
+
if (!firebaseUser.isAnonymous) {
|
|
48
|
+
return mapToAuthUser(firebaseUser);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// If Firebase user is anonymous AND we're in guest mode, return null
|
|
52
|
+
// Guest mode = user clicked "Continue as Guest" with no account
|
|
41
53
|
if (isGuest) return null;
|
|
54
|
+
|
|
55
|
+
// If Firebase user is anonymous but NOT in guest mode, return the user
|
|
56
|
+
// This handles anonymous auth accounts that can be upgraded later
|
|
42
57
|
return mapToAuthUser(firebaseUser);
|
|
43
|
-
}, [isGuest, firebaseUser?.uid]);
|
|
58
|
+
}, [isGuest, firebaseUser?.uid, firebaseUser?.isAnonymous]);
|
|
44
59
|
|
|
45
60
|
// Anonymous users are NOT authenticated - they need to register/login
|
|
46
61
|
const isAuthenticated = !!user && !isGuest && !user.isAnonymous;
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* User Profile Header Component
|
|
3
|
-
* Displays user avatar, name, and ID
|
|
4
|
-
* Works for both guest and authenticated users
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, { useCallback } from "react";
|
|
8
|
-
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
9
|
-
import {
|
|
10
|
-
AtomicIcon,
|
|
11
|
-
AtomicText,
|
|
12
|
-
useAppDesignTokens,
|
|
13
|
-
} from "@umituz/react-native-design-system";
|
|
14
|
-
import { useNavigation } from "@react-navigation/native";
|
|
15
|
-
import { Avatar } from "@umituz/react-native-avatar";
|
|
16
|
-
|
|
17
|
-
export interface UserProfileHeaderProps {
|
|
18
|
-
/** User display name */
|
|
19
|
-
displayName?: string;
|
|
20
|
-
/** User ID */
|
|
21
|
-
userId?: string;
|
|
22
|
-
/** Whether user is anonymous (device-based ID) */
|
|
23
|
-
isAnonymous?: boolean;
|
|
24
|
-
/** Avatar URL (optional) */
|
|
25
|
-
avatarUrl?: string;
|
|
26
|
-
/** Navigation route for account settings */
|
|
27
|
-
accountSettingsRoute?: string;
|
|
28
|
-
/** Custom onPress handler */
|
|
29
|
-
onPress?: () => void;
|
|
30
|
-
/** Custom anonymous user display name */
|
|
31
|
-
anonymousDisplayName?: string;
|
|
32
|
-
/** Custom avatar service URL */
|
|
33
|
-
avatarServiceUrl?: string;
|
|
34
|
-
/** Default user display name when no displayName provided */
|
|
35
|
-
defaultUserDisplayName?: string;
|
|
36
|
-
/** Default anonymous display name */
|
|
37
|
-
defaultAnonymousDisplayName?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const UserProfileHeader: React.FC<UserProfileHeaderProps> = ({
|
|
41
|
-
displayName,
|
|
42
|
-
userId,
|
|
43
|
-
isAnonymous = false,
|
|
44
|
-
avatarUrl,
|
|
45
|
-
accountSettingsRoute,
|
|
46
|
-
onPress,
|
|
47
|
-
anonymousDisplayName,
|
|
48
|
-
avatarServiceUrl,
|
|
49
|
-
defaultUserDisplayName,
|
|
50
|
-
defaultAnonymousDisplayName,
|
|
51
|
-
}) => {
|
|
52
|
-
const tokens = useAppDesignTokens();
|
|
53
|
-
const navigation = useNavigation();
|
|
54
|
-
const colors = tokens.colors;
|
|
55
|
-
const spacing = tokens.spacing;
|
|
56
|
-
const finalDisplayName = displayName || (isAnonymous ? anonymousDisplayName || defaultAnonymousDisplayName || "Anonymous" : defaultUserDisplayName || "User");
|
|
57
|
-
const avatarName = isAnonymous ? anonymousDisplayName || defaultAnonymousDisplayName || "Anonymous" : finalDisplayName;
|
|
58
|
-
|
|
59
|
-
const handlePress = useCallback(() => {
|
|
60
|
-
if (onPress) {
|
|
61
|
-
onPress();
|
|
62
|
-
} else if (accountSettingsRoute) {
|
|
63
|
-
navigation.navigate(accountSettingsRoute as never);
|
|
64
|
-
}
|
|
65
|
-
}, [onPress, accountSettingsRoute, navigation]);
|
|
66
|
-
|
|
67
|
-
const shouldShowChevron = !!(onPress || accountSettingsRoute);
|
|
68
|
-
const isPressable = !!(onPress || accountSettingsRoute);
|
|
69
|
-
|
|
70
|
-
const containerStyle = [
|
|
71
|
-
styles.container,
|
|
72
|
-
{
|
|
73
|
-
backgroundColor: colors.surface,
|
|
74
|
-
paddingHorizontal: spacing.md,
|
|
75
|
-
paddingVertical: spacing.md,
|
|
76
|
-
marginHorizontal: spacing.md,
|
|
77
|
-
},
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
const content = (
|
|
81
|
-
<>
|
|
82
|
-
<View style={styles.content}>
|
|
83
|
-
<View style={styles.avatarContainer}>
|
|
84
|
-
<Avatar
|
|
85
|
-
uri={avatarUrl}
|
|
86
|
-
name={avatarName}
|
|
87
|
-
size="lg"
|
|
88
|
-
shape="circle"
|
|
89
|
-
/>
|
|
90
|
-
</View>
|
|
91
|
-
<View style={[styles.textContainer, { marginLeft: spacing.md }]}>
|
|
92
|
-
<AtomicText
|
|
93
|
-
type="headlineSmall"
|
|
94
|
-
style={[styles.name, { color: colors.textPrimary }]}
|
|
95
|
-
numberOfLines={1}
|
|
96
|
-
>
|
|
97
|
-
{finalDisplayName}
|
|
98
|
-
</AtomicText>
|
|
99
|
-
{userId && (
|
|
100
|
-
<AtomicText
|
|
101
|
-
type="bodySmall"
|
|
102
|
-
style={[styles.id, { color: colors.textSecondary }]}
|
|
103
|
-
numberOfLines={1}
|
|
104
|
-
>
|
|
105
|
-
{userId}
|
|
106
|
-
</AtomicText>
|
|
107
|
-
)}
|
|
108
|
-
</View>
|
|
109
|
-
</View>
|
|
110
|
-
<View style={[styles.chevronContainer, { marginLeft: spacing.sm }]}>
|
|
111
|
-
<AtomicIcon name="chevron-forward" size="md" color="onSurface" />
|
|
112
|
-
</View>
|
|
113
|
-
</>
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
if (isPressable) {
|
|
117
|
-
return (
|
|
118
|
-
<TouchableOpacity
|
|
119
|
-
style={containerStyle}
|
|
120
|
-
onPress={handlePress}
|
|
121
|
-
activeOpacity={0.7}
|
|
122
|
-
>
|
|
123
|
-
{content}
|
|
124
|
-
</TouchableOpacity>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return <View style={containerStyle}>{content}</View>;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const styles = StyleSheet.create({
|
|
132
|
-
container: {
|
|
133
|
-
flexDirection: "row",
|
|
134
|
-
alignItems: "center",
|
|
135
|
-
justifyContent: "space-between",
|
|
136
|
-
marginTop: 0,
|
|
137
|
-
marginBottom: 0,
|
|
138
|
-
borderRadius: 20,
|
|
139
|
-
minHeight: 80,
|
|
140
|
-
},
|
|
141
|
-
content: {
|
|
142
|
-
flexDirection: "row",
|
|
143
|
-
alignItems: "center",
|
|
144
|
-
flex: 1,
|
|
145
|
-
},
|
|
146
|
-
avatarContainer: {
|
|
147
|
-
justifyContent: "center",
|
|
148
|
-
alignItems: "center",
|
|
149
|
-
},
|
|
150
|
-
textContainer: {
|
|
151
|
-
flex: 1,
|
|
152
|
-
},
|
|
153
|
-
name: {
|
|
154
|
-
fontWeight: "700",
|
|
155
|
-
},
|
|
156
|
-
id: {
|
|
157
|
-
fontWeight: "500",
|
|
158
|
-
opacity: 0.7,
|
|
159
|
-
},
|
|
160
|
-
chevronContainer: {
|
|
161
|
-
justifyContent: "center",
|
|
162
|
-
alignItems: "center",
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
|