@umituz/react-native-auth 2.5.0 → 2.5.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/package.json +1 -1
- package/src/infrastructure/adapters/StorageProviderAdapter.ts +11 -33
- package/src/infrastructure/services/AuthEventService.ts +3 -5
- package/src/infrastructure/storage/GuestModeStorage.ts +8 -36
- package/src/infrastructure/utils/AuthEventEmitter.ts +0 -15
- package/src/presentation/components/AuthBottomSheet.tsx +23 -21
- package/src/presentation/components/LoginForm.tsx +1 -24
- package/src/presentation/hooks/useAuth.ts +0 -2
- package/src/presentation/hooks/useAuthActions.ts +52 -127
- package/src/presentation/hooks/useAuthState.ts +0 -2
- package/src/presentation/hooks/useLoginForm.ts +22 -69
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.2",
|
|
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",
|
|
@@ -15,11 +15,9 @@ export class StorageProviderAdapter implements IStorageProvider {
|
|
|
15
15
|
async get(key: string): Promise<string | null> {
|
|
16
16
|
try {
|
|
17
17
|
if (this.storage.getString) {
|
|
18
|
-
// @umituz/react-native-storage format
|
|
19
18
|
const result = await this.storage.getString(key, null);
|
|
20
19
|
return result?.value ?? null;
|
|
21
20
|
} else if (this.storage.getItem) {
|
|
22
|
-
// AsyncStorage format
|
|
23
21
|
return await this.storage.getItem(key);
|
|
24
22
|
} else {
|
|
25
23
|
throw new Error("Unsupported storage implementation");
|
|
@@ -30,44 +28,24 @@ export class StorageProviderAdapter implements IStorageProvider {
|
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
async set(key: string, value: string): Promise<void> {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
await this.storage.setItem(key, value);
|
|
40
|
-
} else {
|
|
41
|
-
throw new Error("Unsupported storage implementation");
|
|
42
|
-
}
|
|
43
|
-
} catch (error) {
|
|
44
|
-
if (__DEV__) {
|
|
45
|
-
console.warn("[StorageProviderAdapter] Failed to set value:", error);
|
|
46
|
-
}
|
|
47
|
-
throw error;
|
|
31
|
+
if (this.storage.setString) {
|
|
32
|
+
await this.storage.setString(key, value);
|
|
33
|
+
} else if (this.storage.setItem) {
|
|
34
|
+
await this.storage.setItem(key, value);
|
|
35
|
+
} else {
|
|
36
|
+
throw new Error("Unsupported storage implementation");
|
|
48
37
|
}
|
|
49
38
|
}
|
|
50
39
|
|
|
51
40
|
async remove(key: string): Promise<void> {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} else {
|
|
57
|
-
throw new Error("Unsupported storage implementation");
|
|
58
|
-
}
|
|
59
|
-
} catch (error) {
|
|
60
|
-
if (__DEV__) {
|
|
61
|
-
console.warn("[StorageProviderAdapter] Failed to remove value:", error);
|
|
62
|
-
}
|
|
63
|
-
throw error;
|
|
41
|
+
if (this.storage.removeItem) {
|
|
42
|
+
await this.storage.removeItem(key);
|
|
43
|
+
} else {
|
|
44
|
+
throw new Error("Unsupported storage implementation");
|
|
64
45
|
}
|
|
65
46
|
}
|
|
66
47
|
}
|
|
67
48
|
|
|
68
|
-
/**
|
|
69
|
-
* Create storage provider from various storage implementations
|
|
70
|
-
*/
|
|
71
49
|
export function createStorageProvider(storage: any): IStorageProvider {
|
|
72
50
|
return new StorageProviderAdapter(storage);
|
|
73
|
-
}
|
|
51
|
+
}
|
|
@@ -85,13 +85,11 @@ export class AuthEventService {
|
|
|
85
85
|
private notifyListeners(event: string, payload: AuthEventPayload): void {
|
|
86
86
|
const eventListeners = this.listeners.get(event);
|
|
87
87
|
if (eventListeners) {
|
|
88
|
-
eventListeners.forEach(listener => {
|
|
88
|
+
eventListeners.forEach((listener) => {
|
|
89
89
|
try {
|
|
90
90
|
listener(payload);
|
|
91
|
-
} catch
|
|
92
|
-
|
|
93
|
-
console.error(`Error in auth event listener for ${event}:`, error);
|
|
94
|
-
}
|
|
91
|
+
} catch {
|
|
92
|
+
// Silent fail for listener errors
|
|
95
93
|
}
|
|
96
94
|
});
|
|
97
95
|
}
|
|
@@ -3,24 +3,15 @@
|
|
|
3
3
|
* Single Responsibility: Manage guest mode persistence
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { IStorageProvider } from "../services/GuestModeService";
|
|
7
6
|
import { getAuthPackage } from "../services/AuthPackage";
|
|
8
7
|
|
|
9
|
-
/**
|
|
10
|
-
* Load guest mode from storage
|
|
11
|
-
*/
|
|
12
8
|
export async function loadGuestMode(storageKey?: string): Promise<boolean> {
|
|
13
9
|
try {
|
|
14
10
|
const packageConfig = getAuthPackage()?.getConfig();
|
|
15
11
|
const key = storageKey ?? packageConfig?.storageKeys.guestMode ?? "@auth_guest_mode";
|
|
16
|
-
|
|
12
|
+
|
|
17
13
|
const storageProvider = getAuthPackage()?.getStorageProvider();
|
|
18
|
-
if (!storageProvider)
|
|
19
|
-
if (__DEV__) {
|
|
20
|
-
console.warn("[GuestModeStorage] No storage provider available");
|
|
21
|
-
}
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
14
|
+
if (!storageProvider) return false;
|
|
24
15
|
|
|
25
16
|
const value = await storageProvider.get(key);
|
|
26
17
|
return value === "true";
|
|
@@ -29,53 +20,34 @@ export async function loadGuestMode(storageKey?: string): Promise<boolean> {
|
|
|
29
20
|
}
|
|
30
21
|
}
|
|
31
22
|
|
|
32
|
-
/**
|
|
33
|
-
* Save guest mode to storage
|
|
34
|
-
*/
|
|
35
23
|
export async function saveGuestMode(isGuest: boolean, storageKey?: string): Promise<void> {
|
|
36
24
|
try {
|
|
37
25
|
const packageConfig = getAuthPackage()?.getConfig();
|
|
38
26
|
const key = storageKey ?? packageConfig?.storageKeys.guestMode ?? "@auth_guest_mode";
|
|
39
|
-
|
|
27
|
+
|
|
40
28
|
const storageProvider = getAuthPackage()?.getStorageProvider();
|
|
41
|
-
if (!storageProvider)
|
|
42
|
-
if (__DEV__) {
|
|
43
|
-
console.warn("[GuestModeStorage] No storage provider available");
|
|
44
|
-
}
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
29
|
+
if (!storageProvider) return;
|
|
47
30
|
|
|
48
31
|
if (isGuest) {
|
|
49
32
|
await storageProvider.set(key, "true");
|
|
50
|
-
if (__DEV__) {
|
|
51
|
-
console.log("[GuestModeStorage] Guest mode persisted to storage");
|
|
52
|
-
}
|
|
53
33
|
} else {
|
|
54
34
|
await storageProvider.remove(key);
|
|
55
35
|
}
|
|
56
|
-
} catch
|
|
57
|
-
|
|
58
|
-
console.warn("[GuestModeStorage] Failed to persist guest mode:", error);
|
|
59
|
-
}
|
|
36
|
+
} catch {
|
|
37
|
+
// Ignore storage errors
|
|
60
38
|
}
|
|
61
39
|
}
|
|
62
40
|
|
|
63
|
-
/**
|
|
64
|
-
* Clear guest mode from storage
|
|
65
|
-
*/
|
|
66
41
|
export async function clearGuestMode(storageKey?: string): Promise<void> {
|
|
67
42
|
try {
|
|
68
43
|
const packageConfig = getAuthPackage()?.getConfig();
|
|
69
44
|
const key = storageKey ?? packageConfig?.storageKeys.guestMode ?? "@auth_guest_mode";
|
|
70
|
-
|
|
45
|
+
|
|
71
46
|
const storageProvider = getAuthPackage()?.getStorageProvider();
|
|
72
|
-
if (!storageProvider)
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
47
|
+
if (!storageProvider) return;
|
|
75
48
|
|
|
76
49
|
await storageProvider.remove(key);
|
|
77
50
|
} catch {
|
|
78
51
|
// Ignore storage errors
|
|
79
52
|
}
|
|
80
53
|
}
|
|
81
|
-
|
|
@@ -5,25 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
import { DeviceEventEmitter } from "react-native";
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
* Emit user authenticated event
|
|
10
|
-
*/
|
|
11
8
|
export function emitUserAuthenticated(userId: string): void {
|
|
12
|
-
/* eslint-disable-next-line no-console */
|
|
13
|
-
if (__DEV__) {
|
|
14
|
-
console.log("[AuthEventEmitter] Emitting user-authenticated event");
|
|
15
|
-
}
|
|
16
9
|
DeviceEventEmitter.emit("user-authenticated", { userId });
|
|
17
10
|
}
|
|
18
11
|
|
|
19
|
-
/**
|
|
20
|
-
* Emit guest mode enabled event
|
|
21
|
-
*/
|
|
22
12
|
export function emitGuestModeEnabled(): void {
|
|
23
|
-
/* eslint-disable-next-line no-console */
|
|
24
|
-
if (__DEV__) {
|
|
25
|
-
console.log("[AuthEventEmitter] Emitting guest-mode-enabled event");
|
|
26
|
-
}
|
|
27
13
|
DeviceEventEmitter.emit("guest-mode-enabled");
|
|
28
14
|
}
|
|
29
|
-
|
|
@@ -1,43 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AuthBottomSheet Component
|
|
3
3
|
* Bottom sheet modal for authentication (Login/Register)
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* ```tsx
|
|
7
|
-
* import { AuthBottomSheet } from '@umituz/react-native-auth';
|
|
8
|
-
*
|
|
9
|
-
* // In your app root (inside BottomSheetModalProvider)
|
|
10
|
-
* <AuthBottomSheet
|
|
11
|
-
* termsUrl="https://example.com/terms"
|
|
12
|
-
* privacyUrl="https://example.com/privacy"
|
|
13
|
-
* />
|
|
14
|
-
* ```
|
|
15
4
|
*/
|
|
16
5
|
|
|
17
6
|
import React, { useEffect, useCallback, useRef } from "react";
|
|
18
|
-
import { View, Text, StyleSheet,
|
|
7
|
+
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
|
|
19
8
|
import {
|
|
20
9
|
BottomSheetModal,
|
|
21
|
-
BottomSheetView,
|
|
22
10
|
BottomSheetBackdrop,
|
|
23
11
|
BottomSheetScrollView,
|
|
24
12
|
type BottomSheetBackdropProps,
|
|
25
13
|
} from "@gorhom/bottom-sheet";
|
|
26
14
|
import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
|
|
27
15
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
16
|
+
import { X } from "lucide-react-native";
|
|
28
17
|
import { useAuthModalStore } from "../stores/authModalStore";
|
|
29
18
|
import { useAuth } from "../hooks/useAuth";
|
|
30
19
|
import { LoginForm } from "./LoginForm";
|
|
31
20
|
import { RegisterForm } from "./RegisterForm";
|
|
32
21
|
|
|
33
22
|
export interface AuthBottomSheetProps {
|
|
34
|
-
/** Terms of Service URL */
|
|
35
23
|
termsUrl?: string;
|
|
36
|
-
/** Privacy Policy URL */
|
|
37
24
|
privacyUrl?: string;
|
|
38
|
-
/** Callback when Terms of Service is pressed */
|
|
39
25
|
onTermsPress?: () => void;
|
|
40
|
-
/** Callback when Privacy Policy is pressed */
|
|
41
26
|
onPrivacyPress?: () => void;
|
|
42
27
|
}
|
|
43
28
|
|
|
@@ -47,7 +32,6 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
47
32
|
onTermsPress,
|
|
48
33
|
onPrivacyPress,
|
|
49
34
|
}) => {
|
|
50
|
-
if (__DEV__) console.log("[AuthBottomSheet] Component rendering");
|
|
51
35
|
const tokens = useAppDesignTokens();
|
|
52
36
|
const { t } = useLocalization();
|
|
53
37
|
const modalRef = useRef<BottomSheetModal>(null);
|
|
@@ -55,9 +39,7 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
55
39
|
const { isVisible, mode, hideAuthModal, setMode, executePendingCallback, clearPendingCallback } =
|
|
56
40
|
useAuthModalStore();
|
|
57
41
|
const { isAuthenticated, isGuest } = useAuth();
|
|
58
|
-
if (__DEV__) console.log("[AuthBottomSheet] isVisible:", isVisible, "isAuthenticated:", isAuthenticated);
|
|
59
42
|
|
|
60
|
-
// Present/dismiss modal based on visibility state
|
|
61
43
|
useEffect(() => {
|
|
62
44
|
if (isVisible) {
|
|
63
45
|
modalRef.current?.present();
|
|
@@ -66,7 +48,6 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
66
48
|
}
|
|
67
49
|
}, [isVisible]);
|
|
68
50
|
|
|
69
|
-
// Handle successful authentication
|
|
70
51
|
useEffect(() => {
|
|
71
52
|
if (isAuthenticated && !isGuest && isVisible) {
|
|
72
53
|
hideAuthModal();
|
|
@@ -79,6 +60,11 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
79
60
|
clearPendingCallback();
|
|
80
61
|
}, [hideAuthModal, clearPendingCallback]);
|
|
81
62
|
|
|
63
|
+
const handleClose = useCallback(() => {
|
|
64
|
+
modalRef.current?.dismiss();
|
|
65
|
+
handleDismiss();
|
|
66
|
+
}, [handleDismiss]);
|
|
67
|
+
|
|
82
68
|
const handleNavigateToRegister = useCallback(() => {
|
|
83
69
|
setMode("register");
|
|
84
70
|
}, [setMode]);
|
|
@@ -114,6 +100,14 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
114
100
|
showsVerticalScrollIndicator={false}
|
|
115
101
|
keyboardShouldPersistTaps="handled"
|
|
116
102
|
>
|
|
103
|
+
<TouchableOpacity
|
|
104
|
+
style={styles.closeButton}
|
|
105
|
+
onPress={handleClose}
|
|
106
|
+
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
107
|
+
>
|
|
108
|
+
<X size={24} color={tokens.colors.textSecondary} />
|
|
109
|
+
</TouchableOpacity>
|
|
110
|
+
|
|
117
111
|
<View style={styles.header}>
|
|
118
112
|
<Text style={[styles.title, { color: tokens.colors.textPrimary }]}>
|
|
119
113
|
{mode === "login" ? t("auth.signIn") : t("auth.createAccount")}
|
|
@@ -155,10 +149,18 @@ const styles = StyleSheet.create({
|
|
|
155
149
|
paddingHorizontal: 24,
|
|
156
150
|
paddingBottom: 40,
|
|
157
151
|
},
|
|
152
|
+
closeButton: {
|
|
153
|
+
position: "absolute",
|
|
154
|
+
top: 0,
|
|
155
|
+
right: 0,
|
|
156
|
+
padding: 8,
|
|
157
|
+
zIndex: 10,
|
|
158
|
+
},
|
|
158
159
|
header: {
|
|
159
160
|
alignItems: "center",
|
|
160
161
|
marginBottom: 24,
|
|
161
162
|
marginTop: 8,
|
|
163
|
+
paddingTop: 16,
|
|
162
164
|
},
|
|
163
165
|
title: {
|
|
164
166
|
fontSize: 28,
|
|
@@ -9,16 +9,13 @@ import { AtomicInput, AtomicButton } from "@umituz/react-native-design-system-at
|
|
|
9
9
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
10
10
|
import { useLoginForm } from "../hooks/useLoginForm";
|
|
11
11
|
import { AuthErrorDisplay } from "./AuthErrorDisplay";
|
|
12
|
-
import { AuthDivider } from "./AuthDivider";
|
|
13
12
|
import { AuthLink } from "./AuthLink";
|
|
14
13
|
|
|
15
14
|
interface LoginFormProps {
|
|
16
15
|
onNavigateToRegister: () => void;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
export const LoginForm: React.FC<LoginFormProps> = ({
|
|
20
|
-
onNavigateToRegister,
|
|
21
|
-
}) => {
|
|
18
|
+
export const LoginForm: React.FC<LoginFormProps> = ({ onNavigateToRegister }) => {
|
|
22
19
|
const { t } = useLocalization();
|
|
23
20
|
const {
|
|
24
21
|
email,
|
|
@@ -29,7 +26,6 @@ export const LoginForm: React.FC<LoginFormProps> = ({
|
|
|
29
26
|
handleEmailChange,
|
|
30
27
|
handlePasswordChange,
|
|
31
28
|
handleSignIn,
|
|
32
|
-
handleContinueAsGuest,
|
|
33
29
|
displayError,
|
|
34
30
|
} = useLoginForm();
|
|
35
31
|
|
|
@@ -77,21 +73,6 @@ export const LoginForm: React.FC<LoginFormProps> = ({
|
|
|
77
73
|
</AtomicButton>
|
|
78
74
|
</View>
|
|
79
75
|
|
|
80
|
-
<AuthDivider />
|
|
81
|
-
|
|
82
|
-
<View style={styles.buttonContainer}>
|
|
83
|
-
<AtomicButton
|
|
84
|
-
variant="outline"
|
|
85
|
-
onPress={handleContinueAsGuest}
|
|
86
|
-
disabled={loading}
|
|
87
|
-
fullWidth
|
|
88
|
-
style={styles.guestButton}
|
|
89
|
-
testID="continue-as-guest-button"
|
|
90
|
-
>
|
|
91
|
-
{t("auth.continueAsGuest")}
|
|
92
|
-
</AtomicButton>
|
|
93
|
-
</View>
|
|
94
|
-
|
|
95
76
|
<AuthLink
|
|
96
77
|
text={t("auth.dontHaveAccount")}
|
|
97
78
|
linkText={t("auth.createAccount")}
|
|
@@ -112,8 +93,4 @@ const styles = StyleSheet.create({
|
|
|
112
93
|
signInButton: {
|
|
113
94
|
minHeight: 52,
|
|
114
95
|
},
|
|
115
|
-
guestButton: {
|
|
116
|
-
minHeight: 52,
|
|
117
|
-
},
|
|
118
96
|
});
|
|
119
|
-
|
|
@@ -47,10 +47,8 @@ export interface UseAuthResult {
|
|
|
47
47
|
* ```
|
|
48
48
|
*/
|
|
49
49
|
export function useAuth(): UseAuthResult {
|
|
50
|
-
if (__DEV__) console.log("[useAuth] Hook called");
|
|
51
50
|
const state = useAuthState();
|
|
52
51
|
const actions = useAuthActions(state);
|
|
53
|
-
if (__DEV__) console.log("[useAuth] isAuthenticated:", state.isAuthenticated, "isGuest:", state.isGuest);
|
|
54
52
|
|
|
55
53
|
return {
|
|
56
54
|
user: state.user,
|
|
@@ -14,85 +14,65 @@ export interface UseAuthActionsResult {
|
|
|
14
14
|
continueAsGuest: () => Promise<void>;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
* Hook for authentication actions
|
|
19
|
-
*/
|
|
20
|
-
export function useAuthActions(
|
|
21
|
-
state: UseAuthStateResult
|
|
22
|
-
): UseAuthActionsResult {
|
|
17
|
+
export function useAuthActions(state: UseAuthStateResult): UseAuthActionsResult {
|
|
23
18
|
const { isGuest, setIsGuest, setLoading, setError } = state;
|
|
24
19
|
|
|
25
|
-
const signUp = useCallback(
|
|
26
|
-
email: string,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const err = "Auth service is not initialized";
|
|
33
|
-
setError(err);
|
|
34
|
-
throw new Error(err);
|
|
35
|
-
}
|
|
36
|
-
try {
|
|
37
|
-
setLoading(true);
|
|
38
|
-
setError(null);
|
|
39
|
-
await service.signUp({ email, password, displayName });
|
|
40
|
-
if (isGuest) {
|
|
41
|
-
setIsGuest(false);
|
|
20
|
+
const signUp = useCallback(
|
|
21
|
+
async (email: string, password: string, displayName?: string) => {
|
|
22
|
+
const service = getAuthService();
|
|
23
|
+
if (!service) {
|
|
24
|
+
const err = "Auth service is not initialized";
|
|
25
|
+
setError(err);
|
|
26
|
+
throw new Error(err);
|
|
42
27
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
const service = getAuthService();
|
|
58
|
-
if (!service) {
|
|
59
|
-
const err = "Auth service is not initialized";
|
|
60
|
-
setError(err);
|
|
61
|
-
throw new Error(err);
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
setLoading(true);
|
|
65
|
-
setError(null);
|
|
66
|
-
/* eslint-disable-next-line no-console */
|
|
67
|
-
if (__DEV__) {
|
|
68
|
-
console.log("[useAuthActions] Calling service.signIn()");
|
|
28
|
+
try {
|
|
29
|
+
setLoading(true);
|
|
30
|
+
setError(null);
|
|
31
|
+
await service.signUp({ email, password, displayName });
|
|
32
|
+
if (isGuest) {
|
|
33
|
+
setIsGuest(false);
|
|
34
|
+
}
|
|
35
|
+
} catch (err: unknown) {
|
|
36
|
+
const errorMessage = err instanceof Error ? err.message : "Sign up failed";
|
|
37
|
+
setError(errorMessage);
|
|
38
|
+
throw err;
|
|
39
|
+
} finally {
|
|
40
|
+
setLoading(false);
|
|
69
41
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
42
|
+
},
|
|
43
|
+
[isGuest, setIsGuest, setLoading, setError],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const signIn = useCallback(
|
|
47
|
+
async (email: string, password: string) => {
|
|
48
|
+
const service = getAuthService();
|
|
49
|
+
if (!service) {
|
|
50
|
+
const err = "Auth service is not initialized";
|
|
51
|
+
setError(err);
|
|
52
|
+
throw new Error(err);
|
|
74
53
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
54
|
+
try {
|
|
55
|
+
setLoading(true);
|
|
56
|
+
setError(null);
|
|
57
|
+
await service.signIn({ email, password });
|
|
58
|
+
if (isGuest) {
|
|
59
|
+
setIsGuest(false);
|
|
79
60
|
}
|
|
80
|
-
|
|
61
|
+
} catch (err: unknown) {
|
|
62
|
+
const errorMessage = err instanceof Error ? err.message : "Sign in failed";
|
|
63
|
+
setError(errorMessage);
|
|
64
|
+
throw err;
|
|
65
|
+
} finally {
|
|
66
|
+
setLoading(false);
|
|
81
67
|
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
throw err;
|
|
86
|
-
} finally {
|
|
87
|
-
setLoading(false);
|
|
88
|
-
}
|
|
89
|
-
}, [isGuest, setIsGuest, setLoading, setError]);
|
|
68
|
+
},
|
|
69
|
+
[isGuest, setIsGuest, setLoading, setError],
|
|
70
|
+
);
|
|
90
71
|
|
|
91
72
|
const signOut = useCallback(async () => {
|
|
92
73
|
const service = getAuthService();
|
|
93
|
-
if (!service)
|
|
94
|
-
|
|
95
|
-
}
|
|
74
|
+
if (!service) return;
|
|
75
|
+
|
|
96
76
|
try {
|
|
97
77
|
setLoading(true);
|
|
98
78
|
await service.signOut();
|
|
@@ -102,76 +82,22 @@ export function useAuthActions(
|
|
|
102
82
|
}, [setLoading]);
|
|
103
83
|
|
|
104
84
|
const continueAsGuest = useCallback(async () => {
|
|
105
|
-
/* eslint-disable-next-line no-console */
|
|
106
|
-
if (__DEV__) {
|
|
107
|
-
console.log("========================================");
|
|
108
|
-
console.log("[useAuthActions] 🎯 continueAsGuest() CALLED");
|
|
109
|
-
console.log("[useAuthActions] Current state:", {
|
|
110
|
-
isGuest,
|
|
111
|
-
loading: state.loading,
|
|
112
|
-
});
|
|
113
|
-
console.log("========================================");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
85
|
const service = getAuthService();
|
|
117
86
|
if (!service) {
|
|
118
|
-
/* eslint-disable-next-line no-console */
|
|
119
|
-
if (__DEV__) {
|
|
120
|
-
console.log("[useAuthActions] ⚠️ No service available, setting isGuest directly");
|
|
121
|
-
}
|
|
122
87
|
setIsGuest(true);
|
|
123
88
|
return;
|
|
124
89
|
}
|
|
125
|
-
|
|
90
|
+
|
|
126
91
|
try {
|
|
127
|
-
/* eslint-disable-next-line no-console */
|
|
128
|
-
if (__DEV__) {
|
|
129
|
-
console.log("[useAuthActions] Setting loading to true...");
|
|
130
|
-
}
|
|
131
92
|
setLoading(true);
|
|
132
|
-
|
|
133
|
-
/* eslint-disable-next-line no-console */
|
|
134
|
-
if (__DEV__) {
|
|
135
|
-
console.log("[useAuthActions] 📞 Calling service.setGuestMode()...");
|
|
136
|
-
}
|
|
137
93
|
await service.setGuestMode();
|
|
138
|
-
|
|
139
|
-
/* eslint-disable-next-line no-console */
|
|
140
|
-
if (__DEV__) {
|
|
141
|
-
console.log("[useAuthActions] ✅ Service.setGuestMode() completed successfully");
|
|
142
|
-
console.log("[useAuthActions] Setting isGuest to true...");
|
|
143
|
-
}
|
|
144
|
-
|
|
145
94
|
setIsGuest(true);
|
|
146
|
-
|
|
147
|
-
/* eslint-disable-next-line no-console */
|
|
148
|
-
if (__DEV__) {
|
|
149
|
-
console.log("[useAuthActions] ✅ isGuest set to true");
|
|
150
|
-
}
|
|
151
|
-
} catch (error: unknown) {
|
|
152
|
-
const errorMessage = error instanceof Error ? error.message : "Failed to continue as guest";
|
|
153
|
-
setError(errorMessage);
|
|
154
|
-
if (__DEV__) {
|
|
155
|
-
console.error("[useAuthActions] ❌ ERROR in continueAsGuest:", error);
|
|
156
|
-
console.error("[useAuthActions] Error details:", {
|
|
157
|
-
message: errorMessage,
|
|
158
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
159
|
-
});
|
|
160
|
-
}
|
|
95
|
+
} catch {
|
|
161
96
|
setIsGuest(true);
|
|
162
97
|
} finally {
|
|
163
|
-
/* eslint-disable-next-line no-console */
|
|
164
|
-
if (__DEV__) {
|
|
165
|
-
console.log("[useAuthActions] Setting loading to false...");
|
|
166
|
-
}
|
|
167
98
|
setLoading(false);
|
|
168
|
-
/* eslint-disable-next-line no-console */
|
|
169
|
-
if (__DEV__) {
|
|
170
|
-
console.log("[useAuthActions] ✅ continueAsGuest() FINISHED");
|
|
171
|
-
console.log("========================================");
|
|
172
|
-
}
|
|
173
99
|
}
|
|
174
|
-
}, [
|
|
100
|
+
}, [setIsGuest, setLoading]);
|
|
175
101
|
|
|
176
102
|
return {
|
|
177
103
|
signUp,
|
|
@@ -180,4 +106,3 @@ export function useAuthActions(
|
|
|
180
106
|
continueAsGuest,
|
|
181
107
|
};
|
|
182
108
|
}
|
|
183
|
-
|
|
@@ -25,9 +25,7 @@ export interface UseAuthStateResult {
|
|
|
25
25
|
* Hook for managing authentication state
|
|
26
26
|
*/
|
|
27
27
|
export function useAuthState(): UseAuthStateResult {
|
|
28
|
-
if (__DEV__) console.log("[useAuthState] Hook called");
|
|
29
28
|
const { user: firebaseUser, loading: firebaseLoading } = useFirebaseAuth();
|
|
30
|
-
if (__DEV__) console.log("[useAuthState] firebaseUser:", firebaseUser?.uid, "firebaseLoading:", firebaseLoading);
|
|
31
29
|
const [isGuest, setIsGuest] = useState(() => {
|
|
32
30
|
const service = getAuthService();
|
|
33
31
|
return service ? service.getIsGuestMode() : false;
|
|
@@ -23,9 +23,6 @@ export interface UseLoginFormResult {
|
|
|
23
23
|
displayError: string | null;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
* Hook for login form logic
|
|
28
|
-
*/
|
|
29
26
|
export function useLoginForm(): UseLoginFormResult {
|
|
30
27
|
const { t } = useLocalization();
|
|
31
28
|
const { signIn, loading, error, continueAsGuest } = useAuth();
|
|
@@ -36,23 +33,25 @@ export function useLoginForm(): UseLoginFormResult {
|
|
|
36
33
|
const [passwordError, setPasswordError] = useState<string | null>(null);
|
|
37
34
|
const [localError, setLocalError] = useState<string | null>(null);
|
|
38
35
|
|
|
39
|
-
const handleEmailChange = useCallback(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
const handleEmailChange = useCallback(
|
|
37
|
+
(text: string) => {
|
|
38
|
+
setEmail(text);
|
|
39
|
+
if (emailError) setEmailError(null);
|
|
40
|
+
if (localError) setLocalError(null);
|
|
41
|
+
},
|
|
42
|
+
[emailError, localError],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const handlePasswordChange = useCallback(
|
|
46
|
+
(text: string) => {
|
|
47
|
+
setPassword(text);
|
|
48
|
+
if (passwordError) setPasswordError(null);
|
|
49
|
+
if (localError) setLocalError(null);
|
|
50
|
+
},
|
|
51
|
+
[passwordError, localError],
|
|
52
|
+
);
|
|
50
53
|
|
|
51
54
|
const handleSignIn = useCallback(async () => {
|
|
52
|
-
/* eslint-disable-next-line no-console */
|
|
53
|
-
if (__DEV__) {
|
|
54
|
-
console.log("[useLoginForm] handleSignIn called");
|
|
55
|
-
}
|
|
56
55
|
setEmailError(null);
|
|
57
56
|
setPasswordError(null);
|
|
58
57
|
setLocalError(null);
|
|
@@ -73,29 +72,11 @@ export function useLoginForm(): UseLoginFormResult {
|
|
|
73
72
|
hasError = true;
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
if (hasError)
|
|
77
|
-
/* eslint-disable-next-line no-console */
|
|
78
|
-
if (__DEV__) {
|
|
79
|
-
console.log("[useLoginForm] Validation errors, returning early");
|
|
80
|
-
}
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
75
|
+
if (hasError) return;
|
|
83
76
|
|
|
84
77
|
try {
|
|
85
|
-
/* eslint-disable-next-line no-console */
|
|
86
|
-
if (__DEV__) {
|
|
87
|
-
console.log("[useLoginForm] Calling signIn with email:", email.trim());
|
|
88
|
-
}
|
|
89
78
|
await signIn(email.trim(), password);
|
|
90
|
-
|
|
91
|
-
if (__DEV__) {
|
|
92
|
-
console.log("[useLoginForm] signIn completed successfully");
|
|
93
|
-
}
|
|
94
|
-
} catch (err: any) {
|
|
95
|
-
/* eslint-disable-next-line no-console */
|
|
96
|
-
if (__DEV__) {
|
|
97
|
-
console.error("[useLoginForm] signIn error:", err);
|
|
98
|
-
}
|
|
79
|
+
} catch (err: unknown) {
|
|
99
80
|
const localizationKey = getAuthErrorLocalizationKey(err);
|
|
100
81
|
const errorMessage = t(localizationKey);
|
|
101
82
|
setLocalError(errorMessage);
|
|
@@ -103,39 +84,12 @@ export function useLoginForm(): UseLoginFormResult {
|
|
|
103
84
|
}, [email, password, t, signIn]);
|
|
104
85
|
|
|
105
86
|
const handleContinueAsGuest = useCallback(async () => {
|
|
106
|
-
/* eslint-disable-next-line no-console */
|
|
107
|
-
if (__DEV__) {
|
|
108
|
-
console.log("========================================");
|
|
109
|
-
console.log("[useLoginForm] 🎯 Continue as Guest button PRESSED");
|
|
110
|
-
console.log("[useLoginForm] Current loading state:", loading);
|
|
111
|
-
console.log("[useLoginForm] Current error state:", error);
|
|
112
|
-
console.log("========================================");
|
|
113
|
-
}
|
|
114
|
-
|
|
115
87
|
try {
|
|
116
|
-
/* eslint-disable-next-line no-console */
|
|
117
|
-
if (__DEV__) {
|
|
118
|
-
console.log("[useLoginForm] Calling continueAsGuest() function...");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
88
|
await continueAsGuest();
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (__DEV__) {
|
|
125
|
-
console.log("[useLoginForm] ✅ continueAsGuest() completed successfully");
|
|
126
|
-
console.log("[useLoginForm] Current loading state after:", loading);
|
|
127
|
-
}
|
|
128
|
-
} catch (err) {
|
|
129
|
-
/* eslint-disable-next-line no-console */
|
|
130
|
-
if (__DEV__) {
|
|
131
|
-
console.error("[useLoginForm] ❌ ERROR in continueAsGuest:", err);
|
|
132
|
-
console.error("[useLoginForm] Error details:", {
|
|
133
|
-
message: err instanceof Error ? err.message : String(err),
|
|
134
|
-
stack: err instanceof Error ? err.stack : undefined,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
89
|
+
} catch {
|
|
90
|
+
// Silent fail - guest mode should always work
|
|
137
91
|
}
|
|
138
|
-
}, [continueAsGuest
|
|
92
|
+
}, [continueAsGuest]);
|
|
139
93
|
|
|
140
94
|
const displayError = localError || error;
|
|
141
95
|
|
|
@@ -153,4 +107,3 @@ export function useLoginForm(): UseLoginFormResult {
|
|
|
153
107
|
displayError,
|
|
154
108
|
};
|
|
155
109
|
}
|
|
156
|
-
|