@umituz/react-native-auth 3.6.85 → 3.6.87
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/repositories/UserDocumentRepository.ts +85 -0
- package/src/infrastructure/services/AnonymousModeService.ts +5 -2
- package/src/infrastructure/services/AuthService.ts +13 -1
- package/src/infrastructure/utils/AuthValidation.ts +8 -4
- package/src/infrastructure/utils/UserMapper.ts +7 -3
- package/src/infrastructure/utils/error/errorCodeMapping.constants.ts +12 -109
- package/src/infrastructure/utils/error/mappings/actionCodeErrorMappings.ts +26 -0
- package/src/infrastructure/utils/error/mappings/authErrorMappings.ts +69 -0
- package/src/infrastructure/utils/error/mappings/configErrorMappings.ts +31 -0
- package/src/infrastructure/utils/error/mappings/errorMapping.types.ts +12 -0
- package/src/infrastructure/utils/error/mappings/networkErrorMappings.ts +22 -0
- package/src/infrastructure/utils/listener/anonymousHandler.ts +31 -0
- package/src/infrastructure/utils/listener/authStateHandler.ts +59 -0
- package/src/infrastructure/utils/listener/cleanupHandlers.ts +46 -0
- package/src/infrastructure/utils/listener/initializationHandlers.ts +26 -0
- package/src/infrastructure/utils/listener/listenerLifecycle.util.ts +19 -152
- package/src/infrastructure/utils/listener/listenerState.util.ts +21 -3
- package/src/infrastructure/utils/listener/setupListener.ts +63 -0
- package/src/presentation/hooks/registerForm/registerFormHandlers.ts +57 -0
- package/src/presentation/hooks/registerForm/registerFormSubmit.ts +64 -0
- package/src/presentation/hooks/registerForm/useRegisterForm.types.ts +39 -0
- package/src/presentation/hooks/useRegisterForm.ts +31 -109
- package/src/presentation/utils/form/formValidation.util.ts +23 -114
- package/src/presentation/utils/form/useFormField.hook.ts +2 -2
- package/src/presentation/utils/form/validation/formValidation.hook.ts +30 -0
- package/src/presentation/utils/form/validation/formValidation.types.ts +31 -0
- package/src/presentation/utils/form/validation/formValidation.utils.ts +17 -0
- package/src/presentation/utils/form/validation/formValidators.ts +86 -0
|
@@ -1,159 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auth Listener Lifecycle
|
|
3
|
-
*
|
|
2
|
+
* Auth Listener Lifecycle - Main Export
|
|
3
|
+
* Re-exports all listener lifecycle utilities from modular files
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
setUnsubscribe,
|
|
13
|
-
incrementRefCount,
|
|
14
|
-
decrementRefCount,
|
|
15
|
-
startAnonymousSignIn,
|
|
16
|
-
completeAnonymousSignIn,
|
|
17
|
-
resetListenerState,
|
|
18
|
-
} from "./listenerState.util";
|
|
19
|
-
import { onIdTokenChanged } from "firebase/auth";
|
|
20
|
-
import { getAuthService } from "../../../infrastructure/services/AuthService";
|
|
6
|
+
// Cleanup handlers
|
|
7
|
+
export {
|
|
8
|
+
createUnsubscribeHandler,
|
|
9
|
+
handleExistingInitialization,
|
|
10
|
+
handleInitializationInProgress,
|
|
11
|
+
} from "./cleanupHandlers";
|
|
21
12
|
|
|
22
|
-
|
|
13
|
+
// Setup listener
|
|
14
|
+
export { setupAuthListener } from "./setupListener";
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*/
|
|
27
|
-
export function createUnsubscribeHandler(): () => void {
|
|
28
|
-
return () => {
|
|
29
|
-
const { shouldCleanup } = decrementRefCount();
|
|
30
|
-
|
|
31
|
-
if (shouldCleanup) {
|
|
32
|
-
resetListenerState();
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Return existing unsubscribe if already initialized
|
|
39
|
-
* Returns null if initialization is in progress
|
|
40
|
-
*/
|
|
41
|
-
export function handleExistingInitialization(): (() => void) | null {
|
|
42
|
-
if (!isListenerInitialized()) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
incrementRefCount();
|
|
47
|
-
return createUnsubscribeHandler();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Return no-op if initialization is in progress
|
|
52
|
-
*/
|
|
53
|
-
export function handleInitializationInProgress(): () => void {
|
|
54
|
-
return () => {
|
|
55
|
-
// No-op - handled by initial initialization
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Setup Firebase auth listener
|
|
61
|
-
*/
|
|
62
|
-
export function setupAuthListener(
|
|
63
|
-
auth: Auth,
|
|
64
|
-
store: Store,
|
|
65
|
-
autoAnonymousSignIn: boolean,
|
|
66
|
-
onAuthStateChange?: (user: User | null) => void
|
|
67
|
-
): void {
|
|
68
|
-
const service = getAuthService();
|
|
69
|
-
|
|
70
|
-
if (service) {
|
|
71
|
-
const isAnonymous = service.getIsAnonymousMode();
|
|
72
|
-
if (isAnonymous) {
|
|
73
|
-
store.setIsAnonymous(true);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
const unsubscribe = onIdTokenChanged(auth, (user) => {
|
|
79
|
-
handleAuthStateChange(user, store, auth, autoAnonymousSignIn, onAuthStateChange);
|
|
80
|
-
});
|
|
16
|
+
// Auth state handler
|
|
17
|
+
export { handleAuthStateChange } from "./authStateHandler";
|
|
81
18
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// If listener setup fails, ensure we clean up and mark as initialized
|
|
85
|
-
console.error("[AuthListener] Failed to setup auth listener:", error);
|
|
86
|
-
completeInitialization();
|
|
87
|
-
store.setLoading(false);
|
|
88
|
-
store.setInitialized(true);
|
|
89
|
-
store.setError("Failed to initialize authentication listener");
|
|
90
|
-
throw error;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Handle auth state change from Firebase
|
|
96
|
-
*/
|
|
97
|
-
function handleAuthStateChange(
|
|
98
|
-
user: User | null,
|
|
99
|
-
store: Store,
|
|
100
|
-
auth: Auth,
|
|
101
|
-
autoAnonymousSignIn: boolean,
|
|
102
|
-
onAuthStateChange?: (user: User | null) => void
|
|
103
|
-
): void {
|
|
104
|
-
try {
|
|
105
|
-
if (!user && autoAnonymousSignIn) {
|
|
106
|
-
// Start anonymous sign-in without blocking
|
|
107
|
-
void handleAnonymousMode(store, auth);
|
|
108
|
-
store.setFirebaseUser(null);
|
|
109
|
-
completeInitialization();
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
store.setFirebaseUser(user);
|
|
114
|
-
store.setInitialized(true);
|
|
115
|
-
|
|
116
|
-
// Handle conversion from anonymous
|
|
117
|
-
if (user && !user.isAnonymous && store.isAnonymous) {
|
|
118
|
-
store.setIsAnonymous(false);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
onAuthStateChange?.(user);
|
|
122
|
-
} catch (error) {
|
|
123
|
-
console.error("[AuthListener] Error handling auth state change:", error);
|
|
124
|
-
// Ensure we don't leave the app in a bad state
|
|
125
|
-
store.setInitialized(true);
|
|
126
|
-
store.setLoading(false);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Handle anonymous mode sign-in
|
|
132
|
-
*/
|
|
133
|
-
async function handleAnonymousMode(store: Store, auth: Auth): Promise<void> {
|
|
134
|
-
if (!startAnonymousSignIn()) {
|
|
135
|
-
return; // Already signing in
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const handleAnonymousSignIn = createAnonymousSignInHandler(auth, store);
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
await handleAnonymousSignIn();
|
|
142
|
-
} finally {
|
|
143
|
-
completeAnonymousSignIn();
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Handle case where Firebase auth is not available
|
|
149
|
-
*/
|
|
150
|
-
export function handleNoFirebaseAuth(store: Store): () => void {
|
|
151
|
-
completeInitialization();
|
|
152
|
-
store.setLoading(false);
|
|
153
|
-
store.setInitialized(true);
|
|
154
|
-
return () => {};
|
|
155
|
-
}
|
|
19
|
+
// Anonymous mode handler
|
|
20
|
+
export { handleAnonymousMode } from "./anonymousHandler";
|
|
156
21
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
22
|
+
// Initialization handlers
|
|
23
|
+
export {
|
|
24
|
+
handleNoFirebaseAuth,
|
|
25
|
+
completeListenerSetup,
|
|
26
|
+
} from "./initializationHandlers";
|
|
@@ -8,6 +8,7 @@ export interface ListenerState {
|
|
|
8
8
|
refCount: number;
|
|
9
9
|
initializationInProgress: boolean;
|
|
10
10
|
anonymousSignInInProgress: boolean;
|
|
11
|
+
cleanupInProgress: boolean;
|
|
11
12
|
unsubscribe: (() => void) | null;
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -16,6 +17,7 @@ const state: ListenerState = {
|
|
|
16
17
|
refCount: 0,
|
|
17
18
|
initializationInProgress: false,
|
|
18
19
|
anonymousSignInInProgress: false,
|
|
20
|
+
cleanupInProgress: false,
|
|
19
21
|
unsubscribe: null,
|
|
20
22
|
};
|
|
21
23
|
|
|
@@ -60,11 +62,16 @@ export function startInitialization(): boolean {
|
|
|
60
62
|
|
|
61
63
|
/**
|
|
62
64
|
* Complete initialization
|
|
65
|
+
* If refCount is 0, set to 1 (first subscriber)
|
|
66
|
+
* Otherwise, keep existing refCount from concurrent subscribers
|
|
63
67
|
*/
|
|
64
68
|
export function completeInitialization(): void {
|
|
65
69
|
state.initializationInProgress = false;
|
|
66
70
|
state.initialized = true;
|
|
67
|
-
|
|
71
|
+
// Only set refCount to 1 if it's still 0 (no concurrent subscribers)
|
|
72
|
+
if (state.refCount === 0) {
|
|
73
|
+
state.refCount = 1;
|
|
74
|
+
}
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
/**
|
|
@@ -85,10 +92,20 @@ export function incrementRefCount(): number {
|
|
|
85
92
|
/**
|
|
86
93
|
* Decrement reference count when a subscriber leaves
|
|
87
94
|
* Returns true if cleanup should be performed
|
|
95
|
+
* Uses cleanupInProgress flag to prevent concurrent cleanup attempts
|
|
88
96
|
*/
|
|
89
97
|
export function decrementRefCount(): { shouldCleanup: boolean; count: number } {
|
|
90
98
|
state.refCount--;
|
|
91
|
-
const shouldCleanup =
|
|
99
|
+
const shouldCleanup =
|
|
100
|
+
state.refCount <= 0 &&
|
|
101
|
+
state.unsubscribe !== null &&
|
|
102
|
+
!state.cleanupInProgress;
|
|
103
|
+
|
|
104
|
+
// If cleanup should happen, mark as in progress to prevent concurrent cleanup
|
|
105
|
+
if (shouldCleanup) {
|
|
106
|
+
state.cleanupInProgress = true;
|
|
107
|
+
}
|
|
108
|
+
|
|
92
109
|
return { shouldCleanup, count: state.refCount };
|
|
93
110
|
}
|
|
94
111
|
|
|
@@ -111,7 +128,7 @@ export function completeAnonymousSignIn(): void {
|
|
|
111
128
|
}
|
|
112
129
|
|
|
113
130
|
/**
|
|
114
|
-
* Reset all state (for testing)
|
|
131
|
+
* Reset all state (for testing and cleanup)
|
|
115
132
|
*/
|
|
116
133
|
export function resetListenerState(): void {
|
|
117
134
|
if (state.unsubscribe) {
|
|
@@ -121,5 +138,6 @@ export function resetListenerState(): void {
|
|
|
121
138
|
state.refCount = 0;
|
|
122
139
|
state.initializationInProgress = false;
|
|
123
140
|
state.anonymousSignInInProgress = false;
|
|
141
|
+
state.cleanupInProgress = false;
|
|
124
142
|
state.unsubscribe = null;
|
|
125
143
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup Auth Listener
|
|
3
|
+
* Configures Firebase auth listener with timeout protection
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Auth, User } from "firebase/auth";
|
|
7
|
+
import { onIdTokenChanged } from "firebase/auth";
|
|
8
|
+
import type { AuthActions } from "../../../types/auth-store.types";
|
|
9
|
+
import { getAuthService } from "../../services/AuthService";
|
|
10
|
+
import { completeInitialization, setUnsubscribe } from "./listenerState.util";
|
|
11
|
+
import { handleAuthStateChange } from "./authStateHandler";
|
|
12
|
+
|
|
13
|
+
type Store = AuthActions & { isAnonymous: boolean };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Setup Firebase auth listener with timeout protection
|
|
17
|
+
*/
|
|
18
|
+
export function setupAuthListener(
|
|
19
|
+
auth: Auth,
|
|
20
|
+
store: Store,
|
|
21
|
+
autoAnonymousSignIn: boolean,
|
|
22
|
+
onAuthStateChange?: (user: User | null) => void
|
|
23
|
+
): void {
|
|
24
|
+
const service = getAuthService();
|
|
25
|
+
|
|
26
|
+
if (service) {
|
|
27
|
+
const isAnonymous = service.getIsAnonymousMode();
|
|
28
|
+
if (isAnonymous) {
|
|
29
|
+
store.setIsAnonymous(true);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Safety timeout: if listener doesn't trigger within 10 seconds, mark as initialized
|
|
34
|
+
let hasTriggered = false;
|
|
35
|
+
const timeout = setTimeout(() => {
|
|
36
|
+
if (!hasTriggered) {
|
|
37
|
+
console.warn("[AuthListener] Auth listener timeout - marking as initialized");
|
|
38
|
+
store.setInitialized(true);
|
|
39
|
+
store.setLoading(false);
|
|
40
|
+
}
|
|
41
|
+
}, 10000);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const unsubscribe = onIdTokenChanged(auth, (user) => {
|
|
45
|
+
if (!hasTriggered) {
|
|
46
|
+
hasTriggered = true;
|
|
47
|
+
clearTimeout(timeout);
|
|
48
|
+
}
|
|
49
|
+
handleAuthStateChange(user, store, auth, autoAnonymousSignIn, onAuthStateChange);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
setUnsubscribe(unsubscribe);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
clearTimeout(timeout);
|
|
55
|
+
// If listener setup fails, ensure we clean up and mark as initialized
|
|
56
|
+
console.error("[AuthListener] Failed to setup auth listener:", error);
|
|
57
|
+
completeInitialization();
|
|
58
|
+
store.setLoading(false);
|
|
59
|
+
store.setInitialized(true);
|
|
60
|
+
store.setError("Failed to initialize authentication listener");
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Form Change Handlers
|
|
3
|
+
* Input field change handlers with error clearing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback } from "react";
|
|
7
|
+
import type { FieldErrors } from "./useRegisterForm.types";
|
|
8
|
+
import { clearFieldError, clearFieldErrors } from "../../utils/form/formErrorUtils";
|
|
9
|
+
|
|
10
|
+
export function useRegisterFormHandlers(
|
|
11
|
+
updateField: (field: string, value: string) => void,
|
|
12
|
+
setFieldErrors: React.Dispatch<React.SetStateAction<FieldErrors>>,
|
|
13
|
+
clearLocalError: () => void
|
|
14
|
+
) {
|
|
15
|
+
const handleDisplayNameChange = useCallback(
|
|
16
|
+
(text: string) => {
|
|
17
|
+
updateField("displayName", text);
|
|
18
|
+
clearFieldError(setFieldErrors, "displayName");
|
|
19
|
+
clearLocalError();
|
|
20
|
+
},
|
|
21
|
+
[updateField, clearLocalError]
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const handleEmailChange = useCallback(
|
|
25
|
+
(text: string) => {
|
|
26
|
+
updateField("email", text);
|
|
27
|
+
clearFieldError(setFieldErrors, "email");
|
|
28
|
+
clearLocalError();
|
|
29
|
+
},
|
|
30
|
+
[updateField, clearLocalError]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const handlePasswordChange = useCallback(
|
|
34
|
+
(text: string) => {
|
|
35
|
+
updateField("password", text);
|
|
36
|
+
clearFieldErrors(setFieldErrors, ["password", "confirmPassword"]);
|
|
37
|
+
clearLocalError();
|
|
38
|
+
},
|
|
39
|
+
[updateField, clearLocalError]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const handleConfirmPasswordChange = useCallback(
|
|
43
|
+
(text: string) => {
|
|
44
|
+
updateField("confirmPassword", text);
|
|
45
|
+
clearFieldError(setFieldErrors, "confirmPassword");
|
|
46
|
+
clearLocalError();
|
|
47
|
+
},
|
|
48
|
+
[updateField, clearLocalError]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
handleDisplayNameChange,
|
|
53
|
+
handleEmailChange,
|
|
54
|
+
handlePasswordChange,
|
|
55
|
+
handleConfirmPasswordChange,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Form Submit Logic
|
|
3
|
+
* Handles form validation and sign-up submission
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback } from "react";
|
|
7
|
+
import { alertService } from "@umituz/react-native-design-system";
|
|
8
|
+
import { DEFAULT_PASSWORD_CONFIG } from "../../../domain/value-objects/AuthConfig";
|
|
9
|
+
import {
|
|
10
|
+
validateRegisterForm,
|
|
11
|
+
errorsToFieldErrors,
|
|
12
|
+
} from "../../utils/form/formValidation.util";
|
|
13
|
+
import { getAuthErrorLocalizationKey } from "../../utils/getAuthErrorMessage";
|
|
14
|
+
import type { FieldErrors, RegisterFormTranslations } from "./useRegisterForm.types";
|
|
15
|
+
|
|
16
|
+
interface RegisterFormFields {
|
|
17
|
+
displayName: string;
|
|
18
|
+
email: string;
|
|
19
|
+
password: string;
|
|
20
|
+
confirmPassword: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useRegisterFormSubmit(
|
|
24
|
+
fields: RegisterFormFields,
|
|
25
|
+
signUp: (email: string, password: string, displayName?: string) => Promise<void>,
|
|
26
|
+
setFieldErrors: React.Dispatch<React.SetStateAction<FieldErrors>>,
|
|
27
|
+
setLocalError: React.Dispatch<React.SetStateAction<string | null>>,
|
|
28
|
+
clearFormErrors: () => void,
|
|
29
|
+
getErrorMessage: (key: string) => string,
|
|
30
|
+
translations?: RegisterFormTranslations
|
|
31
|
+
) {
|
|
32
|
+
const handleSignUp = useCallback(async () => {
|
|
33
|
+
clearFormErrors();
|
|
34
|
+
|
|
35
|
+
const validation = validateRegisterForm(
|
|
36
|
+
{
|
|
37
|
+
displayName: fields.displayName.trim() || undefined,
|
|
38
|
+
email: fields.email.trim(),
|
|
39
|
+
password: fields.password,
|
|
40
|
+
confirmPassword: fields.confirmPassword,
|
|
41
|
+
},
|
|
42
|
+
getErrorMessage,
|
|
43
|
+
DEFAULT_PASSWORD_CONFIG
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if (!validation.isValid) {
|
|
47
|
+
setFieldErrors(errorsToFieldErrors(validation.errors));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
await signUp(fields.email.trim(), fields.password, fields.displayName.trim() || undefined);
|
|
53
|
+
|
|
54
|
+
if (translations) {
|
|
55
|
+
alertService.success(translations.successTitle, translations.signUpSuccess);
|
|
56
|
+
}
|
|
57
|
+
} catch (err: unknown) {
|
|
58
|
+
const localizationKey = getAuthErrorLocalizationKey(err);
|
|
59
|
+
setLocalError(getErrorMessage(localizationKey));
|
|
60
|
+
}
|
|
61
|
+
}, [fields, signUp, translations, getErrorMessage, clearFormErrors, setFieldErrors, setLocalError]);
|
|
62
|
+
|
|
63
|
+
return { handleSignUp };
|
|
64
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Form Types
|
|
3
|
+
* Type definitions for register form hook
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type FieldErrors = {
|
|
7
|
+
displayName?: string;
|
|
8
|
+
email?: string;
|
|
9
|
+
password?: string;
|
|
10
|
+
confirmPassword?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export interface RegisterFormTranslations {
|
|
14
|
+
successTitle: string;
|
|
15
|
+
signUpSuccess: string;
|
|
16
|
+
errors: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UseRegisterFormConfig {
|
|
20
|
+
translations: RegisterFormTranslations;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface UseRegisterFormResult {
|
|
24
|
+
displayName: string;
|
|
25
|
+
email: string;
|
|
26
|
+
password: string;
|
|
27
|
+
confirmPassword: string;
|
|
28
|
+
fieldErrors: FieldErrors;
|
|
29
|
+
localError: string | null;
|
|
30
|
+
loading: boolean;
|
|
31
|
+
passwordRequirements: { hasMinLength: boolean };
|
|
32
|
+
passwordsMatch: boolean;
|
|
33
|
+
handleDisplayNameChange: (text: string) => void;
|
|
34
|
+
handleEmailChange: (text: string) => void;
|
|
35
|
+
handlePasswordChange: (text: string) => void;
|
|
36
|
+
handleConfirmPasswordChange: (text: string) => void;
|
|
37
|
+
handleSignUp: () => Promise<void>;
|
|
38
|
+
displayError: string | null;
|
|
39
|
+
}
|
|
@@ -1,47 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Form Hook
|
|
3
|
+
* Main hook that combines all register form logic
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
import { useState, useCallback } from "react";
|
|
2
7
|
import { DEFAULT_PASSWORD_CONFIG } from "../../domain/value-objects/AuthConfig";
|
|
3
8
|
import { useAuth } from "./useAuth";
|
|
4
|
-
import {
|
|
5
|
-
import { validateRegisterForm, errorsToFieldErrors } from "../utils/form/formValidation.util";
|
|
6
|
-
import { alertService } from "@umituz/react-native-design-system";
|
|
9
|
+
import { resolveErrorMessage } from "../utils/getAuthErrorMessage";
|
|
7
10
|
import { useFormFields } from "../utils/form/useFormField.hook";
|
|
8
11
|
import { usePasswordValidation } from "../utils/form/usePasswordValidation.hook";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export interface RegisterFormTranslations {
|
|
19
|
-
successTitle: string;
|
|
20
|
-
signUpSuccess: string;
|
|
21
|
-
errors: Record<string, string>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface UseRegisterFormConfig {
|
|
25
|
-
translations: RegisterFormTranslations;
|
|
26
|
-
}
|
|
12
|
+
import { useRegisterFormHandlers } from "./registerForm/registerFormHandlers";
|
|
13
|
+
import { useRegisterFormSubmit } from "./registerForm/registerFormSubmit";
|
|
14
|
+
import type {
|
|
15
|
+
FieldErrors,
|
|
16
|
+
UseRegisterFormConfig,
|
|
17
|
+
UseRegisterFormResult,
|
|
18
|
+
} from "./registerForm/useRegisterForm.types";
|
|
27
19
|
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
email: string;
|
|
31
|
-
password: string;
|
|
32
|
-
confirmPassword: string;
|
|
33
|
-
fieldErrors: FieldErrors;
|
|
34
|
-
localError: string | null;
|
|
35
|
-
loading: boolean;
|
|
36
|
-
passwordRequirements: { hasMinLength: boolean };
|
|
37
|
-
passwordsMatch: boolean;
|
|
38
|
-
handleDisplayNameChange: (text: string) => void;
|
|
39
|
-
handleEmailChange: (text: string) => void;
|
|
40
|
-
handlePasswordChange: (text: string) => void;
|
|
41
|
-
handleConfirmPasswordChange: (text: string) => void;
|
|
42
|
-
handleSignUp: () => Promise<void>;
|
|
43
|
-
displayError: string | null;
|
|
44
|
-
}
|
|
20
|
+
// Re-export types for backward compatibility
|
|
21
|
+
export type { FieldErrors, RegisterFormTranslations, UseRegisterFormConfig, UseRegisterFormResult } from "./registerForm/useRegisterForm.types";
|
|
45
22
|
|
|
46
23
|
export function useRegisterForm(config?: UseRegisterFormConfig): UseRegisterFormResult {
|
|
47
24
|
const { signUp, loading, error } = useAuth();
|
|
@@ -70,9 +47,12 @@ export function useRegisterForm(config?: UseRegisterFormConfig): UseRegisterForm
|
|
|
70
47
|
{ clearLocalError }
|
|
71
48
|
);
|
|
72
49
|
|
|
73
|
-
const getErrorMessage = useCallback(
|
|
74
|
-
|
|
75
|
-
|
|
50
|
+
const getErrorMessage = useCallback(
|
|
51
|
+
(key: string) => {
|
|
52
|
+
return resolveErrorMessage(key, translations?.errors);
|
|
53
|
+
},
|
|
54
|
+
[translations]
|
|
55
|
+
);
|
|
76
56
|
|
|
77
57
|
const { passwordRequirements, passwordsMatch } = usePasswordValidation(
|
|
78
58
|
fields.password,
|
|
@@ -80,73 +60,18 @@ export function useRegisterForm(config?: UseRegisterFormConfig): UseRegisterForm
|
|
|
80
60
|
{ passwordConfig: DEFAULT_PASSWORD_CONFIG }
|
|
81
61
|
);
|
|
82
62
|
|
|
83
|
-
const
|
|
84
|
-
(text: string) => {
|
|
85
|
-
updateField("displayName", text);
|
|
86
|
-
clearFieldError(setFieldErrors, "displayName");
|
|
87
|
-
clearLocalError();
|
|
88
|
-
},
|
|
89
|
-
[updateField, clearLocalError]
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const handleEmailChange = useCallback(
|
|
93
|
-
(text: string) => {
|
|
94
|
-
updateField("email", text);
|
|
95
|
-
clearFieldError(setFieldErrors, "email");
|
|
96
|
-
clearLocalError();
|
|
97
|
-
},
|
|
98
|
-
[updateField, clearLocalError]
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
const handlePasswordChange = useCallback(
|
|
102
|
-
(text: string) => {
|
|
103
|
-
updateField("password", text);
|
|
104
|
-
clearFieldErrors(setFieldErrors, ["password", "confirmPassword"]);
|
|
105
|
-
clearLocalError();
|
|
106
|
-
},
|
|
107
|
-
[updateField, clearLocalError]
|
|
108
|
-
);
|
|
63
|
+
const handlers = useRegisterFormHandlers(updateField, setFieldErrors, clearLocalError);
|
|
109
64
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
65
|
+
const { handleSignUp } = useRegisterFormSubmit(
|
|
66
|
+
fields,
|
|
67
|
+
signUp,
|
|
68
|
+
setFieldErrors,
|
|
69
|
+
setLocalError,
|
|
70
|
+
clearFormErrors,
|
|
71
|
+
getErrorMessage,
|
|
72
|
+
translations
|
|
117
73
|
);
|
|
118
74
|
|
|
119
|
-
const handleSignUp = useCallback(async () => {
|
|
120
|
-
clearFormErrors();
|
|
121
|
-
|
|
122
|
-
const validation = validateRegisterForm(
|
|
123
|
-
{
|
|
124
|
-
displayName: fields.displayName.trim() || undefined,
|
|
125
|
-
email: fields.email.trim(),
|
|
126
|
-
password: fields.password,
|
|
127
|
-
confirmPassword: fields.confirmPassword,
|
|
128
|
-
},
|
|
129
|
-
getErrorMessage,
|
|
130
|
-
DEFAULT_PASSWORD_CONFIG
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
if (!validation.isValid) {
|
|
134
|
-
setFieldErrors(errorsToFieldErrors(validation.errors));
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
await signUp(fields.email.trim(), fields.password, fields.displayName.trim() || undefined);
|
|
140
|
-
|
|
141
|
-
if (translations) {
|
|
142
|
-
alertService.success(translations.successTitle, translations.signUpSuccess);
|
|
143
|
-
}
|
|
144
|
-
} catch (err: unknown) {
|
|
145
|
-
const localizationKey = getAuthErrorLocalizationKey(err);
|
|
146
|
-
setLocalError(getErrorMessage(localizationKey));
|
|
147
|
-
}
|
|
148
|
-
}, [fields, signUp, translations, getErrorMessage, clearFormErrors, updateField]);
|
|
149
|
-
|
|
150
75
|
const displayError = localError || error;
|
|
151
76
|
|
|
152
77
|
return {
|
|
@@ -159,10 +84,7 @@ export function useRegisterForm(config?: UseRegisterFormConfig): UseRegisterForm
|
|
|
159
84
|
loading,
|
|
160
85
|
passwordRequirements,
|
|
161
86
|
passwordsMatch,
|
|
162
|
-
|
|
163
|
-
handleEmailChange,
|
|
164
|
-
handlePasswordChange,
|
|
165
|
-
handleConfirmPasswordChange,
|
|
87
|
+
...handlers,
|
|
166
88
|
handleSignUp,
|
|
167
89
|
displayError,
|
|
168
90
|
};
|