@umituz/react-native-auth 1.12.0 → 2.0.2

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.
Files changed (136) hide show
  1. package/README.md +0 -0
  2. package/lib/__tests__/services/AuthCoreService.test.d.ts +4 -0
  3. package/lib/__tests__/services/AuthCoreService.test.js +198 -0
  4. package/lib/__tests__/services/AuthPackage.test.d.ts +4 -0
  5. package/lib/__tests__/services/AuthPackage.test.js +177 -0
  6. package/lib/__tests__/services/GuestModeService.test.d.ts +4 -0
  7. package/lib/__tests__/services/GuestModeService.test.js +141 -0
  8. package/lib/__tests__/utils/AuthValidation.test.d.ts +4 -0
  9. package/lib/__tests__/utils/AuthValidation.test.js +222 -0
  10. package/lib/application/ports/IAuthProvider.d.ts +42 -0
  11. package/lib/application/ports/IAuthProvider.js +5 -0
  12. package/lib/application/ports/IAuthService.d.ts +48 -0
  13. package/lib/application/ports/IAuthService.js +5 -0
  14. package/lib/domain/entities/AuthUser.d.ts +12 -0
  15. package/lib/domain/entities/AuthUser.js +5 -0
  16. package/lib/domain/errors/AuthError.d.ts +36 -0
  17. package/lib/domain/errors/AuthError.js +76 -0
  18. package/lib/domain/value-objects/AuthConfig.d.ts +16 -0
  19. package/lib/domain/value-objects/AuthConfig.js +14 -0
  20. package/lib/index.d.ts +45 -0
  21. package/lib/index.js +59 -0
  22. package/lib/infrastructure/adapters/StorageProviderAdapter.d.ts +16 -0
  23. package/lib/infrastructure/adapters/StorageProviderAdapter.js +72 -0
  24. package/lib/infrastructure/adapters/UIProviderAdapter.d.ts +18 -0
  25. package/lib/infrastructure/adapters/UIProviderAdapter.js +28 -0
  26. package/lib/infrastructure/providers/FirebaseAuthProvider.d.ts +19 -0
  27. package/lib/infrastructure/providers/FirebaseAuthProvider.js +94 -0
  28. package/lib/infrastructure/services/AuthCoreService.d.ts +22 -0
  29. package/lib/infrastructure/services/AuthCoreService.js +102 -0
  30. package/lib/infrastructure/services/AuthEventService.d.ts +28 -0
  31. package/lib/infrastructure/services/AuthEventService.js +88 -0
  32. package/lib/infrastructure/services/AuthPackage.d.ts +62 -0
  33. package/lib/infrastructure/services/AuthPackage.js +91 -0
  34. package/lib/infrastructure/services/AuthService.d.ts +42 -0
  35. package/lib/infrastructure/services/AuthService.js +123 -0
  36. package/lib/infrastructure/services/GuestModeService.d.ts +23 -0
  37. package/lib/infrastructure/services/GuestModeService.js +69 -0
  38. package/lib/infrastructure/storage/GuestModeStorage.d.ts +16 -0
  39. package/lib/infrastructure/storage/GuestModeStorage.js +73 -0
  40. package/lib/infrastructure/utils/AuthErrorMapper.d.ts +8 -0
  41. package/lib/infrastructure/utils/AuthErrorMapper.js +51 -0
  42. package/lib/infrastructure/utils/AuthEventEmitter.d.ts +12 -0
  43. package/lib/infrastructure/utils/AuthEventEmitter.js +25 -0
  44. package/lib/infrastructure/utils/AuthValidation.d.ts +49 -0
  45. package/lib/infrastructure/utils/AuthValidation.js +133 -0
  46. package/lib/infrastructure/utils/UserMapper.d.ts +15 -0
  47. package/lib/infrastructure/utils/UserMapper.js +16 -0
  48. package/lib/presentation/components/AuthContainer.d.ts +10 -0
  49. package/lib/presentation/components/AuthContainer.js +27 -0
  50. package/lib/presentation/components/AuthDivider.d.ts +6 -0
  51. package/lib/presentation/components/AuthDivider.js +36 -0
  52. package/lib/presentation/components/AuthErrorDisplay.d.ts +10 -0
  53. package/lib/presentation/components/AuthErrorDisplay.js +24 -0
  54. package/lib/presentation/components/AuthFormCard.d.ts +10 -0
  55. package/lib/presentation/components/AuthFormCard.js +19 -0
  56. package/lib/presentation/components/AuthGradientBackground.d.ts +6 -0
  57. package/lib/presentation/components/AuthGradientBackground.js +8 -0
  58. package/lib/presentation/components/AuthHeader.d.ts +11 -0
  59. package/lib/presentation/components/AuthHeader.js +38 -0
  60. package/lib/presentation/components/AuthLegalLinks.d.ts +28 -0
  61. package/lib/presentation/components/AuthLegalLinks.js +54 -0
  62. package/lib/presentation/components/AuthLink.d.ts +13 -0
  63. package/lib/presentation/components/AuthLink.js +27 -0
  64. package/lib/presentation/components/LoginForm.d.ts +10 -0
  65. package/lib/presentation/components/LoginForm.js +27 -0
  66. package/lib/presentation/components/PasswordMatchIndicator.d.ts +9 -0
  67. package/lib/presentation/components/PasswordMatchIndicator.js +30 -0
  68. package/lib/presentation/components/PasswordStrengthIndicator.d.ts +11 -0
  69. package/lib/presentation/components/PasswordStrengthIndicator.js +60 -0
  70. package/lib/presentation/components/RegisterForm.d.ts +14 -0
  71. package/lib/presentation/components/RegisterForm.js +30 -0
  72. package/lib/presentation/hooks/useAuth.d.ts +44 -0
  73. package/lib/presentation/hooks/useAuth.js +38 -0
  74. package/lib/presentation/hooks/useAuthActions.d.ts +15 -0
  75. package/lib/presentation/hooks/useAuthActions.js +162 -0
  76. package/lib/presentation/hooks/useAuthState.d.ts +19 -0
  77. package/lib/presentation/hooks/useAuthState.js +79 -0
  78. package/lib/presentation/hooks/useLoginForm.d.ts +21 -0
  79. package/lib/presentation/hooks/useLoginForm.js +131 -0
  80. package/lib/presentation/hooks/useRegisterForm.d.ts +31 -0
  81. package/lib/presentation/hooks/useRegisterForm.js +136 -0
  82. package/lib/presentation/navigation/AuthNavigator.d.ts +28 -0
  83. package/lib/presentation/navigation/AuthNavigator.js +37 -0
  84. package/lib/presentation/screens/LoginScreen.d.ts +6 -0
  85. package/lib/presentation/screens/LoginScreen.js +15 -0
  86. package/lib/presentation/screens/RegisterScreen.d.ts +12 -0
  87. package/lib/presentation/screens/RegisterScreen.js +15 -0
  88. package/lib/presentation/utils/getAuthErrorMessage.d.ts +8 -0
  89. package/lib/presentation/utils/getAuthErrorMessage.js +69 -0
  90. package/package.json +12 -4
  91. package/src/__tests__/services/AuthCoreService.test.ts +247 -0
  92. package/src/__tests__/services/AuthPackage.test.ts +226 -0
  93. package/src/__tests__/services/GuestModeService.test.ts +194 -0
  94. package/src/__tests__/utils/AuthValidation.test.ts +270 -0
  95. package/src/application/ports/IAuthProvider.ts +0 -0
  96. package/src/application/ports/IAuthService.ts +0 -0
  97. package/src/domain/entities/AuthUser.ts +0 -0
  98. package/src/domain/errors/AuthError.ts +0 -0
  99. package/src/domain/value-objects/AuthConfig.ts +0 -0
  100. package/src/index.ts +0 -0
  101. package/src/infrastructure/adapters/StorageProviderAdapter.ts +73 -0
  102. package/src/infrastructure/adapters/UIProviderAdapter.ts +39 -0
  103. package/src/infrastructure/providers/FirebaseAuthProvider.ts +10 -2
  104. package/src/infrastructure/services/AuthCoreService.ts +138 -0
  105. package/src/infrastructure/services/AuthEventService.ts +115 -0
  106. package/src/infrastructure/services/AuthPackage.ts +148 -0
  107. package/src/infrastructure/services/AuthService.ts +62 -128
  108. package/src/infrastructure/services/GuestModeService.ts +86 -0
  109. package/src/infrastructure/storage/GuestModeStorage.ts +40 -14
  110. package/src/infrastructure/utils/AuthErrorMapper.ts +7 -3
  111. package/src/infrastructure/utils/AuthEventEmitter.ts +0 -0
  112. package/src/infrastructure/utils/AuthValidation.ts +47 -17
  113. package/src/infrastructure/utils/UserMapper.ts +0 -0
  114. package/src/presentation/components/AuthContainer.tsx +0 -0
  115. package/src/presentation/components/AuthDivider.tsx +0 -0
  116. package/src/presentation/components/AuthErrorDisplay.tsx +0 -0
  117. package/src/presentation/components/AuthFormCard.tsx +0 -0
  118. package/src/presentation/components/AuthGradientBackground.tsx +0 -0
  119. package/src/presentation/components/AuthHeader.tsx +0 -0
  120. package/src/presentation/components/AuthLegalLinks.tsx +0 -0
  121. package/src/presentation/components/AuthLink.tsx +0 -0
  122. package/src/presentation/components/LoginForm.tsx +0 -0
  123. package/src/presentation/components/PasswordMatchIndicator.tsx +2 -2
  124. package/src/presentation/components/PasswordStrengthIndicator.tsx +0 -0
  125. package/src/presentation/components/RegisterForm.tsx +0 -0
  126. package/src/presentation/hooks/useAuth.ts +0 -0
  127. package/src/presentation/hooks/useAuthActions.ts +8 -11
  128. package/src/presentation/hooks/useAuthState.ts +10 -0
  129. package/src/presentation/hooks/useLoginForm.ts +0 -0
  130. package/src/presentation/hooks/useRegisterForm.ts +16 -17
  131. package/src/presentation/navigation/AuthNavigator.tsx +2 -2
  132. package/src/presentation/screens/LoginScreen.tsx +3 -6
  133. package/src/presentation/screens/RegisterScreen.tsx +3 -6
  134. package/src/presentation/utils/getAuthErrorMessage.ts +0 -0
  135. package/src/types/external.d.ts +68 -0
  136. package/LICENSE +0 -22
@@ -0,0 +1,162 @@
1
+ /**
2
+ * useAuthActions Hook
3
+ * Single Responsibility: Handle authentication actions
4
+ */
5
+ import { useCallback } from "react";
6
+ import { getAuthService } from "../../infrastructure/services/AuthService";
7
+ /**
8
+ * Hook for authentication actions
9
+ */
10
+ export function useAuthActions(state) {
11
+ const { isGuest, setIsGuest, setLoading, setError } = state;
12
+ const signUp = useCallback(async (email, password, displayName) => {
13
+ const service = getAuthService();
14
+ if (!service) {
15
+ const err = "Auth service is not initialized";
16
+ setError(err);
17
+ throw new Error(err);
18
+ }
19
+ try {
20
+ setLoading(true);
21
+ setError(null);
22
+ await service.signUp({ email, password, displayName });
23
+ if (isGuest) {
24
+ setIsGuest(false);
25
+ }
26
+ }
27
+ catch (err) {
28
+ const errorMessage = err instanceof Error ? err.message : "Sign up failed";
29
+ setError(errorMessage);
30
+ throw err;
31
+ }
32
+ finally {
33
+ setLoading(false);
34
+ }
35
+ }, [isGuest, setIsGuest, setLoading, setError]);
36
+ const signIn = useCallback(async (email, password) => {
37
+ /* eslint-disable-next-line no-console */
38
+ if (__DEV__) {
39
+ console.log("[useAuthActions] signIn called with email:", email);
40
+ }
41
+ const service = getAuthService();
42
+ if (!service) {
43
+ const err = "Auth service is not initialized";
44
+ setError(err);
45
+ throw new Error(err);
46
+ }
47
+ try {
48
+ setLoading(true);
49
+ setError(null);
50
+ /* eslint-disable-next-line no-console */
51
+ if (__DEV__) {
52
+ console.log("[useAuthActions] Calling service.signIn()");
53
+ }
54
+ await service.signIn({ email, password });
55
+ /* eslint-disable-next-line no-console */
56
+ if (__DEV__) {
57
+ console.log("[useAuthActions] Service.signIn() completed successfully");
58
+ }
59
+ if (isGuest) {
60
+ /* eslint-disable-next-line no-console */
61
+ if (__DEV__) {
62
+ console.log("[useAuthActions] Clearing guest mode after sign in");
63
+ }
64
+ setIsGuest(false);
65
+ }
66
+ }
67
+ catch (err) {
68
+ const errorMessage = err instanceof Error ? err.message : "Sign in failed";
69
+ setError(errorMessage);
70
+ throw err;
71
+ }
72
+ finally {
73
+ setLoading(false);
74
+ }
75
+ }, [isGuest, setIsGuest, setLoading, setError]);
76
+ const signOut = useCallback(async () => {
77
+ const service = getAuthService();
78
+ if (!service) {
79
+ return;
80
+ }
81
+ try {
82
+ setLoading(true);
83
+ await service.signOut();
84
+ }
85
+ finally {
86
+ setLoading(false);
87
+ }
88
+ }, [setLoading]);
89
+ const continueAsGuest = useCallback(async () => {
90
+ /* eslint-disable-next-line no-console */
91
+ if (__DEV__) {
92
+ console.log("========================================");
93
+ console.log("[useAuthActions] 🎯 continueAsGuest() CALLED");
94
+ console.log("[useAuthActions] Current state:", {
95
+ isGuest,
96
+ loading: state.loading,
97
+ });
98
+ console.log("========================================");
99
+ }
100
+ const service = getAuthService();
101
+ if (!service) {
102
+ /* eslint-disable-next-line no-console */
103
+ if (__DEV__) {
104
+ console.log("[useAuthActions] ⚠️ No service available, setting isGuest directly");
105
+ }
106
+ setIsGuest(true);
107
+ return;
108
+ }
109
+ try {
110
+ /* eslint-disable-next-line no-console */
111
+ if (__DEV__) {
112
+ console.log("[useAuthActions] Setting loading to true...");
113
+ }
114
+ setLoading(true);
115
+ /* eslint-disable-next-line no-console */
116
+ if (__DEV__) {
117
+ console.log("[useAuthActions] 📞 Calling service.setGuestMode()...");
118
+ }
119
+ await service.setGuestMode();
120
+ /* eslint-disable-next-line no-console */
121
+ if (__DEV__) {
122
+ console.log("[useAuthActions] ✅ Service.setGuestMode() completed successfully");
123
+ console.log("[useAuthActions] Setting isGuest to true...");
124
+ }
125
+ setIsGuest(true);
126
+ /* eslint-disable-next-line no-console */
127
+ if (__DEV__) {
128
+ console.log("[useAuthActions] ✅ isGuest set to true");
129
+ }
130
+ }
131
+ catch (error) {
132
+ const errorMessage = error instanceof Error ? error.message : "Failed to continue as guest";
133
+ setError(errorMessage);
134
+ if (__DEV__) {
135
+ console.error("[useAuthActions] ❌ ERROR in continueAsGuest:", error);
136
+ console.error("[useAuthActions] Error details:", {
137
+ message: errorMessage,
138
+ stack: error instanceof Error ? error.stack : undefined,
139
+ });
140
+ }
141
+ setIsGuest(true);
142
+ }
143
+ finally {
144
+ /* eslint-disable-next-line no-console */
145
+ if (__DEV__) {
146
+ console.log("[useAuthActions] Setting loading to false...");
147
+ }
148
+ setLoading(false);
149
+ /* eslint-disable-next-line no-console */
150
+ if (__DEV__) {
151
+ console.log("[useAuthActions] ✅ continueAsGuest() FINISHED");
152
+ console.log("========================================");
153
+ }
154
+ }
155
+ }, [isGuest, setIsGuest, setLoading, state.loading]);
156
+ return {
157
+ signUp,
158
+ signIn,
159
+ signOut,
160
+ continueAsGuest,
161
+ };
162
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * useAuthState Hook
3
+ * Single Responsibility: Manage authentication state
4
+ */
5
+ import type { AuthUser } from "../../domain/entities/AuthUser";
6
+ export interface UseAuthStateResult {
7
+ user: AuthUser | null;
8
+ isAuthenticated: boolean;
9
+ isGuest: boolean;
10
+ loading: boolean;
11
+ error: string | null;
12
+ setError: (error: string | null) => void;
13
+ setIsGuest: (isGuest: boolean) => void;
14
+ setLoading: (loading: boolean) => void;
15
+ }
16
+ /**
17
+ * Hook for managing authentication state
18
+ */
19
+ export declare function useAuthState(): UseAuthStateResult;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * useAuthState Hook
3
+ * Single Responsibility: Manage authentication state
4
+ */
5
+ import { useState, useEffect, useRef, useMemo } from "react";
6
+ import { DeviceEventEmitter } from "react-native";
7
+ import { getAuthService } from "../../infrastructure/services/AuthService";
8
+ import { useFirebaseAuth } from "@umituz/react-native-firebase-auth";
9
+ import { mapToAuthUser } from "../../infrastructure/utils/UserMapper";
10
+ /**
11
+ * Hook for managing authentication state
12
+ */
13
+ export function useAuthState() {
14
+ const { user: firebaseUser, loading: firebaseLoading } = useFirebaseAuth();
15
+ const [isGuest, setIsGuest] = useState(() => {
16
+ const service = getAuthService();
17
+ return service ? service.getIsGuestMode() : false;
18
+ });
19
+ const [error, setError] = useState(null);
20
+ const [loading, setLoading] = useState(false);
21
+ // Ref to track latest isGuest value for event handlers
22
+ const isGuestRef = useRef(isGuest);
23
+ // Memoize user to prevent new object reference on every render
24
+ const user = useMemo(() => {
25
+ if (isGuest)
26
+ return null;
27
+ return mapToAuthUser(firebaseUser);
28
+ }, [isGuest, firebaseUser?.uid]);
29
+ const isAuthenticated = !!user && !isGuest;
30
+ // Keep ref in sync with state
31
+ useEffect(() => {
32
+ isGuestRef.current = isGuest;
33
+ }, [isGuest]);
34
+ // Reset guest mode when user signs in
35
+ useEffect(() => {
36
+ if (firebaseUser && isGuest) {
37
+ setIsGuest(false);
38
+ }
39
+ }, [firebaseUser, isGuest]);
40
+ // Sync isGuest state with service on mount only
41
+ useEffect(() => {
42
+ const service = getAuthService();
43
+ if (service) {
44
+ const serviceIsGuest = service.getIsGuestMode();
45
+ if (serviceIsGuest !== isGuestRef.current) {
46
+ setIsGuest(serviceIsGuest);
47
+ }
48
+ }
49
+ }, []);
50
+ // Listen for auth events - subscribe once on mount
51
+ useEffect(() => {
52
+ const guestSubscription = DeviceEventEmitter.addListener("guest-mode-enabled", () => setIsGuest(true));
53
+ const authSubscription = DeviceEventEmitter.addListener("user-authenticated", () => {
54
+ if (isGuestRef.current) {
55
+ setIsGuest(false);
56
+ }
57
+ });
58
+ const errorSubscription = DeviceEventEmitter.addListener("auth-error", (payload) => {
59
+ if (payload?.error) {
60
+ setError(payload.error);
61
+ }
62
+ });
63
+ return () => {
64
+ guestSubscription.remove();
65
+ authSubscription.remove();
66
+ errorSubscription.remove();
67
+ };
68
+ }, []);
69
+ return {
70
+ user,
71
+ isAuthenticated,
72
+ isGuest,
73
+ loading: loading || firebaseLoading,
74
+ error,
75
+ setError,
76
+ setIsGuest,
77
+ setLoading,
78
+ };
79
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * useLoginForm Hook
3
+ * Single Responsibility: Handle login form logic
4
+ */
5
+ export interface UseLoginFormResult {
6
+ email: string;
7
+ password: string;
8
+ emailError: string | null;
9
+ passwordError: string | null;
10
+ localError: string | null;
11
+ loading: boolean;
12
+ handleEmailChange: (text: string) => void;
13
+ handlePasswordChange: (text: string) => void;
14
+ handleSignIn: () => Promise<void>;
15
+ handleContinueAsGuest: () => Promise<void>;
16
+ displayError: string | null;
17
+ }
18
+ /**
19
+ * Hook for login form logic
20
+ */
21
+ export declare function useLoginForm(): UseLoginFormResult;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * useLoginForm Hook
3
+ * Single Responsibility: Handle login form logic
4
+ */
5
+ import { useState, useCallback } from "react";
6
+ import { useLocalization } from "@umituz/react-native-localization";
7
+ import { useAuth } from "./useAuth";
8
+ import { getAuthErrorLocalizationKey } from "../utils/getAuthErrorMessage";
9
+ import { validateEmail } from "../../infrastructure/utils/AuthValidation";
10
+ /**
11
+ * Hook for login form logic
12
+ */
13
+ export function useLoginForm() {
14
+ const { t } = useLocalization();
15
+ const { signIn, loading, error, continueAsGuest } = useAuth();
16
+ const [email, setEmail] = useState("");
17
+ const [password, setPassword] = useState("");
18
+ const [emailError, setEmailError] = useState(null);
19
+ const [passwordError, setPasswordError] = useState(null);
20
+ const [localError, setLocalError] = useState(null);
21
+ const handleEmailChange = useCallback((text) => {
22
+ setEmail(text);
23
+ if (emailError)
24
+ setEmailError(null);
25
+ if (localError)
26
+ setLocalError(null);
27
+ }, [emailError, localError]);
28
+ const handlePasswordChange = useCallback((text) => {
29
+ setPassword(text);
30
+ if (passwordError)
31
+ setPasswordError(null);
32
+ if (localError)
33
+ setLocalError(null);
34
+ }, [passwordError, localError]);
35
+ const handleSignIn = useCallback(async () => {
36
+ /* eslint-disable-next-line no-console */
37
+ if (__DEV__) {
38
+ console.log("[useLoginForm] handleSignIn called");
39
+ }
40
+ setEmailError(null);
41
+ setPasswordError(null);
42
+ setLocalError(null);
43
+ let hasError = false;
44
+ const emailResult = validateEmail(email.trim());
45
+ if (!emailResult.isValid) {
46
+ setEmailError(t("auth.errors.invalidEmail"));
47
+ hasError = true;
48
+ }
49
+ if (!password.trim()) {
50
+ setPasswordError(t("auth.errors.weakPassword"));
51
+ hasError = true;
52
+ }
53
+ else if (password.length < 6) {
54
+ setPasswordError(t("auth.errors.weakPassword"));
55
+ hasError = true;
56
+ }
57
+ if (hasError) {
58
+ /* eslint-disable-next-line no-console */
59
+ if (__DEV__) {
60
+ console.log("[useLoginForm] Validation errors, returning early");
61
+ }
62
+ return;
63
+ }
64
+ try {
65
+ /* eslint-disable-next-line no-console */
66
+ if (__DEV__) {
67
+ console.log("[useLoginForm] Calling signIn with email:", email.trim());
68
+ }
69
+ await signIn(email.trim(), password);
70
+ /* eslint-disable-next-line no-console */
71
+ if (__DEV__) {
72
+ console.log("[useLoginForm] signIn completed successfully");
73
+ }
74
+ }
75
+ catch (err) {
76
+ /* eslint-disable-next-line no-console */
77
+ if (__DEV__) {
78
+ console.error("[useLoginForm] signIn error:", err);
79
+ }
80
+ const localizationKey = getAuthErrorLocalizationKey(err);
81
+ const errorMessage = t(localizationKey);
82
+ setLocalError(errorMessage);
83
+ }
84
+ }, [email, password, t, signIn]);
85
+ const handleContinueAsGuest = useCallback(async () => {
86
+ /* eslint-disable-next-line no-console */
87
+ if (__DEV__) {
88
+ console.log("========================================");
89
+ console.log("[useLoginForm] 🎯 Continue as Guest button PRESSED");
90
+ console.log("[useLoginForm] Current loading state:", loading);
91
+ console.log("[useLoginForm] Current error state:", error);
92
+ console.log("========================================");
93
+ }
94
+ try {
95
+ /* eslint-disable-next-line no-console */
96
+ if (__DEV__) {
97
+ console.log("[useLoginForm] Calling continueAsGuest() function...");
98
+ }
99
+ await continueAsGuest();
100
+ /* eslint-disable-next-line no-console */
101
+ if (__DEV__) {
102
+ console.log("[useLoginForm] ✅ continueAsGuest() completed successfully");
103
+ console.log("[useLoginForm] Current loading state after:", loading);
104
+ }
105
+ }
106
+ catch (err) {
107
+ /* eslint-disable-next-line no-console */
108
+ if (__DEV__) {
109
+ console.error("[useLoginForm] ❌ ERROR in continueAsGuest:", err);
110
+ console.error("[useLoginForm] Error details:", {
111
+ message: err instanceof Error ? err.message : String(err),
112
+ stack: err instanceof Error ? err.stack : undefined,
113
+ });
114
+ }
115
+ }
116
+ }, [continueAsGuest, loading, error]);
117
+ const displayError = localError || error;
118
+ return {
119
+ email,
120
+ password,
121
+ emailError,
122
+ passwordError,
123
+ localError,
124
+ loading,
125
+ handleEmailChange,
126
+ handlePasswordChange,
127
+ handleSignIn,
128
+ handleContinueAsGuest,
129
+ displayError,
130
+ };
131
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * useRegisterForm Hook
3
+ * Single Responsibility: Handle register form logic
4
+ */
5
+ import type { PasswordRequirements } from "../../infrastructure/utils/AuthValidation";
6
+ export interface UseRegisterFormResult {
7
+ displayName: string;
8
+ email: string;
9
+ password: string;
10
+ confirmPassword: string;
11
+ fieldErrors: {
12
+ displayName?: string;
13
+ email?: string;
14
+ password?: string;
15
+ confirmPassword?: string;
16
+ };
17
+ localError: string | null;
18
+ loading: boolean;
19
+ passwordRequirements: PasswordRequirements;
20
+ passwordsMatch: boolean;
21
+ handleDisplayNameChange: (text: string) => void;
22
+ handleEmailChange: (text: string) => void;
23
+ handlePasswordChange: (text: string) => void;
24
+ handleConfirmPasswordChange: (text: string) => void;
25
+ handleSignUp: () => Promise<void>;
26
+ displayError: string | null;
27
+ }
28
+ /**
29
+ * Hook for register form logic
30
+ */
31
+ export declare function useRegisterForm(): UseRegisterFormResult;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * useRegisterForm Hook
3
+ * Single Responsibility: Handle register form logic
4
+ */
5
+ import { useState, useCallback, useMemo } from "react";
6
+ import { useLocalization } from "@umituz/react-native-localization";
7
+ import { validateEmail, validatePasswordForRegister, validatePasswordConfirmation, } from "../../infrastructure/utils/AuthValidation";
8
+ import { DEFAULT_PASSWORD_CONFIG } from "../../domain/value-objects/AuthConfig";
9
+ import { useAuth } from "./useAuth";
10
+ import { getAuthErrorLocalizationKey } from "../utils/getAuthErrorMessage";
11
+ /**
12
+ * Hook for register form logic
13
+ */
14
+ export function useRegisterForm() {
15
+ const { t } = useLocalization();
16
+ const { signUp, loading, error } = useAuth();
17
+ const [displayName, setDisplayName] = useState("");
18
+ const [email, setEmail] = useState("");
19
+ const [password, setPassword] = useState("");
20
+ const [confirmPassword, setConfirmPassword] = useState("");
21
+ const [localError, setLocalError] = useState(null);
22
+ const [fieldErrors, setFieldErrors] = useState({});
23
+ const passwordRequirements = useMemo(() => {
24
+ if (!password) {
25
+ return {
26
+ hasMinLength: false,
27
+ hasUppercase: false,
28
+ hasLowercase: false,
29
+ hasNumber: false,
30
+ hasSpecialChar: false,
31
+ };
32
+ }
33
+ const result = validatePasswordForRegister(password, DEFAULT_PASSWORD_CONFIG);
34
+ return result.requirements;
35
+ }, [password]);
36
+ const passwordsMatch = useMemo(() => {
37
+ return password.length > 0 && password === confirmPassword;
38
+ }, [password, confirmPassword]);
39
+ const handleDisplayNameChange = useCallback((text) => {
40
+ setDisplayName(text);
41
+ setFieldErrors((prev) => {
42
+ const next = { ...prev };
43
+ if (next.displayName) {
44
+ delete next.displayName;
45
+ }
46
+ return next;
47
+ });
48
+ if (localError)
49
+ setLocalError(null);
50
+ }, [localError]);
51
+ const handleEmailChange = useCallback((text) => {
52
+ setEmail(text);
53
+ setFieldErrors((prev) => {
54
+ const next = { ...prev };
55
+ if (next.email) {
56
+ delete next.email;
57
+ }
58
+ return next;
59
+ });
60
+ if (localError)
61
+ setLocalError(null);
62
+ }, [localError]);
63
+ const handlePasswordChange = useCallback((text) => {
64
+ setPassword(text);
65
+ setFieldErrors((prev) => {
66
+ const next = { ...prev };
67
+ if (next.password) {
68
+ delete next.password;
69
+ }
70
+ if (next.confirmPassword) {
71
+ delete next.confirmPassword;
72
+ }
73
+ return next;
74
+ });
75
+ if (localError)
76
+ setLocalError(null);
77
+ }, [localError]);
78
+ const handleConfirmPasswordChange = useCallback((text) => {
79
+ setConfirmPassword(text);
80
+ setFieldErrors((prev) => {
81
+ const next = { ...prev };
82
+ if (next.confirmPassword) {
83
+ delete next.confirmPassword;
84
+ }
85
+ return next;
86
+ });
87
+ if (localError)
88
+ setLocalError(null);
89
+ }, [localError]);
90
+ const handleSignUp = useCallback(async () => {
91
+ setLocalError(null);
92
+ setFieldErrors({});
93
+ // Manual validation since batchValidate is not available
94
+ const emailResult = validateEmail(email.trim());
95
+ if (!emailResult.isValid) {
96
+ setFieldErrors((prev) => ({ ...prev, email: emailResult.error }));
97
+ return;
98
+ }
99
+ const passwordResult = validatePasswordForRegister(password, DEFAULT_PASSWORD_CONFIG);
100
+ if (!passwordResult.isValid) {
101
+ setFieldErrors((prev) => ({ ...prev, password: passwordResult.error }));
102
+ return;
103
+ }
104
+ const confirmResult = validatePasswordConfirmation(password, confirmPassword);
105
+ if (!confirmResult.isValid) {
106
+ setFieldErrors((prev) => ({ ...prev, confirmPassword: confirmResult.error }));
107
+ return;
108
+ }
109
+ try {
110
+ await signUp(email.trim(), password, displayName.trim() || undefined);
111
+ }
112
+ catch (err) {
113
+ const localizationKey = getAuthErrorLocalizationKey(err);
114
+ const errorMessage = t(localizationKey);
115
+ setLocalError(errorMessage);
116
+ }
117
+ }, [displayName, email, password, confirmPassword, signUp, t]);
118
+ const displayError = localError || error;
119
+ return {
120
+ displayName,
121
+ email,
122
+ password,
123
+ confirmPassword,
124
+ fieldErrors,
125
+ localError,
126
+ loading,
127
+ passwordRequirements,
128
+ passwordsMatch,
129
+ handleDisplayNameChange,
130
+ handleEmailChange,
131
+ handlePasswordChange,
132
+ handleConfirmPasswordChange,
133
+ handleSignUp,
134
+ displayError,
135
+ };
136
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Auth Navigator
3
+ * Stack navigator for authentication screens (Login, Register)
4
+ */
5
+ import React from "react";
6
+ export type AuthStackParamList = {
7
+ Login: undefined;
8
+ Register: undefined;
9
+ };
10
+ export interface AuthNavigatorProps {
11
+ /**
12
+ * Terms of Service URL
13
+ */
14
+ termsUrl?: string;
15
+ /**
16
+ * Privacy Policy URL
17
+ */
18
+ privacyUrl?: string;
19
+ /**
20
+ * Callback when Terms of Service is pressed
21
+ */
22
+ onTermsPress?: () => void;
23
+ /**
24
+ * Callback when Privacy Policy is pressed
25
+ */
26
+ onPrivacyPress?: () => void;
27
+ }
28
+ export declare const AuthNavigator: React.FC<AuthNavigatorProps>;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Auth Navigator
4
+ * Stack navigator for authentication screens (Login, Register)
5
+ */
6
+ import { useEffect, useState } from "react";
7
+ import { createStackNavigator } from "@react-navigation/stack";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
9
+ import { storageRepository } from "@umituz/react-native-storage";
10
+ import { unwrap } from "@umituz/react-native-storage";
11
+ import { LoginScreen } from "../screens/LoginScreen";
12
+ import { RegisterScreen } from "../screens/RegisterScreen";
13
+ const AuthStack = createStackNavigator();
14
+ const SHOW_REGISTER_KEY = "auth_show_register";
15
+ export const AuthNavigator = ({ termsUrl, privacyUrl, onTermsPress, onPrivacyPress, }) => {
16
+ const tokens = useAppDesignTokens();
17
+ const [initialRouteName, setInitialRouteName] = useState(undefined);
18
+ useEffect(() => {
19
+ storageRepository.getString(SHOW_REGISTER_KEY, "false").then((result) => {
20
+ const value = unwrap(result, "false");
21
+ if (value === "true") {
22
+ setInitialRouteName("Register");
23
+ storageRepository.removeItem(SHOW_REGISTER_KEY);
24
+ }
25
+ else {
26
+ setInitialRouteName("Login");
27
+ }
28
+ });
29
+ }, []);
30
+ if (initialRouteName === undefined) {
31
+ return null;
32
+ }
33
+ return (_jsxs(AuthStack.Navigator, { initialRouteName: initialRouteName, screenOptions: {
34
+ headerShown: false,
35
+ cardStyle: { backgroundColor: tokens.colors.backgroundPrimary },
36
+ }, children: [_jsx(AuthStack.Screen, { name: "Login", component: LoginScreen }), _jsx(AuthStack.Screen, { name: "Register", children: (props) => (_jsx(RegisterScreen, { ...props, termsUrl: termsUrl, privacyUrl: privacyUrl, onTermsPress: onTermsPress, onPrivacyPress: onPrivacyPress })) })] }));
37
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Login Screen
3
+ * Beautiful, production-ready login screen with email/password and guest mode
4
+ */
5
+ import React from "react";
6
+ export declare const LoginScreen: React.FC;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useNavigation } from "@react-navigation/native";
3
+ import { useLocalization } from "@umituz/react-native-localization";
4
+ import { AuthContainer } from "../components/AuthContainer";
5
+ import { AuthHeader } from "../components/AuthHeader";
6
+ import { AuthFormCard } from "../components/AuthFormCard";
7
+ import { LoginForm } from "../components/LoginForm";
8
+ export const LoginScreen = () => {
9
+ const { t } = useLocalization();
10
+ const navigation = useNavigation();
11
+ const handleNavigateToRegister = () => {
12
+ navigation.navigate("Register");
13
+ };
14
+ return (_jsxs(AuthContainer, { children: [_jsx(AuthHeader, { title: t("auth.title") }), _jsx(AuthFormCard, { children: _jsx(LoginForm, { onNavigateToRegister: handleNavigateToRegister }) })] }));
15
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Register Screen
3
+ * Beautiful, production-ready registration screen with validation
4
+ */
5
+ import React from "react";
6
+ export interface RegisterScreenProps {
7
+ termsUrl?: string;
8
+ privacyUrl?: string;
9
+ onTermsPress?: () => void;
10
+ onPrivacyPress?: () => void;
11
+ }
12
+ export declare const RegisterScreen: React.FC<RegisterScreenProps>;