@umituz/react-native-auth 1.8.1 → 1.10.0

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": "1.8.1",
3
+ "version": "1.10.0",
4
4
  "description": "Authentication service for React Native apps - Secure, type-safe, and production-ready. Provider-agnostic design supports Firebase Auth and can be adapted for Supabase or other providers.",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -37,6 +37,11 @@ export interface IAuthService {
37
37
  */
38
38
  setGuestMode(): Promise<void>;
39
39
 
40
+ /**
41
+ * Check if currently in guest mode
42
+ */
43
+ getIsGuestMode(): boolean;
44
+
40
45
  /**
41
46
  * Get current authenticated user
42
47
  */
@@ -19,6 +19,7 @@ import type {
19
19
  } from "../../application/ports/IAuthProvider";
20
20
  import type { AuthUser } from "../../domain/entities/AuthUser";
21
21
  import { mapFirebaseAuthError } from "../utils/AuthErrorMapper";
22
+ import { mapToAuthUser } from "../utils/UserMapper";
22
23
 
23
24
  export class FirebaseAuthProvider implements IAuthProvider {
24
25
  private auth: Auth | null = null;
@@ -54,7 +55,7 @@ export class FirebaseAuthProvider implements IAuthProvider {
54
55
  credentials.email.trim(),
55
56
  credentials.password
56
57
  );
57
- return this.mapFirebaseUser(userCredential.user);
58
+ return mapToAuthUser(userCredential.user) as AuthUser;
58
59
  } catch (error: unknown) {
59
60
  throw mapFirebaseAuthError(error);
60
61
  }
@@ -82,7 +83,7 @@ export class FirebaseAuthProvider implements IAuthProvider {
82
83
  }
83
84
  }
84
85
 
85
- return this.mapFirebaseUser(userCredential.user);
86
+ return mapToAuthUser(userCredential.user) as AuthUser;
86
87
  } catch (error: unknown) {
87
88
  throw mapFirebaseAuthError(error);
88
89
  }
@@ -104,7 +105,7 @@ export class FirebaseAuthProvider implements IAuthProvider {
104
105
  if (!this.auth?.currentUser) {
105
106
  return null;
106
107
  }
107
- return this.mapFirebaseUser(this.auth.currentUser);
108
+ return mapToAuthUser(this.auth.currentUser);
108
109
  }
109
110
 
110
111
  onAuthStateChange(callback: (user: AuthUser | null) => void): () => void {
@@ -114,18 +115,7 @@ export class FirebaseAuthProvider implements IAuthProvider {
114
115
  }
115
116
 
116
117
  return onAuthStateChanged(this.auth, (user) => {
117
- callback(user ? this.mapFirebaseUser(user) : null);
118
+ callback(mapToAuthUser(user));
118
119
  });
119
120
  }
120
-
121
- private mapFirebaseUser(user: User): AuthUser {
122
- return {
123
- uid: user.uid,
124
- email: user.email,
125
- displayName: user.displayName,
126
- isAnonymous: user.isAnonymous,
127
- emailVerified: user.emailVerified,
128
- photoURL: user.photoURL,
129
- };
130
- }
131
121
  }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * User Mapper
3
+ * Single Source of Truth for user object transformations
4
+ */
5
+
6
+ import type { AuthUser } from "../../domain/entities/AuthUser";
7
+
8
+ interface FirebaseUserLike {
9
+ uid: string;
10
+ email: string | null;
11
+ displayName: string | null;
12
+ isAnonymous: boolean;
13
+ emailVerified: boolean;
14
+ photoURL: string | null;
15
+ }
16
+
17
+ export function mapToAuthUser(user: FirebaseUserLike | null): AuthUser | null {
18
+ if (!user) return null;
19
+ return {
20
+ uid: user.uid,
21
+ email: user.email,
22
+ displayName: user.displayName,
23
+ isAnonymous: user.isAnonymous,
24
+ emailVerified: user.emailVerified,
25
+ photoURL: user.photoURL,
26
+ };
27
+ }
@@ -3,26 +3,13 @@
3
3
  * Single Responsibility: Manage authentication state
4
4
  */
5
5
 
6
- import { useState, useEffect } from "react";
6
+ import { useState, useEffect, useRef } from "react";
7
7
  import { DeviceEventEmitter } from "react-native";
8
8
  import { getAuthService } from "../../infrastructure/services/AuthService";
9
9
  import { useFirebaseAuth } from "@umituz/react-native-firebase-auth";
10
+ import { mapToAuthUser } from "../../infrastructure/utils/UserMapper";
10
11
  import type { AuthUser } from "../../domain/entities/AuthUser";
11
12
 
12
- type FirebaseUser = ReturnType<typeof useFirebaseAuth>["user"];
13
-
14
- function mapToAuthUser(user: FirebaseUser): AuthUser | null {
15
- if (!user) return null;
16
- return {
17
- uid: user.uid,
18
- email: user.email,
19
- displayName: user.displayName,
20
- isAnonymous: user.isAnonymous,
21
- emailVerified: user.emailVerified,
22
- photoURL: user.photoURL,
23
- };
24
- }
25
-
26
13
  export interface UseAuthStateResult {
27
14
  user: AuthUser | null;
28
15
  isAuthenticated: boolean;
@@ -46,9 +33,17 @@ export function useAuthState(): UseAuthStateResult {
46
33
  const [error, setError] = useState<string | null>(null);
47
34
  const [loading, setLoading] = useState(false);
48
35
 
36
+ // Ref to track latest isGuest value for event handlers
37
+ const isGuestRef = useRef(isGuest);
38
+
49
39
  const user = isGuest ? null : mapToAuthUser(firebaseUser);
50
40
  const isAuthenticated = !!user && !isGuest;
51
41
 
42
+ // Keep ref in sync with state
43
+ useEffect(() => {
44
+ isGuestRef.current = isGuest;
45
+ }, [isGuest]);
46
+
52
47
  // Reset guest mode when user signs in
53
48
  useEffect(() => {
54
49
  if (firebaseUser && isGuest) {
@@ -56,38 +51,28 @@ export function useAuthState(): UseAuthStateResult {
56
51
  }
57
52
  }, [firebaseUser, isGuest]);
58
53
 
59
- // Sync isGuest state with service on mount
54
+ // Sync isGuest state with service on mount only
60
55
  useEffect(() => {
61
56
  const service = getAuthService();
62
57
  if (service) {
63
58
  const serviceIsGuest = service.getIsGuestMode();
64
- if (serviceIsGuest !== isGuest) {
59
+ if (serviceIsGuest !== isGuestRef.current) {
65
60
  setIsGuest(serviceIsGuest);
66
61
  }
67
62
  }
68
63
  }, []);
69
64
 
70
- // Listen for guest-mode-enabled event
65
+ // Listen for auth events - subscribe once on mount
71
66
  useEffect(() => {
72
67
  const guestSubscription = DeviceEventEmitter.addListener(
73
68
  "guest-mode-enabled",
74
- () => {
75
- /* eslint-disable-next-line no-console */
76
- if (__DEV__) {
77
- console.log("[useAuthState] Guest mode enabled event received");
78
- }
79
- setIsGuest(true);
80
- }
69
+ () => setIsGuest(true)
81
70
  );
82
71
 
83
72
  const authSubscription = DeviceEventEmitter.addListener(
84
73
  "user-authenticated",
85
74
  () => {
86
- /* eslint-disable-next-line no-console */
87
- if (__DEV__) {
88
- console.log("[useAuthState] User authenticated event received");
89
- }
90
- if (isGuest) {
75
+ if (isGuestRef.current) {
91
76
  setIsGuest(false);
92
77
  }
93
78
  }
@@ -97,7 +82,7 @@ export function useAuthState(): UseAuthStateResult {
97
82
  guestSubscription.remove();
98
83
  authSubscription.remove();
99
84
  };
100
- }, [isGuest]);
85
+ }, []);
101
86
 
102
87
  return {
103
88
  user,
@@ -7,14 +7,7 @@ import { useState, useCallback } from "react";
7
7
  import { useLocalization } from "@umituz/react-native-localization";
8
8
  import { useAuth } from "./useAuth";
9
9
  import { getAuthErrorLocalizationKey } from "../utils/getAuthErrorMessage";
10
-
11
- /**
12
- * Validate email format
13
- */
14
- function validateEmail(emailValue: string): boolean {
15
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
16
- return emailRegex.test(emailValue);
17
- }
10
+ import { validateEmail } from "../../infrastructure/utils/AuthValidation";
18
11
 
19
12
  export interface UseLoginFormResult {
20
13
  email: string;
@@ -66,10 +59,8 @@ export function useLoginForm(): UseLoginFormResult {
66
59
 
67
60
  let hasError = false;
68
61
 
69
- if (!email.trim()) {
70
- setEmailError(t("auth.errors.invalidEmail"));
71
- hasError = true;
72
- } else if (!validateEmail(email.trim())) {
62
+ const emailResult = validateEmail(email.trim());
63
+ if (!emailResult.isValid) {
73
64
  setEmailError(t("auth.errors.invalidEmail"));
74
65
  hasError = true;
75
66
  }
@@ -3,14 +3,13 @@
3
3
  * Maps error codes to localization keys
4
4
  */
5
5
 
6
- import type { AuthError } from "../../domain/errors/AuthError";
6
+ import { AuthError } from "../../domain/errors/AuthError";
7
7
 
8
8
  /**
9
9
  * Map AuthError code to localization key
10
10
  */
11
11
  export function getAuthErrorLocalizationKey(error: Error): string {
12
- // Check if error has code property
13
- const code = (error as any).code;
12
+ const code = error instanceof AuthError ? error.code : undefined;
14
13
 
15
14
  // Map error codes to localization keys
16
15
  const errorCodeMap: Record<string, string> = {
@@ -75,14 +74,3 @@ export function getAuthErrorLocalizationKey(error: Error): string {
75
74
  // Default to unknown error
76
75
  return "auth.errors.unknownError";
77
76
  }
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-