@umituz/react-native-auth 3.6.72 → 3.6.74

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 (35) hide show
  1. package/package.json +1 -1
  2. package/src/domain/value-objects/AuthConfig.ts +9 -56
  3. package/src/index.ts +15 -111
  4. package/src/infrastructure/adapters/StorageProviderAdapter.ts +1 -4
  5. package/src/infrastructure/providers/FirebaseAuthProvider.ts +5 -32
  6. package/src/infrastructure/repositories/AuthRepository.ts +0 -5
  7. package/src/infrastructure/services/AnonymousModeService.ts +6 -20
  8. package/src/infrastructure/services/AuthEventService.ts +2 -4
  9. package/src/infrastructure/services/initializeAuth.ts +2 -11
  10. package/src/infrastructure/utils/AuthErrorMapper.ts +0 -4
  11. package/src/infrastructure/utils/authStateHandler.ts +2 -4
  12. package/src/infrastructure/utils/listener/anonymousSignInHandler.ts +2 -22
  13. package/src/infrastructure/utils/listener/listenerLifecycle.util.ts +144 -0
  14. package/src/infrastructure/utils/listener/listenerState.util.ts +125 -0
  15. package/src/init/createAuthInitModule.ts +3 -22
  16. package/src/presentation/components/AccountActions.tsx +11 -43
  17. package/src/presentation/components/AuthBottomSheet.tsx +1 -17
  18. package/src/presentation/components/ProfileSection.tsx +78 -108
  19. package/src/presentation/components/RegisterForm.tsx +6 -28
  20. package/src/presentation/hooks/useAccountManagement.ts +0 -7
  21. package/src/presentation/hooks/useAuth.ts +2 -4
  22. package/src/presentation/hooks/useAuthBottomSheet.ts +23 -79
  23. package/src/presentation/hooks/useGoogleAuth.ts +0 -3
  24. package/src/presentation/hooks/useLoginForm.ts +26 -30
  25. package/src/presentation/hooks/useProfileEdit.ts +56 -64
  26. package/src/presentation/hooks/useRegisterForm.ts +41 -44
  27. package/src/presentation/providers/AuthProvider.tsx +2 -7
  28. package/src/presentation/stores/authModalStore.ts +0 -7
  29. package/src/presentation/stores/authStore.ts +1 -28
  30. package/src/presentation/stores/initializeAuthListener.ts +30 -160
  31. package/src/presentation/utils/accountDeleteHandler.util.ts +0 -51
  32. package/src/presentation/utils/authTransition.util.ts +72 -0
  33. package/src/presentation/utils/form/formFieldState.util.ts +82 -0
  34. package/src/presentation/utils/form/formValidation.util.ts +173 -0
  35. package/src/presentation/utils/socialAuthHandler.util.ts +106 -0
@@ -119,10 +119,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
119
119
  style={styles.input}
120
120
  />
121
121
  {password.length > 0 && (
122
- <PasswordStrengthIndicator
123
- translations={translations.passwordStrength}
124
- requirements={passwordRequirements}
125
- />
122
+ <PasswordStrengthIndicator translations={translations.passwordStrength} requirements={passwordRequirements} />
126
123
  )}
127
124
 
128
125
  <AtomicInput
@@ -144,10 +141,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
144
141
  style={styles.input}
145
142
  />
146
143
  {confirmPassword.length > 0 && (
147
- <PasswordMatchIndicator
148
- translations={translations.passwordMatch}
149
- isMatch={passwordsMatch}
150
- />
144
+ <PasswordMatchIndicator translations={translations.passwordMatch} isMatch={passwordsMatch} />
151
145
  )}
152
146
 
153
147
  <AuthErrorDisplay error={displayError} />
@@ -155,24 +149,14 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
155
149
  <AtomicButton
156
150
  variant="primary"
157
151
  onPress={() => { void handleSignUp(); }}
158
- disabled={
159
- loading ||
160
- !email.trim() ||
161
- !password.trim() ||
162
- !confirmPassword.trim()
163
- }
152
+ disabled={loading || !email.trim() || !password.trim() || !confirmPassword.trim()}
164
153
  fullWidth
165
154
  style={styles.signUpButton}
166
155
  >
167
156
  {translations.signUp}
168
157
  </AtomicButton>
169
158
 
170
- <AuthLink
171
- text={translations.alreadyHaveAccount}
172
- linkText={translations.signIn}
173
- onPress={onNavigateToLogin}
174
- disabled={loading}
175
- />
159
+ <AuthLink text={translations.alreadyHaveAccount} linkText={translations.signIn} onPress={onNavigateToLogin} disabled={loading} />
176
160
 
177
161
  <AuthLegalLinks
178
162
  translations={translations.legal}
@@ -187,12 +171,6 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
187
171
  };
188
172
 
189
173
  const styles = StyleSheet.create({
190
- input: {
191
- marginBottom: 20,
192
- },
193
- signUpButton: {
194
- minHeight: 52,
195
- marginBottom: 16,
196
- marginTop: 8,
197
- },
174
+ input: { marginBottom: 20 },
175
+ signUpButton: { minHeight: 52, marginBottom: 16, marginTop: 8 },
198
176
  });
@@ -78,13 +78,6 @@ export const useAccountManagement = (
78
78
  throw new Error("Cannot delete anonymous account");
79
79
  }
80
80
 
81
- if (__DEV__) {
82
- console.log("[useAccountManagement] Starting delete account", {
83
- userId: user.uid,
84
- provider: user.provider,
85
- });
86
- }
87
-
88
81
  setIsDeletingAccount(true);
89
82
 
90
83
  try {
@@ -146,10 +146,8 @@ export function useAuth(): UseAuthResult {
146
146
  setLoading(true);
147
147
  await anonymousModeMutation.mutateAsync();
148
148
  setIsAnonymous(true);
149
- } catch (error) {
150
- if (__DEV__) {
151
- console.warn("[useAuth] continueAnonymously failed:", error);
152
- }
149
+ } catch {
150
+ // Silently fail - anonymous mode is optional
153
151
  setIsAnonymous(true);
154
152
  } finally {
155
153
  setLoading(false);
@@ -1,11 +1,15 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
- import { Platform } from "react-native";
3
2
  import type { BottomSheetModalRef } from "@umituz/react-native-design-system";
4
3
  import { useAuthModalStore } from "../stores/authModalStore";
5
4
  import { useAuth } from "../hooks/useAuth";
6
5
  import { useGoogleAuth, type GoogleAuthConfig } from "./useGoogleAuth";
7
6
  import { useAppleAuth } from "./useAppleAuth";
8
7
  import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
8
+ import {
9
+ useAuthTransitions,
10
+ executeAfterAuth,
11
+ } from "../utils/authTransition.util";
12
+ import { determineEnabledProviders } from "../utils/socialAuthHandler.util";
9
13
 
10
14
  export interface SocialAuthConfiguration {
11
15
  google?: GoogleAuthConfig;
@@ -22,7 +26,7 @@ interface UseAuthBottomSheetParams {
22
26
 
23
27
  export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
24
28
  const { socialConfig, onGoogleSignIn, onAppleSignIn, onAuthSuccess } = params;
25
-
29
+
26
30
  const modalRef = useRef<BottomSheetModalRef>(null);
27
31
  const [googleLoading, setGoogleLoading] = useState(false);
28
32
  const [appleLoading, setAppleLoading] = useState(false);
@@ -37,37 +41,14 @@ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
37
41
 
38
42
  // Determine enabled providers
39
43
  const providers = useMemo<SocialAuthProvider[]>(() => {
40
- const result: SocialAuthProvider[] = [];
41
-
42
- if (Platform.OS === "ios" && socialConfig?.apple?.enabled && appleAvailable) {
43
- result.push("apple");
44
- }
45
-
46
- if (googleConfigured) {
47
- result.push("google");
48
- }
49
-
50
- return result;
51
- }, [socialConfig?.apple?.enabled, appleAvailable, googleConfigured]);
44
+ return determineEnabledProviders(socialConfig, appleAvailable, googleConfigured);
45
+ }, [socialConfig, appleAvailable, googleConfigured]);
52
46
 
53
47
  // Handle visibility sync with modalRef
54
48
  useEffect(() => {
55
- if (__DEV__) {
56
- console.log('[useAuthBottomSheet] Visibility changed:', {
57
- isVisible,
58
- hasModalRef: !!modalRef.current,
59
- modalRefMethods: modalRef.current ? Object.keys(modalRef.current) : [],
60
- });
61
- }
62
49
  if (isVisible) {
63
- if (__DEV__) {
64
- console.log('[useAuthBottomSheet] Calling present()');
65
- }
66
50
  modalRef.current?.present();
67
51
  } else {
68
- if (__DEV__) {
69
- console.log('[useAuthBottomSheet] Calling dismiss()');
70
- }
71
52
  modalRef.current?.dismiss();
72
53
  }
73
54
  }, [isVisible]);
@@ -82,61 +63,24 @@ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
82
63
  handleDismiss();
83
64
  }, [handleDismiss]);
84
65
 
85
- const prevIsAuthenticatedRef = useRef(isAuthenticated);
86
- const prevIsVisibleRef = useRef(isVisible);
87
- const prevIsAnonymousRef = useRef(isAnonymous);
88
-
89
- useEffect(() => {
90
- const justAuthenticated = !prevIsAuthenticatedRef.current && isAuthenticated;
91
- const justConvertedFromAnonymous = prevIsAnonymousRef.current && !isAnonymous && isAuthenticated;
92
-
93
- if (__DEV__) {
94
- console.log("[useAuthBottomSheet] Auth state effect:", {
95
- isAuthenticated,
96
- isAnonymous,
97
- isVisible,
98
- prevIsAuthenticated: prevIsAuthenticatedRef.current,
99
- prevIsAnonymous: prevIsAnonymousRef.current,
100
- justAuthenticated,
101
- justConvertedFromAnonymous,
102
- willClose: (justAuthenticated || justConvertedFromAnonymous) && isVisible && !isAnonymous,
103
- });
104
- }
105
-
106
- let timeoutId: NodeJS.Timeout | undefined;
107
-
108
- if ((justAuthenticated || justConvertedFromAnonymous) && isVisible && !isAnonymous) {
109
- if (__DEV__) {
110
- console.log("[useAuthBottomSheet] Auto-closing due to successful authentication transition", {
111
- justAuthenticated,
112
- justConvertedFromAnonymous,
66
+ // Handle auth transitions
67
+ useAuthTransitions(
68
+ { isAuthenticated, isAnonymous, isVisible },
69
+ (result) => {
70
+ if (result.shouldClose) {
71
+ modalRef.current?.dismiss();
72
+ hideAuthModal();
73
+ onAuthSuccess?.();
74
+
75
+ const timeoutId = executeAfterAuth(() => {
76
+ executePendingCallback();
113
77
  });
114
- }
115
- // Close modal and hide first
116
- modalRef.current?.dismiss();
117
- hideAuthModal();
118
- // Notify auth success
119
- onAuthSuccess?.();
120
- // Execute callback with delay to ensure auth state has propagated
121
- timeoutId = setTimeout(() => {
122
- if (__DEV__) {
123
- console.log("[useAuthBottomSheet] Executing pending callback after auth");
124
- }
125
- executePendingCallback();
126
- }, 100);
127
- }
128
78
 
129
- prevIsAuthenticatedRef.current = isAuthenticated;
130
- prevIsVisibleRef.current = isVisible;
131
- prevIsAnonymousRef.current = isAnonymous;
132
-
133
- // Cleanup timeout on unmount or dependency change
134
- return () => {
135
- if (timeoutId) {
136
- clearTimeout(timeoutId);
79
+ // Cleanup timeout on unmount
80
+ return () => clearTimeout(timeoutId);
137
81
  }
138
- };
139
- }, [isAuthenticated, isVisible, isAnonymous, executePendingCallback, hideAuthModal, onAuthSuccess]);
82
+ }
83
+ );
140
84
 
141
85
  const handleNavigateToRegister = useCallback(() => {
142
86
  setMode("register");
@@ -87,9 +87,6 @@ export function useGoogleAuth(config?: GoogleAuthConfig): UseGoogleAuthResult {
87
87
  .catch((error) => {
88
88
  const errorMessage = error instanceof Error ? error.message : "Firebase sign-in failed";
89
89
  setGoogleError(errorMessage);
90
- if (__DEV__) {
91
- console.error("[useGoogleAuth] Firebase sign-in failed:", error);
92
- }
93
90
  })
94
91
  .finally(() => {
95
92
  setIsLoading(false);
@@ -1,7 +1,7 @@
1
1
  import { useState, useCallback } from "react";
2
2
  import { useAuth } from "./useAuth";
3
3
  import { getAuthErrorLocalizationKey, resolveErrorMessage } from "../utils/getAuthErrorMessage";
4
- import { validateEmail, validatePasswordForLogin } from "../../infrastructure/utils/AuthValidation";
4
+ import { validateLoginForm } from "../utils/form/formValidation.util";
5
5
  import { alertService } from "@umituz/react-native-design-system";
6
6
 
7
7
  export interface LoginFormTranslations {
@@ -42,45 +42,44 @@ export function useLoginForm(config?: UseLoginFormConfig): UseLoginFormResult {
42
42
  return resolveErrorMessage(key, translations?.errors);
43
43
  }, [translations]);
44
44
 
45
+ const clearErrors = useCallback(() => {
46
+ setEmailError(null);
47
+ setPasswordError(null);
48
+ setLocalError(null);
49
+ }, []);
50
+
45
51
  const handleEmailChange = useCallback(
46
52
  (text: string) => {
47
53
  setEmail(text);
48
- if (emailError) setEmailError(null);
49
- if (localError) setLocalError(null);
54
+ if (emailError || localError) clearErrors();
50
55
  },
51
- [emailError, localError],
56
+ [emailError, localError, clearErrors],
52
57
  );
53
58
 
54
59
  const handlePasswordChange = useCallback(
55
60
  (text: string) => {
56
61
  setPassword(text);
57
- if (passwordError) setPasswordError(null);
58
- if (localError) setLocalError(null);
62
+ if (passwordError || localError) clearErrors();
59
63
  },
60
- [passwordError, localError],
64
+ [passwordError, localError, clearErrors],
61
65
  );
62
66
 
63
67
  const handleSignIn = useCallback(async () => {
64
- setEmailError(null);
65
- setPasswordError(null);
66
- setLocalError(null);
67
-
68
- let hasError = false;
68
+ clearErrors();
69
69
 
70
- const emailResult = validateEmail(email.trim());
71
- if (!emailResult.isValid && emailResult.error) {
72
- setEmailError(getErrorMessage(emailResult.error));
73
- hasError = true;
74
- }
70
+ const validation = validateLoginForm(
71
+ { email: email.trim(), password },
72
+ getErrorMessage
73
+ );
75
74
 
76
- const passwordResult = validatePasswordForLogin(password);
77
- if (!passwordResult.isValid && passwordResult.error) {
78
- setPasswordError(getErrorMessage(passwordResult.error));
79
- hasError = true;
75
+ if (!validation.isValid) {
76
+ for (const error of validation.errors) {
77
+ if (error.field === "email") setEmailError(error.message);
78
+ if (error.field === "password") setPasswordError(error.message);
79
+ }
80
+ return;
80
81
  }
81
82
 
82
- if (hasError) return;
83
-
84
83
  try {
85
84
  await signIn(email.trim(), password);
86
85
 
@@ -92,18 +91,15 @@ export function useLoginForm(config?: UseLoginFormConfig): UseLoginFormResult {
92
91
  }
93
92
  } catch (err: unknown) {
94
93
  const localizationKey = getAuthErrorLocalizationKey(err);
95
- const errorMessage = getErrorMessage(localizationKey);
96
- setLocalError(errorMessage);
94
+ setLocalError(getErrorMessage(localizationKey));
97
95
  }
98
- }, [email, password, signIn, translations, getErrorMessage]);
96
+ }, [email, password, signIn, translations, getErrorMessage, clearErrors]);
99
97
 
100
98
  const handleContinueAnonymously = useCallback(async () => {
101
99
  try {
102
100
  await continueAnonymously();
103
- } catch (error) {
104
- if (__DEV__) {
105
- console.warn("[useLoginForm] Continue anonymously failed:", error);
106
- }
101
+ } catch {
102
+ // Silently fail - anonymous mode is optional
107
103
  }
108
104
  }, [continueAnonymously]);
109
105
 
@@ -1,88 +1,80 @@
1
1
  /**
2
- * useProfileEdit Hook
2
+ * Profile Edit Hook
3
3
  * Simple profile editing with form state management
4
4
  * Apps provide image picker and backend update logic
5
5
  */
6
6
 
7
7
  import { useState, useCallback } from "react";
8
- import { validateEmail } from "../../infrastructure/utils/AuthValidation";
8
+ import { validateProfileForm } from "../utils/form/formValidation.util";
9
9
 
10
10
  export interface ProfileEditFormState {
11
- displayName: string;
12
- email: string;
13
- photoURL: string | null;
14
- isModified: boolean;
11
+ displayName: string;
12
+ email: string;
13
+ photoURL: string | null;
14
+ isModified: boolean;
15
15
  }
16
16
 
17
17
  export interface UseProfileEditReturn {
18
- formState: ProfileEditFormState;
19
- setDisplayName: (value: string) => void;
20
- setEmail: (value: string) => void;
21
- setPhotoURL: (value: string | null) => void;
22
- resetForm: (initial: Partial<ProfileEditFormState>) => void;
23
- validateForm: () => { isValid: boolean; errors: string[] };
18
+ formState: ProfileEditFormState;
19
+ setDisplayName: (value: string) => void;
20
+ setEmail: (value: string) => void;
21
+ setPhotoURL: (value: string | null) => void;
22
+ resetForm: (initial: Partial<ProfileEditFormState>) => void;
23
+ validateForm: () => { isValid: boolean; errors: string[] };
24
24
  }
25
25
 
26
26
  export const useProfileEdit = (
27
- initialState: Partial<ProfileEditFormState> = {},
27
+ initialState: Partial<ProfileEditFormState> = {},
28
28
  ): UseProfileEditReturn => {
29
- const [formState, setFormState] = useState<ProfileEditFormState>({
30
- displayName: initialState.displayName || "",
31
- email: initialState.email || "",
32
- photoURL: initialState.photoURL || null,
33
- isModified: false,
34
- });
35
-
36
- const setDisplayName = useCallback((value: string) => {
37
- setFormState((prev) => ({ ...prev, displayName: value, isModified: true }));
38
- }, []);
39
-
40
- const setEmail = useCallback((value: string) => {
41
- setFormState((prev) => ({ ...prev, email: value, isModified: true }));
42
- }, []);
43
-
44
- const setPhotoURL = useCallback((value: string | null) => {
45
- setFormState((prev) => ({ ...prev, photoURL: value, isModified: true }));
46
- }, []);
29
+ const [formState, setFormState] = useState<ProfileEditFormState>({
30
+ displayName: initialState.displayName || "",
31
+ email: initialState.email || "",
32
+ photoURL: initialState.photoURL || null,
33
+ isModified: false,
34
+ });
47
35
 
48
- const resetForm = useCallback((initial: Partial<ProfileEditFormState>) => {
49
- setFormState({
50
- displayName: initial.displayName || "",
51
- email: initial.email || "",
52
- photoURL: initial.photoURL || null,
53
- isModified: false,
54
- });
55
- }, []);
36
+ const setDisplayName = useCallback((value: string) => {
37
+ setFormState((prev) => ({ ...prev, displayName: value, isModified: true }));
38
+ }, []);
56
39
 
57
- const validateForm = useCallback((): {
58
- isValid: boolean;
59
- errors: string[];
60
- } => {
61
- const errors: string[] = [];
40
+ const setEmail = useCallback((value: string) => {
41
+ setFormState((prev) => ({ ...prev, email: value, isModified: true }));
42
+ }, []);
62
43
 
63
- if (!formState.displayName.trim()) {
64
- errors.push("Display name is required");
65
- }
44
+ const setPhotoURL = useCallback((value: string | null) => {
45
+ setFormState((prev) => ({ ...prev, photoURL: value, isModified: true }));
46
+ }, []);
66
47
 
67
- if (formState.email) {
68
- const emailResult = validateEmail(formState.email);
69
- if (!emailResult.isValid && emailResult.error) {
70
- errors.push(emailResult.error);
71
- }
72
- }
48
+ const resetForm = useCallback((initial: Partial<ProfileEditFormState>) => {
49
+ setFormState({
50
+ displayName: initial.displayName || "",
51
+ email: initial.email || "",
52
+ photoURL: initial.photoURL || null,
53
+ isModified: false,
54
+ });
55
+ }, []);
73
56
 
74
- return {
75
- isValid: errors.length === 0,
76
- errors,
77
- };
78
- }, [formState]);
57
+ const validateForm = useCallback((): {
58
+ isValid: boolean;
59
+ errors: string[];
60
+ } => {
61
+ const result = validateProfileForm({
62
+ displayName: formState.displayName,
63
+ email: formState.email,
64
+ });
79
65
 
80
66
  return {
81
- formState,
82
- setDisplayName,
83
- setEmail,
84
- setPhotoURL,
85
- resetForm,
86
- validateForm,
67
+ isValid: result.isValid,
68
+ errors: result.errors.map((e) => e.message),
87
69
  };
70
+ }, [formState]);
71
+
72
+ return {
73
+ formState,
74
+ setDisplayName,
75
+ setEmail,
76
+ setPhotoURL,
77
+ resetForm,
78
+ validateForm,
79
+ };
88
80
  };
@@ -1,16 +1,22 @@
1
1
  import { useState, useCallback, useMemo } from "react";
2
2
  import {
3
- validateEmail,
4
3
  validatePasswordForRegister,
5
- validatePasswordConfirmation,
4
+ type PasswordRequirements,
6
5
  } from "../../infrastructure/utils/AuthValidation";
7
6
  import { DEFAULT_PASSWORD_CONFIG } from "../../domain/value-objects/AuthConfig";
8
7
  import { useAuth } from "./useAuth";
9
8
  import { getAuthErrorLocalizationKey, resolveErrorMessage } from "../utils/getAuthErrorMessage";
10
- import type { PasswordRequirements } from "../../infrastructure/utils/AuthValidation";
9
+ import { validateRegisterForm, errorsToFieldErrors } from "../utils/form/formValidation.util";
11
10
  import { alertService } from "@umituz/react-native-design-system";
12
11
  import { clearFieldErrors, clearFieldError } from "../utils/form/formErrorUtils";
13
12
 
13
+ export type FieldErrors = {
14
+ displayName?: string;
15
+ email?: string;
16
+ password?: string;
17
+ confirmPassword?: string;
18
+ };
19
+
14
20
  export interface RegisterFormTranslations {
15
21
  successTitle: string;
16
22
  signUpSuccess: string;
@@ -26,12 +32,7 @@ export interface UseRegisterFormResult {
26
32
  email: string;
27
33
  password: string;
28
34
  confirmPassword: string;
29
- fieldErrors: {
30
- displayName?: string;
31
- email?: string;
32
- password?: string;
33
- confirmPassword?: string;
34
- };
35
+ fieldErrors: FieldErrors;
35
36
  localError: string | null;
36
37
  loading: boolean;
37
38
  passwordRequirements: PasswordRequirements;
@@ -53,12 +54,7 @@ export function useRegisterForm(config?: UseRegisterFormConfig): UseRegisterForm
53
54
  const [password, setPassword] = useState("");
54
55
  const [confirmPassword, setConfirmPassword] = useState("");
55
56
  const [localError, setLocalError] = useState<string | null>(null);
56
- const [fieldErrors, setFieldErrors] = useState<{
57
- displayName?: string;
58
- email?: string;
59
- password?: string;
60
- confirmPassword?: string;
61
- }>({});
57
+ const [fieldErrors, setFieldErrors] = useState<FieldErrors>({});
62
58
 
63
59
  const getErrorMessage = useCallback((key: string) => {
64
60
  return resolveErrorMessage(key, translations?.errors);
@@ -76,49 +72,51 @@ export function useRegisterForm(config?: UseRegisterFormConfig): UseRegisterForm
76
72
  return password.length > 0 && password === confirmPassword;
77
73
  }, [password, confirmPassword]);
78
74
 
75
+ const clearFormErrors = useCallback(() => {
76
+ setLocalError(null);
77
+ setFieldErrors({});
78
+ }, []);
79
+
79
80
  const handleDisplayNameChange = useCallback((text: string) => {
80
81
  setDisplayName(text);
81
82
  clearFieldError(setFieldErrors, "displayName");
82
- setLocalError(null);
83
- }, []);
83
+ if (localError) setLocalError(null);
84
+ }, [localError]);
84
85
 
85
86
  const handleEmailChange = useCallback((text: string) => {
86
87
  setEmail(text);
87
88
  clearFieldError(setFieldErrors, "email");
88
- setLocalError(null);
89
- }, []);
89
+ if (localError) setLocalError(null);
90
+ }, [localError]);
90
91
 
91
92
  const handlePasswordChange = useCallback((text: string) => {
92
93
  setPassword(text);
93
94
  clearFieldErrors(setFieldErrors, ["password", "confirmPassword"]);
94
- setLocalError(null);
95
- }, []);
95
+ if (localError) setLocalError(null);
96
+ }, [localError]);
96
97
 
97
98
  const handleConfirmPasswordChange = useCallback((text: string) => {
98
99
  setConfirmPassword(text);
99
100
  clearFieldError(setFieldErrors, "confirmPassword");
100
- setLocalError(null);
101
- }, []);
101
+ if (localError) setLocalError(null);
102
+ }, [localError]);
102
103
 
103
104
  const handleSignUp = useCallback(async () => {
104
- setLocalError(null);
105
- setFieldErrors({});
106
-
107
- const emailResult = validateEmail(email.trim());
108
- if (!emailResult.isValid && emailResult.error) {
109
- setFieldErrors((prev) => ({ ...prev, email: getErrorMessage(emailResult.error!) }));
110
- return;
111
- }
112
-
113
- const passwordResult = validatePasswordForRegister(password, DEFAULT_PASSWORD_CONFIG);
114
- if (!passwordResult.isValid && passwordResult.error) {
115
- setFieldErrors((prev) => ({ ...prev, password: getErrorMessage(passwordResult.error!) }));
116
- return;
117
- }
118
-
119
- const confirmResult = validatePasswordConfirmation(password, confirmPassword);
120
- if (!confirmResult.isValid && confirmResult.error) {
121
- setFieldErrors((prev) => ({ ...prev, confirmPassword: getErrorMessage(confirmResult.error!) }));
105
+ clearFormErrors();
106
+
107
+ const validation = validateRegisterForm(
108
+ {
109
+ displayName: displayName.trim() || undefined,
110
+ email: email.trim(),
111
+ password,
112
+ confirmPassword,
113
+ },
114
+ getErrorMessage,
115
+ DEFAULT_PASSWORD_CONFIG
116
+ );
117
+
118
+ if (!validation.isValid) {
119
+ setFieldErrors(errorsToFieldErrors(validation.errors));
122
120
  return;
123
121
  }
124
122
 
@@ -130,10 +128,9 @@ export function useRegisterForm(config?: UseRegisterFormConfig): UseRegisterForm
130
128
  }
131
129
  } catch (err: unknown) {
132
130
  const localizationKey = getAuthErrorLocalizationKey(err);
133
- const errorMessage = getErrorMessage(localizationKey);
134
- setLocalError(errorMessage);
131
+ setLocalError(getErrorMessage(localizationKey));
135
132
  }
136
- }, [displayName, email, password, confirmPassword, signUp, translations, getErrorMessage]);
133
+ }, [displayName, email, password, confirmPassword, signUp, translations, getErrorMessage, clearFormErrors]);
137
134
 
138
135
  const displayError = localError || error;
139
136
 
@@ -66,9 +66,6 @@ export function AuthProvider({ children, ErrorFallback = DefaultErrorFallback }:
66
66
  unsubscribe = initializeAuthListener();
67
67
  } catch (err) {
68
68
  const errorObj = err instanceof Error ? err : new Error("Unknown initialization error");
69
- if (__DEV__) {
70
- console.error("[AuthProvider] Initialization failed:", errorObj);
71
- }
72
69
  setError(errorObj);
73
70
  }
74
71
 
@@ -76,10 +73,8 @@ export function AuthProvider({ children, ErrorFallback = DefaultErrorFallback }:
76
73
  if (unsubscribe) {
77
74
  try {
78
75
  unsubscribe();
79
- } catch (cleanupError) {
80
- if (__DEV__) {
81
- console.warn("[AuthProvider] Cleanup failed:", cleanupError);
82
- }
76
+ } catch {
77
+ // Silently fail - cleanup errors are handled elsewhere
83
78
  }
84
79
  }
85
80
  };
@@ -53,13 +53,6 @@ export const useAuthModalStore = createStore<AuthModalState, AuthModalActions>({
53
53
  callback?: () => void | Promise<void>,
54
54
  mode: AuthModalMode = "login",
55
55
  ) => {
56
- if (__DEV__) {
57
- console.log("[authModalStore] showAuthModal called:", {
58
- mode,
59
- hasCallback: !!callback,
60
- currentVisible: get().isVisible,
61
- });
62
- }
63
56
  set({
64
57
  isVisible: true,
65
58
  mode,