@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.
- package/README.md +0 -0
- package/lib/__tests__/services/AuthCoreService.test.d.ts +4 -0
- package/lib/__tests__/services/AuthCoreService.test.js +198 -0
- package/lib/__tests__/services/AuthPackage.test.d.ts +4 -0
- package/lib/__tests__/services/AuthPackage.test.js +177 -0
- package/lib/__tests__/services/GuestModeService.test.d.ts +4 -0
- package/lib/__tests__/services/GuestModeService.test.js +141 -0
- package/lib/__tests__/utils/AuthValidation.test.d.ts +4 -0
- package/lib/__tests__/utils/AuthValidation.test.js +222 -0
- package/lib/application/ports/IAuthProvider.d.ts +42 -0
- package/lib/application/ports/IAuthProvider.js +5 -0
- package/lib/application/ports/IAuthService.d.ts +48 -0
- package/lib/application/ports/IAuthService.js +5 -0
- package/lib/domain/entities/AuthUser.d.ts +12 -0
- package/lib/domain/entities/AuthUser.js +5 -0
- package/lib/domain/errors/AuthError.d.ts +36 -0
- package/lib/domain/errors/AuthError.js +76 -0
- package/lib/domain/value-objects/AuthConfig.d.ts +16 -0
- package/lib/domain/value-objects/AuthConfig.js +14 -0
- package/lib/index.d.ts +45 -0
- package/lib/index.js +59 -0
- package/lib/infrastructure/adapters/StorageProviderAdapter.d.ts +16 -0
- package/lib/infrastructure/adapters/StorageProviderAdapter.js +72 -0
- package/lib/infrastructure/adapters/UIProviderAdapter.d.ts +18 -0
- package/lib/infrastructure/adapters/UIProviderAdapter.js +28 -0
- package/lib/infrastructure/providers/FirebaseAuthProvider.d.ts +19 -0
- package/lib/infrastructure/providers/FirebaseAuthProvider.js +94 -0
- package/lib/infrastructure/services/AuthCoreService.d.ts +22 -0
- package/lib/infrastructure/services/AuthCoreService.js +102 -0
- package/lib/infrastructure/services/AuthEventService.d.ts +28 -0
- package/lib/infrastructure/services/AuthEventService.js +88 -0
- package/lib/infrastructure/services/AuthPackage.d.ts +62 -0
- package/lib/infrastructure/services/AuthPackage.js +91 -0
- package/lib/infrastructure/services/AuthService.d.ts +42 -0
- package/lib/infrastructure/services/AuthService.js +123 -0
- package/lib/infrastructure/services/GuestModeService.d.ts +23 -0
- package/lib/infrastructure/services/GuestModeService.js +69 -0
- package/lib/infrastructure/storage/GuestModeStorage.d.ts +16 -0
- package/lib/infrastructure/storage/GuestModeStorage.js +73 -0
- package/lib/infrastructure/utils/AuthErrorMapper.d.ts +8 -0
- package/lib/infrastructure/utils/AuthErrorMapper.js +51 -0
- package/lib/infrastructure/utils/AuthEventEmitter.d.ts +12 -0
- package/lib/infrastructure/utils/AuthEventEmitter.js +25 -0
- package/lib/infrastructure/utils/AuthValidation.d.ts +49 -0
- package/lib/infrastructure/utils/AuthValidation.js +133 -0
- package/lib/infrastructure/utils/UserMapper.d.ts +15 -0
- package/lib/infrastructure/utils/UserMapper.js +16 -0
- package/lib/presentation/components/AuthContainer.d.ts +10 -0
- package/lib/presentation/components/AuthContainer.js +27 -0
- package/lib/presentation/components/AuthDivider.d.ts +6 -0
- package/lib/presentation/components/AuthDivider.js +36 -0
- package/lib/presentation/components/AuthErrorDisplay.d.ts +10 -0
- package/lib/presentation/components/AuthErrorDisplay.js +24 -0
- package/lib/presentation/components/AuthFormCard.d.ts +10 -0
- package/lib/presentation/components/AuthFormCard.js +19 -0
- package/lib/presentation/components/AuthGradientBackground.d.ts +6 -0
- package/lib/presentation/components/AuthGradientBackground.js +8 -0
- package/lib/presentation/components/AuthHeader.d.ts +11 -0
- package/lib/presentation/components/AuthHeader.js +38 -0
- package/lib/presentation/components/AuthLegalLinks.d.ts +28 -0
- package/lib/presentation/components/AuthLegalLinks.js +54 -0
- package/lib/presentation/components/AuthLink.d.ts +13 -0
- package/lib/presentation/components/AuthLink.js +27 -0
- package/lib/presentation/components/LoginForm.d.ts +10 -0
- package/lib/presentation/components/LoginForm.js +27 -0
- package/lib/presentation/components/PasswordMatchIndicator.d.ts +9 -0
- package/lib/presentation/components/PasswordMatchIndicator.js +30 -0
- package/lib/presentation/components/PasswordStrengthIndicator.d.ts +11 -0
- package/lib/presentation/components/PasswordStrengthIndicator.js +60 -0
- package/lib/presentation/components/RegisterForm.d.ts +14 -0
- package/lib/presentation/components/RegisterForm.js +30 -0
- package/lib/presentation/hooks/useAuth.d.ts +44 -0
- package/lib/presentation/hooks/useAuth.js +38 -0
- package/lib/presentation/hooks/useAuthActions.d.ts +15 -0
- package/lib/presentation/hooks/useAuthActions.js +162 -0
- package/lib/presentation/hooks/useAuthState.d.ts +19 -0
- package/lib/presentation/hooks/useAuthState.js +79 -0
- package/lib/presentation/hooks/useLoginForm.d.ts +21 -0
- package/lib/presentation/hooks/useLoginForm.js +131 -0
- package/lib/presentation/hooks/useRegisterForm.d.ts +31 -0
- package/lib/presentation/hooks/useRegisterForm.js +136 -0
- package/lib/presentation/navigation/AuthNavigator.d.ts +28 -0
- package/lib/presentation/navigation/AuthNavigator.js +37 -0
- package/lib/presentation/screens/LoginScreen.d.ts +6 -0
- package/lib/presentation/screens/LoginScreen.js +15 -0
- package/lib/presentation/screens/RegisterScreen.d.ts +12 -0
- package/lib/presentation/screens/RegisterScreen.js +15 -0
- package/lib/presentation/utils/getAuthErrorMessage.d.ts +8 -0
- package/lib/presentation/utils/getAuthErrorMessage.js +69 -0
- package/package.json +12 -4
- package/src/__tests__/services/AuthCoreService.test.ts +247 -0
- package/src/__tests__/services/AuthPackage.test.ts +226 -0
- package/src/__tests__/services/GuestModeService.test.ts +194 -0
- package/src/__tests__/utils/AuthValidation.test.ts +270 -0
- package/src/application/ports/IAuthProvider.ts +0 -0
- package/src/application/ports/IAuthService.ts +0 -0
- package/src/domain/entities/AuthUser.ts +0 -0
- package/src/domain/errors/AuthError.ts +0 -0
- package/src/domain/value-objects/AuthConfig.ts +0 -0
- package/src/index.ts +0 -0
- package/src/infrastructure/adapters/StorageProviderAdapter.ts +73 -0
- package/src/infrastructure/adapters/UIProviderAdapter.ts +39 -0
- package/src/infrastructure/providers/FirebaseAuthProvider.ts +10 -2
- package/src/infrastructure/services/AuthCoreService.ts +138 -0
- package/src/infrastructure/services/AuthEventService.ts +115 -0
- package/src/infrastructure/services/AuthPackage.ts +148 -0
- package/src/infrastructure/services/AuthService.ts +62 -128
- package/src/infrastructure/services/GuestModeService.ts +86 -0
- package/src/infrastructure/storage/GuestModeStorage.ts +40 -14
- package/src/infrastructure/utils/AuthErrorMapper.ts +7 -3
- package/src/infrastructure/utils/AuthEventEmitter.ts +0 -0
- package/src/infrastructure/utils/AuthValidation.ts +47 -17
- package/src/infrastructure/utils/UserMapper.ts +0 -0
- package/src/presentation/components/AuthContainer.tsx +0 -0
- package/src/presentation/components/AuthDivider.tsx +0 -0
- package/src/presentation/components/AuthErrorDisplay.tsx +0 -0
- package/src/presentation/components/AuthFormCard.tsx +0 -0
- package/src/presentation/components/AuthGradientBackground.tsx +0 -0
- package/src/presentation/components/AuthHeader.tsx +0 -0
- package/src/presentation/components/AuthLegalLinks.tsx +0 -0
- package/src/presentation/components/AuthLink.tsx +0 -0
- package/src/presentation/components/LoginForm.tsx +0 -0
- package/src/presentation/components/PasswordMatchIndicator.tsx +2 -2
- package/src/presentation/components/PasswordStrengthIndicator.tsx +0 -0
- package/src/presentation/components/RegisterForm.tsx +0 -0
- package/src/presentation/hooks/useAuth.ts +0 -0
- package/src/presentation/hooks/useAuthActions.ts +8 -11
- package/src/presentation/hooks/useAuthState.ts +10 -0
- package/src/presentation/hooks/useLoginForm.ts +0 -0
- package/src/presentation/hooks/useRegisterForm.ts +16 -17
- package/src/presentation/navigation/AuthNavigator.tsx +2 -2
- package/src/presentation/screens/LoginScreen.tsx +3 -6
- package/src/presentation/screens/RegisterScreen.tsx +3 -6
- package/src/presentation/utils/getAuthErrorMessage.ts +0 -0
- package/src/types/external.d.ts +68 -0
- 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,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>;
|