@umituz/react-native-auth 2.5.3 → 2.5.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-auth",
3
- "version": "2.5.3",
3
+ "version": "2.5.5",
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",
@@ -0,0 +1,19 @@
1
+ /**
2
+ * User Profile Types
3
+ * Domain types for user profile management across all apps
4
+ */
5
+
6
+ export interface UserProfile {
7
+ uid: string;
8
+ email: string | null;
9
+ displayName: string | null;
10
+ photoURL: string | null;
11
+ isAnonymous: boolean;
12
+ createdAt: Date | null;
13
+ lastLoginAt: Date | null;
14
+ }
15
+
16
+ export interface UpdateProfileParams {
17
+ displayName?: string;
18
+ photoURL?: string;
19
+ }
package/src/index.ts CHANGED
@@ -99,12 +99,26 @@ export type {
99
99
  export { useAuth } from './presentation/hooks/useAuth';
100
100
  export type { UseAuthResult } from './presentation/hooks/useAuth';
101
101
 
102
+ export { useUserProfile } from './presentation/hooks/useUserProfile';
103
+ export type { UserProfileData, UseUserProfileParams } from './presentation/hooks/useUserProfile';
104
+
105
+ export { useAccountManagement } from './presentation/hooks/useAccountManagement';
106
+ export type { UseAccountManagementReturn } from './presentation/hooks/useAccountManagement';
107
+
108
+ export { useProfileUpdate } from './presentation/hooks/useProfileUpdate';
109
+ export type { UseProfileUpdateReturn } from './presentation/hooks/useProfileUpdate';
110
+
111
+ export type { UserProfile, UpdateProfileParams } from './domain/entities/UserProfile';
112
+
102
113
  // =============================================================================
103
114
  // PRESENTATION LAYER - Screens & Navigation
104
115
  // =============================================================================
105
116
 
106
117
  export { LoginScreen } from './presentation/screens/LoginScreen';
107
118
  export { RegisterScreen } from './presentation/screens/RegisterScreen';
119
+ export { AccountScreen } from './presentation/screens/AccountScreen';
120
+ export type { AccountScreenConfig, AccountScreenProps } from './presentation/screens/AccountScreen';
121
+
108
122
  export { AuthNavigator } from './presentation/navigation/AuthNavigator';
109
123
  export type {
110
124
  AuthStackParamList,
@@ -128,6 +142,10 @@ export { PasswordMatchIndicator } from './presentation/components/PasswordMatchI
128
142
  export type { PasswordMatchIndicatorProps } from './presentation/components/PasswordMatchIndicator';
129
143
  export { AuthBottomSheet } from './presentation/components/AuthBottomSheet';
130
144
  export type { AuthBottomSheetProps } from './presentation/components/AuthBottomSheet';
145
+ export { ProfileSection } from './presentation/components/ProfileSection';
146
+ export type { ProfileSectionConfig, ProfileSectionProps } from './presentation/components/ProfileSection';
147
+ export { AccountActions } from './presentation/components/AccountActions';
148
+ export type { AccountActionsConfig, AccountActionsProps } from './presentation/components/AccountActions';
131
149
 
132
150
  // =============================================================================
133
151
  // PRESENTATION LAYER - Stores
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Account Actions Component
3
+ * Provides logout and delete account functionality
4
+ * Only shown for authenticated (non-anonymous) users
5
+ */
6
+
7
+ import React from "react";
8
+ import { View, Text, TouchableOpacity, StyleSheet, Alert } from "react-native";
9
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
10
+
11
+ export interface AccountActionsConfig {
12
+ logoutText?: string;
13
+ deleteAccountText?: string;
14
+ logoutConfirmTitle?: string;
15
+ logoutConfirmMessage?: string;
16
+ deleteConfirmTitle?: string;
17
+ deleteConfirmMessage?: string;
18
+ onLogout: () => Promise<void>;
19
+ onDeleteAccount: () => Promise<void>;
20
+ }
21
+
22
+ export interface AccountActionsProps {
23
+ config: AccountActionsConfig;
24
+ }
25
+
26
+ export const AccountActions: React.FC<AccountActionsProps> = ({ config }) => {
27
+ const tokens = useAppDesignTokens();
28
+ const {
29
+ logoutText = "Log Out",
30
+ deleteAccountText = "Delete Account",
31
+ logoutConfirmTitle = "Log Out?",
32
+ logoutConfirmMessage = "Are you sure you want to log out?",
33
+ deleteConfirmTitle = "Delete Account?",
34
+ deleteConfirmMessage =
35
+ "This will permanently delete your account and all data. This action cannot be undone.",
36
+ onLogout,
37
+ onDeleteAccount,
38
+ } = config;
39
+
40
+ const handleLogout = () => {
41
+ Alert.alert(logoutConfirmTitle, logoutConfirmMessage, [
42
+ { text: "Cancel", style: "cancel" },
43
+ {
44
+ text: logoutText,
45
+ style: "destructive",
46
+ onPress: async () => {
47
+ try {
48
+ await onLogout();
49
+ } catch (error) {
50
+ console.error("Logout failed:", error);
51
+ }
52
+ },
53
+ },
54
+ ]);
55
+ };
56
+
57
+ const handleDeleteAccount = () => {
58
+ Alert.alert(deleteConfirmTitle, deleteConfirmMessage, [
59
+ { text: "Cancel", style: "cancel" },
60
+ {
61
+ text: deleteAccountText,
62
+ style: "destructive",
63
+ onPress: async () => {
64
+ try {
65
+ await onDeleteAccount();
66
+ } catch (error) {
67
+ console.error("Delete account failed:", error);
68
+ }
69
+ },
70
+ },
71
+ ]);
72
+ };
73
+
74
+ return (
75
+ <View style={styles.container}>
76
+ {/* Logout */}
77
+ <TouchableOpacity
78
+ style={[styles.actionButton, { borderColor: tokens.colors.border }]}
79
+ onPress={handleLogout}
80
+ activeOpacity={0.7}
81
+ >
82
+ <Text style={[styles.actionIcon, { color: tokens.colors.error }]}>
83
+
84
+ </Text>
85
+ <Text style={[styles.actionText, { color: tokens.colors.error }]}>
86
+ {logoutText}
87
+ </Text>
88
+ <Text style={[styles.chevron, { color: tokens.colors.textTertiary }]}>
89
+
90
+ </Text>
91
+ </TouchableOpacity>
92
+
93
+ {/* Delete Account */}
94
+ <TouchableOpacity
95
+ style={[styles.actionButton, { borderColor: tokens.colors.border }]}
96
+ onPress={handleDeleteAccount}
97
+ activeOpacity={0.7}
98
+ >
99
+ <Text style={[styles.actionIcon, { color: tokens.colors.error }]}>
100
+ 🗑
101
+ </Text>
102
+ <Text style={[styles.actionText, { color: tokens.colors.error }]}>
103
+ {deleteAccountText}
104
+ </Text>
105
+ <Text style={[styles.chevron, { color: tokens.colors.textTertiary }]}>
106
+
107
+ </Text>
108
+ </TouchableOpacity>
109
+ </View>
110
+ );
111
+ };
112
+
113
+ const styles = StyleSheet.create({
114
+ container: {
115
+ gap: 12,
116
+ },
117
+ actionButton: {
118
+ flexDirection: "row",
119
+ alignItems: "center",
120
+ paddingVertical: 16,
121
+ paddingHorizontal: 16,
122
+ borderRadius: 12,
123
+ borderWidth: 1,
124
+ gap: 12,
125
+ },
126
+ actionIcon: {
127
+ fontSize: 20,
128
+ },
129
+ actionText: {
130
+ flex: 1,
131
+ fontSize: 16,
132
+ fontWeight: "500",
133
+ },
134
+ chevron: {
135
+ fontSize: 24,
136
+ fontWeight: "400",
137
+ },
138
+ });
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Profile Section Component
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
+ */
6
+
7
+ import React from "react";
8
+ import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
9
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
10
+
11
+ export interface ProfileSectionConfig {
12
+ displayName: string;
13
+ userId?: string;
14
+ isAnonymous: boolean;
15
+ avatarUrl?: string;
16
+ accountSettingsRoute?: string;
17
+ }
18
+
19
+ export interface ProfileSectionProps {
20
+ profile: ProfileSectionConfig;
21
+ onPress?: () => void;
22
+ onSignIn?: () => void;
23
+ signInText?: string;
24
+ anonymousText?: string;
25
+ }
26
+
27
+ export const ProfileSection: React.FC<ProfileSectionProps> = ({
28
+ profile,
29
+ onPress,
30
+ onSignIn,
31
+ signInText = "Sign In",
32
+ anonymousText = "Anonymous User",
33
+ }) => {
34
+ const tokens = useAppDesignTokens();
35
+
36
+ const handlePress = () => {
37
+ if (profile.isAnonymous && onSignIn) {
38
+ onSignIn();
39
+ } else if (onPress) {
40
+ onPress();
41
+ }
42
+ };
43
+
44
+ const getInitials = (name: string): string => {
45
+ return name
46
+ .split(" ")
47
+ .map((n) => n[0])
48
+ .join("")
49
+ .toUpperCase()
50
+ .slice(0, 2);
51
+ };
52
+
53
+ return (
54
+ <TouchableOpacity
55
+ style={[styles.container, { backgroundColor: tokens.colors.surface }]}
56
+ onPress={handlePress}
57
+ activeOpacity={0.7}
58
+ disabled={!onPress && !onSignIn}
59
+ >
60
+ <View style={styles.content}>
61
+ {/* Avatar */}
62
+ <View
63
+ style={[
64
+ styles.avatar,
65
+ { backgroundColor: profile.isAnonymous ? tokens.colors.warning : tokens.colors.primary },
66
+ ]}
67
+ >
68
+ {profile.avatarUrl ? (
69
+ <Text style={styles.avatarText}>
70
+ {getInitials(profile.displayName)}
71
+ </Text>
72
+ ) : (
73
+ <Text
74
+ style={[styles.avatarText, { color: tokens.colors.onPrimary }]}
75
+ >
76
+ {getInitials(profile.displayName)}
77
+ </Text>
78
+ )}
79
+ </View>
80
+
81
+ {/* User Info */}
82
+ <View style={styles.info}>
83
+ <Text
84
+ style={[styles.displayName, { color: tokens.colors.textPrimary }]}
85
+ numberOfLines={1}
86
+ >
87
+ {profile.displayName}
88
+ </Text>
89
+ {profile.userId && (
90
+ <Text
91
+ style={[styles.userId, { color: tokens.colors.textSecondary }]}
92
+ numberOfLines={1}
93
+ >
94
+ {profile.userId}
95
+ </Text>
96
+ )}
97
+ </View>
98
+
99
+ {/* Action Icon */}
100
+ {(onPress || onSignIn) && (
101
+ <Text style={[styles.chevron, { color: tokens.colors.textTertiary }]}>
102
+
103
+ </Text>)}
104
+ </View>
105
+
106
+ {/* Sign In CTA for Anonymous Users */}
107
+ {profile.isAnonymous && onSignIn && (
108
+ <View
109
+ style={[
110
+ styles.ctaContainer,
111
+ { borderTopColor: tokens.colors.border },
112
+ ]}
113
+ >
114
+ <TouchableOpacity
115
+ style={[
116
+ styles.ctaButton,
117
+ { backgroundColor: tokens.colors.primary },
118
+ ]}
119
+ onPress={onSignIn}
120
+ activeOpacity={0.8}
121
+ >
122
+ <Text
123
+ style={[
124
+ styles.ctaText,
125
+ { color: tokens.colors.onPrimary },
126
+ ]}
127
+ >
128
+ {signInText}
129
+ </Text>
130
+ </TouchableOpacity>
131
+ </View>
132
+ )}
133
+ </TouchableOpacity>
134
+ );
135
+ };
136
+
137
+ const styles = StyleSheet.create({
138
+ container: {
139
+ borderRadius: 12,
140
+ padding: 16,
141
+ marginBottom: 16,
142
+ },
143
+ content: {
144
+ flexDirection: "row",
145
+ alignItems: "center",
146
+ },
147
+ avatar: {
148
+ width: 56,
149
+ height: 56,
150
+ borderRadius: 28,
151
+ justifyContent: "center",
152
+ alignItems: "center",
153
+ marginRight: 12,
154
+ },
155
+ avatarText: {
156
+ fontSize: 20,
157
+ fontWeight: "600",
158
+ },
159
+ info: {
160
+ flex: 1,
161
+ },
162
+ displayName: {
163
+ fontSize: 18,
164
+ fontWeight: "600",
165
+ marginBottom: 2,
166
+ },
167
+ userId: {
168
+ fontSize: 13,
169
+ },
170
+ chevron: {
171
+ fontSize: 24,
172
+ fontWeight: "400",
173
+ },
174
+ ctaContainer: {
175
+ marginTop: 12,
176
+ paddingTop: 12,
177
+ borderTopWidth: 1,
178
+ },
179
+ ctaButton: {
180
+ paddingVertical: 12,
181
+ borderRadius: 8,
182
+ alignItems: "center",
183
+ },
184
+ ctaText: {
185
+ fontSize: 15,
186
+ fontWeight: "600",
187
+ },
188
+ });
@@ -0,0 +1,41 @@
1
+ /**
2
+ * useAccountManagement Hook
3
+ * Provides account management functionality (logout, delete)
4
+ */
5
+
6
+ import { useCallback } from "react";
7
+ import { useAuth } from "./useAuth";
8
+
9
+ export interface UseAccountManagementReturn {
10
+ logout: () => Promise<void>;
11
+ deleteAccount: () => Promise<void>;
12
+ isLoading: boolean;
13
+ }
14
+
15
+ export const useAccountManagement = (): UseAccountManagementReturn => {
16
+ const { user, loading, signOut } = useAuth();
17
+
18
+ const logout = useCallback(async () => {
19
+ await signOut();
20
+ }, [signOut]);
21
+
22
+ const deleteAccount = useCallback(async () => {
23
+ if (!user) {
24
+ throw new Error("No user logged in");
25
+ }
26
+
27
+ if (user.isAnonymous) {
28
+ throw new Error("Cannot delete anonymous account");
29
+ }
30
+
31
+ // Note: Add user deletion logic via Firebase Admin SDK on backend
32
+ // Frontend should call backend API to delete user account
33
+ throw new Error("Account deletion requires backend implementation");
34
+ }, [user]);
35
+
36
+ return {
37
+ logout,
38
+ deleteAccount,
39
+ isLoading: loading,
40
+ };
41
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * useProfileUpdate Hook
3
+ * Hook for profile updates - implementation should be provided by app
4
+ * Apps should use Firebase SDK directly or backend API
5
+ */
6
+
7
+ import { useState, useCallback } from "react";
8
+ import { useAuth } from "./useAuth";
9
+ import type { UpdateProfileParams } from "../../domain/entities/UserProfile";
10
+
11
+ export interface UseProfileUpdateReturn {
12
+ updateProfile: (params: UpdateProfileParams) => Promise<void>;
13
+ isUpdating: boolean;
14
+ error: string | null;
15
+ }
16
+
17
+ export const useProfileUpdate = (): UseProfileUpdateReturn => {
18
+ const { user } = useAuth();
19
+ const [isUpdating, setIsUpdating] = useState(false);
20
+ const [error, setError] = useState<string | null>(null);
21
+
22
+ const updateProfile = useCallback(
23
+ async (params: UpdateProfileParams) => {
24
+ if (!user) {
25
+ throw new Error("No user logged in");
26
+ }
27
+
28
+ if (user.isAnonymous) {
29
+ throw new Error("Anonymous users cannot update profile");
30
+ }
31
+
32
+ // Note: App should implement this via Firebase SDK
33
+ // Example: auth().currentUser?.updateProfile({ displayName, photoURL })
34
+ throw new Error("Profile update should be implemented by app");
35
+ },
36
+ [user],
37
+ );
38
+
39
+ return {
40
+ updateProfile,
41
+ isUpdating,
42
+ error,
43
+ };
44
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * useUserProfile Hook
3
+ * Generic hook for user profile configuration
4
+ * Returns profile data for display in settings or profile screens
5
+ */
6
+
7
+ import { useMemo } from "react";
8
+ import { useAuth } from "./useAuth";
9
+
10
+ export interface UserProfileData {
11
+ displayName: string;
12
+ userId?: string;
13
+ isAnonymous: boolean;
14
+ avatarUrl?: string;
15
+ accountSettingsRoute?: string;
16
+ }
17
+
18
+ export interface UseUserProfileParams {
19
+ anonymousDisplayName?: string;
20
+ guestDisplayName?: string;
21
+ accountRoute?: string;
22
+ }
23
+
24
+ export const useUserProfile = (
25
+ params?: UseUserProfileParams,
26
+ ): UserProfileData | undefined => {
27
+ const { user } = useAuth();
28
+
29
+ const anonymousName = params?.anonymousDisplayName || "Anonymous User";
30
+ const guestName = params?.guestDisplayName || "Guest User";
31
+ const accountRoute = params?.accountRoute || "Account";
32
+
33
+ return useMemo(() => {
34
+ if (!user) {
35
+ return undefined;
36
+ }
37
+
38
+ const isAnonymous = user.isAnonymous || false;
39
+
40
+ if (isAnonymous) {
41
+ return {
42
+ displayName: anonymousName,
43
+ userId: user.uid,
44
+ isAnonymous: true,
45
+ };
46
+ }
47
+
48
+ return {
49
+ accountSettingsRoute: accountRoute,
50
+ displayName: user.displayName || user.email || guestName,
51
+ userId: user.uid,
52
+ isAnonymous: false,
53
+ avatarUrl: user.photoURL || undefined,
54
+ };
55
+ }, [user, anonymousName, guestName, accountRoute]);
56
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Account Screen
3
+ * Pure UI component for account management
4
+ * Business logic provided via props from app layer
5
+ */
6
+
7
+ import React from "react";
8
+ import { View, ScrollView, StyleSheet } from "react-native";
9
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
10
+ import { ProfileSection, type ProfileSectionConfig } from "../components/ProfileSection";
11
+ import { AccountActions, type AccountActionsConfig } from "../components/AccountActions";
12
+
13
+ export interface AccountScreenConfig {
14
+ profile: ProfileSectionConfig;
15
+ accountActions: AccountActionsConfig;
16
+ }
17
+
18
+ export interface AccountScreenProps {
19
+ config: AccountScreenConfig;
20
+ }
21
+
22
+ export const AccountScreen: React.FC<AccountScreenProps> = ({ config }) => {
23
+ const tokens = useAppDesignTokens();
24
+
25
+ return (
26
+ <ScrollView
27
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
28
+ contentContainerStyle={styles.content}
29
+ >
30
+ <ProfileSection profile={config.profile} />
31
+ <View style={styles.divider} />
32
+ <AccountActions config={config.accountActions} />
33
+ </ScrollView>
34
+ );
35
+ };
36
+
37
+ const styles = StyleSheet.create({
38
+ container: {
39
+ flex: 1,
40
+ },
41
+ content: {
42
+ padding: 16,
43
+ },
44
+ divider: {
45
+ height: 24,
46
+ },
47
+ });