@umituz/react-native-auth 4.3.77 → 4.3.79
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/exports/domain.ts +32 -0
- package/src/exports/infrastructure.ts +80 -0
- package/src/exports/presentation.ts +115 -0
- package/src/exports/shared.ts +93 -0
- package/src/exports/utils.ts +10 -0
- package/src/index.ts +7 -132
- package/src/infrastructure/utils/listener/anonymousSignIn/attemptAnonymousSignIn.ts +81 -0
- package/src/infrastructure/utils/listener/anonymousSignIn/constants.ts +7 -0
- package/src/infrastructure/utils/listener/anonymousSignIn/createAnonymousSignInHandler.ts +59 -0
- package/src/infrastructure/utils/listener/anonymousSignIn/index.ts +16 -0
- package/src/infrastructure/utils/listener/anonymousSignIn/types.ts +21 -0
- package/src/presentation/components/RegisterForm/RegisterForm.tsx +91 -0
- package/src/presentation/components/RegisterForm/RegisterFormFields.tsx +117 -0
- package/src/presentation/components/RegisterForm/index.ts +7 -0
- package/src/presentation/components/RegisterForm/styles.ts +11 -0
- package/src/presentation/components/RegisterForm/types.ts +34 -0
- package/src/presentation/hooks/auth/index.ts +7 -0
- package/src/presentation/hooks/auth/types.ts +26 -0
- package/src/presentation/hooks/auth/useAuthBottomSheet.ts +110 -0
- package/src/presentation/hooks/auth/useSocialAuthHandlers.ts +56 -0
- package/src/shared/error-handling/handlers/ErrorHandler.ts +70 -0
- package/src/shared/error-handling/handlers/FormErrorHandler.ts +99 -0
- package/src/shared/error-handling/handlers/index.ts +7 -0
- package/src/shared/error-handling/index.ts +19 -0
- package/src/shared/error-handling/mappers/ErrorMapper.ts +115 -0
- package/src/shared/error-handling/mappers/FieldErrorMapper.ts +97 -0
- package/src/shared/error-handling/mappers/index.ts +6 -0
- package/src/shared/error-handling/types/ErrorTypes.ts +23 -0
- package/src/shared/error-handling/types/index.ts +10 -0
- package/src/shared/form/builders/FieldBuilder.ts +90 -0
- package/src/shared/form/builders/FormBuilder.ts +126 -0
- package/src/shared/form/builders/index.ts +9 -0
- package/src/shared/form/index.ts +51 -0
- package/src/shared/form/state/FormState.ts +114 -0
- package/src/shared/form/state/index.ts +16 -0
- package/src/shared/form/types/FormTypes.ts +30 -0
- package/src/shared/form/types/index.ts +11 -0
- package/src/shared/form/utils/fieldUtils.ts +115 -0
- package/src/shared/form/utils/formUtils.ts +113 -0
- package/src/shared/form/utils/index.ts +6 -0
- package/src/shared/validation/index.ts +23 -0
- package/src/shared/validation/rules/ValidationRule.ts +71 -0
- package/src/shared/validation/rules/index.ts +5 -0
- package/src/shared/validation/sanitizers/EmailSanitizer.ts +22 -0
- package/src/shared/validation/sanitizers/PasswordSanitizer.ts +24 -0
- package/src/shared/validation/sanitizers/index.ts +6 -0
- package/src/shared/validation/types.ts +26 -0
- package/src/shared/validation/validators/EmailValidator.ts +61 -0
- package/src/shared/validation/validators/NameValidator.ts +52 -0
- package/src/shared/validation/validators/PasswordValidator.ts +92 -0
- package/src/shared/validation/validators/index.ts +8 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anonymous Sign-In Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface AnonymousSignInCallbacks {
|
|
6
|
+
onSignInSuccess: () => void;
|
|
7
|
+
onSignInFailure: (error: Error) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AnonymousSignInOptions {
|
|
11
|
+
maxRetries?: number;
|
|
12
|
+
retryDelay?: number;
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AnonymousStore {
|
|
17
|
+
setFirebaseUser: (user: any) => void;
|
|
18
|
+
setLoading: (loading: boolean) => void;
|
|
19
|
+
setInitialized: (initialized: boolean) => void;
|
|
20
|
+
setError: (error: string | null) => void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Form Component
|
|
3
|
+
* Main registration form with error display and actions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { memo } from 'react';
|
|
7
|
+
import { AtomicButton } from '@umituz/react-native-design-system/atoms';
|
|
8
|
+
import { useRegisterForm } from '../../hooks/useRegisterForm';
|
|
9
|
+
import { AuthErrorDisplay } from '../AuthErrorDisplay';
|
|
10
|
+
import { AuthLink } from '../AuthLink';
|
|
11
|
+
import { AuthLegalLinks } from '../AuthLegalLinks';
|
|
12
|
+
import { RegisterFormFields } from './RegisterFormFields';
|
|
13
|
+
import { styles } from './styles';
|
|
14
|
+
import type { RegisterFormProps } from './types';
|
|
15
|
+
|
|
16
|
+
export const RegisterForm = memo<RegisterFormProps>(({
|
|
17
|
+
translations,
|
|
18
|
+
onNavigateToLogin,
|
|
19
|
+
termsUrl,
|
|
20
|
+
privacyUrl,
|
|
21
|
+
onTermsPress,
|
|
22
|
+
onPrivacyPress,
|
|
23
|
+
}) => {
|
|
24
|
+
const {
|
|
25
|
+
displayName,
|
|
26
|
+
email,
|
|
27
|
+
password,
|
|
28
|
+
confirmPassword,
|
|
29
|
+
fieldErrors,
|
|
30
|
+
loading,
|
|
31
|
+
passwordRequirements,
|
|
32
|
+
passwordsMatch,
|
|
33
|
+
handleDisplayNameChange,
|
|
34
|
+
handleEmailChange,
|
|
35
|
+
handlePasswordChange,
|
|
36
|
+
handleConfirmPasswordChange,
|
|
37
|
+
handleSignUp,
|
|
38
|
+
displayError,
|
|
39
|
+
} = useRegisterForm();
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
<RegisterFormFields
|
|
44
|
+
displayName={displayName}
|
|
45
|
+
email={email}
|
|
46
|
+
password={password}
|
|
47
|
+
confirmPassword={confirmPassword}
|
|
48
|
+
fieldErrors={fieldErrors}
|
|
49
|
+
loading={loading}
|
|
50
|
+
passwordRequirements={passwordRequirements}
|
|
51
|
+
passwordsMatch={passwordsMatch}
|
|
52
|
+
translations={translations}
|
|
53
|
+
onDisplayNameChange={handleDisplayNameChange}
|
|
54
|
+
onEmailChange={handleEmailChange}
|
|
55
|
+
onPasswordChange={handlePasswordChange}
|
|
56
|
+
onConfirmPasswordChange={handleConfirmPasswordChange}
|
|
57
|
+
onSubmit={handleSignUp}
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
<AuthErrorDisplay error={displayError} />
|
|
61
|
+
|
|
62
|
+
<AtomicButton
|
|
63
|
+
variant="primary"
|
|
64
|
+
onPress={() => void handleSignUp()}
|
|
65
|
+
disabled={loading || !email.trim() || !password || !confirmPassword}
|
|
66
|
+
fullWidth
|
|
67
|
+
style={styles.signUpButton}
|
|
68
|
+
>
|
|
69
|
+
{translations.signUp}
|
|
70
|
+
</AtomicButton>
|
|
71
|
+
|
|
72
|
+
<AuthLink
|
|
73
|
+
text={translations.alreadyHaveAccount}
|
|
74
|
+
linkText={translations.signIn}
|
|
75
|
+
onPress={onNavigateToLogin}
|
|
76
|
+
disabled={loading}
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
<AuthLegalLinks
|
|
80
|
+
translations={translations.legal}
|
|
81
|
+
termsUrl={termsUrl}
|
|
82
|
+
privacyUrl={privacyUrl}
|
|
83
|
+
onTermsPress={onTermsPress}
|
|
84
|
+
onPrivacyPress={onPrivacyPress}
|
|
85
|
+
prefixText={translations.bySigningUp}
|
|
86
|
+
/>
|
|
87
|
+
</>
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
RegisterForm.displayName = 'RegisterForm';
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Form Fields
|
|
3
|
+
* Individual form field components for registration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useRef } from 'react';
|
|
7
|
+
import { TextInput } from 'react-native';
|
|
8
|
+
import { FormTextInput } from '../form/FormTextInput';
|
|
9
|
+
import { FormEmailInput } from '../form/FormEmailInput';
|
|
10
|
+
import { FormPasswordInput } from '../form/FormPasswordInput';
|
|
11
|
+
import { PasswordStrengthIndicator } from '../PasswordStrengthIndicator';
|
|
12
|
+
import { PasswordMatchIndicator } from '../PasswordMatchIndicator';
|
|
13
|
+
import type { RegisterFormTranslations } from './types';
|
|
14
|
+
|
|
15
|
+
export interface RegisterFormFieldsProps {
|
|
16
|
+
displayName: string;
|
|
17
|
+
email: string;
|
|
18
|
+
password: string;
|
|
19
|
+
confirmPassword: string;
|
|
20
|
+
fieldErrors: Record<string, string | null>;
|
|
21
|
+
loading: boolean;
|
|
22
|
+
passwordRequirements: any;
|
|
23
|
+
passwordsMatch: boolean;
|
|
24
|
+
translations: RegisterFormTranslations;
|
|
25
|
+
onDisplayNameChange: (value: string) => void;
|
|
26
|
+
onEmailChange: (value: string) => void;
|
|
27
|
+
onPasswordChange: (value: string) => void;
|
|
28
|
+
onConfirmPasswordChange: (value: string) => void;
|
|
29
|
+
onSubmit: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const RegisterFormFields: React.FC<RegisterFormFieldsProps> = ({
|
|
33
|
+
displayName,
|
|
34
|
+
email,
|
|
35
|
+
password,
|
|
36
|
+
confirmPassword,
|
|
37
|
+
fieldErrors,
|
|
38
|
+
loading,
|
|
39
|
+
passwordRequirements,
|
|
40
|
+
passwordsMatch,
|
|
41
|
+
translations,
|
|
42
|
+
onDisplayNameChange,
|
|
43
|
+
onEmailChange,
|
|
44
|
+
onPasswordChange,
|
|
45
|
+
onConfirmPasswordChange,
|
|
46
|
+
onSubmit,
|
|
47
|
+
}) => {
|
|
48
|
+
const emailRef = useRef<React.ElementRef<typeof TextInput>>(null);
|
|
49
|
+
const passwordRef = useRef<React.ElementRef<typeof TextInput>>(null);
|
|
50
|
+
const confirmPasswordRef = useRef<React.ElementRef<typeof TextInput>>(null);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<FormTextInput
|
|
55
|
+
value={displayName}
|
|
56
|
+
onChangeText={onDisplayNameChange}
|
|
57
|
+
label={translations.displayName}
|
|
58
|
+
placeholder={translations.displayNamePlaceholder}
|
|
59
|
+
error={fieldErrors.displayName}
|
|
60
|
+
disabled={loading}
|
|
61
|
+
autoCapitalize="words"
|
|
62
|
+
onSubmitEditing={() => emailRef.current?.focus()}
|
|
63
|
+
returnKeyType="next"
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
<FormEmailInput
|
|
67
|
+
ref={emailRef}
|
|
68
|
+
value={email}
|
|
69
|
+
onChangeText={onEmailChange}
|
|
70
|
+
label={translations.email}
|
|
71
|
+
placeholder={translations.emailPlaceholder}
|
|
72
|
+
error={fieldErrors.email}
|
|
73
|
+
disabled={loading}
|
|
74
|
+
onSubmitEditing={() => passwordRef.current?.focus()}
|
|
75
|
+
returnKeyType="next"
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<FormPasswordInput
|
|
79
|
+
ref={passwordRef}
|
|
80
|
+
value={password}
|
|
81
|
+
onChangeText={onPasswordChange}
|
|
82
|
+
label={translations.password}
|
|
83
|
+
placeholder={translations.passwordPlaceholder}
|
|
84
|
+
error={fieldErrors.password}
|
|
85
|
+
disabled={loading}
|
|
86
|
+
onSubmitEditing={() => confirmPasswordRef.current?.focus()}
|
|
87
|
+
returnKeyType="next"
|
|
88
|
+
style={{ marginBottom: 4 }}
|
|
89
|
+
/>
|
|
90
|
+
{password.length > 0 && (
|
|
91
|
+
<PasswordStrengthIndicator
|
|
92
|
+
translations={translations.passwordStrength}
|
|
93
|
+
requirements={passwordRequirements}
|
|
94
|
+
/>
|
|
95
|
+
)}
|
|
96
|
+
|
|
97
|
+
<FormPasswordInput
|
|
98
|
+
ref={confirmPasswordRef}
|
|
99
|
+
value={confirmPassword}
|
|
100
|
+
onChangeText={onConfirmPasswordChange}
|
|
101
|
+
label={translations.confirmPassword}
|
|
102
|
+
placeholder={translations.confirmPasswordPlaceholder}
|
|
103
|
+
error={fieldErrors.confirmPassword}
|
|
104
|
+
disabled={loading}
|
|
105
|
+
onSubmitEditing={() => void onSubmit()}
|
|
106
|
+
returnKeyType="done"
|
|
107
|
+
style={{ marginBottom: 4 }}
|
|
108
|
+
/>
|
|
109
|
+
{confirmPassword.length > 0 && (
|
|
110
|
+
<PasswordMatchIndicator
|
|
111
|
+
translations={translations.passwordMatch}
|
|
112
|
+
isMatch={passwordsMatch}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
</>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Form Styles
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { StyleSheet } from 'react-native';
|
|
6
|
+
|
|
7
|
+
export const styles = StyleSheet.create({
|
|
8
|
+
passwordInput: { marginBottom: 4 },
|
|
9
|
+
confirmPasswordInput: { marginBottom: 4 },
|
|
10
|
+
signUpButton: { minHeight: 52, marginBottom: 16, marginTop: 8 },
|
|
11
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register Form Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { AuthLegalLinksTranslations } from '../AuthLegalLinks';
|
|
6
|
+
import type { PasswordStrengthTranslations } from '../PasswordStrengthIndicator';
|
|
7
|
+
import type { PasswordMatchTranslations } from '../PasswordMatchIndicator';
|
|
8
|
+
|
|
9
|
+
export interface RegisterFormTranslations {
|
|
10
|
+
displayName: string;
|
|
11
|
+
displayNamePlaceholder: string;
|
|
12
|
+
email: string;
|
|
13
|
+
emailPlaceholder: string;
|
|
14
|
+
password: string;
|
|
15
|
+
passwordPlaceholder: string;
|
|
16
|
+
confirmPassword: string;
|
|
17
|
+
confirmPasswordPlaceholder: string;
|
|
18
|
+
signUp: string;
|
|
19
|
+
alreadyHaveAccount: string;
|
|
20
|
+
signIn: string;
|
|
21
|
+
bySigningUp: string;
|
|
22
|
+
legal: AuthLegalLinksTranslations;
|
|
23
|
+
passwordStrength: PasswordStrengthTranslations;
|
|
24
|
+
passwordMatch: PasswordMatchTranslations;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface RegisterFormProps {
|
|
28
|
+
translations: RegisterFormTranslations;
|
|
29
|
+
onNavigateToLogin: () => void;
|
|
30
|
+
termsUrl?: string;
|
|
31
|
+
privacyUrl?: string;
|
|
32
|
+
onTermsPress?: () => void;
|
|
33
|
+
onPrivacyPress?: () => void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Hooks Module Exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { useAuthBottomSheet } from './useAuthBottomSheet';
|
|
6
|
+
export { useSocialAuthHandlers } from './useSocialAuthHandlers';
|
|
7
|
+
export type { UseAuthBottomSheetParams, SocialAuthConfiguration, SocialAuthHandlers } from './types';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Bottom Sheet Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { GoogleAuthConfig } from '../useGoogleAuth';
|
|
6
|
+
import type { SocialAuthProvider } from '../../domain/value-objects/AuthConfig';
|
|
7
|
+
|
|
8
|
+
export interface SocialAuthConfiguration {
|
|
9
|
+
google?: GoogleAuthConfig;
|
|
10
|
+
apple?: { enabled: boolean };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UseAuthBottomSheetParams {
|
|
14
|
+
socialConfig?: SocialAuthConfiguration;
|
|
15
|
+
onGoogleSignIn?: () => Promise<void>;
|
|
16
|
+
onAppleSignIn?: () => Promise<void>;
|
|
17
|
+
/** Called when auth completes successfully (login or register) */
|
|
18
|
+
onAuthSuccess?: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SocialAuthHandlers {
|
|
22
|
+
handleGoogleSignIn: () => Promise<void>;
|
|
23
|
+
handleAppleSignIn: () => Promise<void>;
|
|
24
|
+
googleLoading: boolean;
|
|
25
|
+
appleLoading: boolean;
|
|
26
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Bottom Sheet Hook (Refactored)
|
|
3
|
+
* Main hook for auth bottom sheet management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
7
|
+
import type { BottomSheetModalRef } from '@umituz/react-native-design-system/molecules';
|
|
8
|
+
import { useAuthModalStore } from '../../stores/authModalStore';
|
|
9
|
+
import { useAuth } from '../useAuth';
|
|
10
|
+
import { determineEnabledProviders } from '../../utils/socialAuthHandler.util';
|
|
11
|
+
import { useAuthTransitions, executeAfterAuth } from '../../utils/authTransition.util';
|
|
12
|
+
import { useSocialAuthHandlers } from './useSocialAuthHandlers';
|
|
13
|
+
import type { SocialAuthProvider } from '../../../domain/value-objects/AuthConfig';
|
|
14
|
+
import type { UseAuthBottomSheetParams } from './types';
|
|
15
|
+
|
|
16
|
+
export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
|
|
17
|
+
const { socialConfig, onGoogleSignIn, onAppleSignIn, onAuthSuccess } = params;
|
|
18
|
+
|
|
19
|
+
const modalRef = useRef<BottomSheetModalRef>(null);
|
|
20
|
+
|
|
21
|
+
const { isVisible, mode, hideAuthModal, setMode, executePendingCallback, clearPendingCallback } =
|
|
22
|
+
useAuthModalStore();
|
|
23
|
+
const { isAuthenticated, isAnonymous } = useAuth();
|
|
24
|
+
|
|
25
|
+
// Social auth handlers
|
|
26
|
+
const { handleGoogleSignIn, handleAppleSignIn, googleLoading, appleLoading } =
|
|
27
|
+
useSocialAuthHandlers(socialConfig, onGoogleSignIn, onAppleSignIn);
|
|
28
|
+
|
|
29
|
+
// Determine enabled providers
|
|
30
|
+
const providers = useMemo<SocialAuthProvider[]>(() => {
|
|
31
|
+
return determineEnabledProviders(socialConfig, appleAvailable, googleConfigured);
|
|
32
|
+
}, [socialConfig, appleAvailable, googleConfigured]);
|
|
33
|
+
|
|
34
|
+
// Handle visibility sync with modalRef
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (isVisible) {
|
|
37
|
+
modalRef.current?.present();
|
|
38
|
+
} else {
|
|
39
|
+
modalRef.current?.dismiss();
|
|
40
|
+
}
|
|
41
|
+
}, [isVisible]);
|
|
42
|
+
|
|
43
|
+
const handleDismiss = useCallback(() => {
|
|
44
|
+
hideAuthModal();
|
|
45
|
+
clearPendingCallback();
|
|
46
|
+
}, [hideAuthModal, clearPendingCallback]);
|
|
47
|
+
|
|
48
|
+
const handleClose = useCallback(() => {
|
|
49
|
+
modalRef.current?.dismiss();
|
|
50
|
+
handleDismiss();
|
|
51
|
+
}, [handleDismiss]);
|
|
52
|
+
|
|
53
|
+
// Handle auth transitions
|
|
54
|
+
useAuthTransitions(
|
|
55
|
+
{ isAuthenticated, isAnonymous, isVisible },
|
|
56
|
+
(result) => {
|
|
57
|
+
if (result.shouldClose) {
|
|
58
|
+
modalRef.current?.dismiss();
|
|
59
|
+
hideAuthModal();
|
|
60
|
+
onAuthSuccess?.();
|
|
61
|
+
|
|
62
|
+
const timeoutId = executeAfterAuth(() => {
|
|
63
|
+
executePendingCallback();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return () => clearTimeout(timeoutId);
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const handleNavigateToRegister = useCallback(() => {
|
|
73
|
+
setMode('register');
|
|
74
|
+
}, [setMode]);
|
|
75
|
+
|
|
76
|
+
const handleNavigateToLogin = useCallback(() => {
|
|
77
|
+
setMode('login');
|
|
78
|
+
}, [setMode]);
|
|
79
|
+
|
|
80
|
+
return useMemo(
|
|
81
|
+
() => ({
|
|
82
|
+
modalRef,
|
|
83
|
+
googleLoading,
|
|
84
|
+
appleLoading,
|
|
85
|
+
mode,
|
|
86
|
+
providers,
|
|
87
|
+
handleDismiss,
|
|
88
|
+
handleClose,
|
|
89
|
+
handleNavigateToRegister,
|
|
90
|
+
handleNavigateToLogin,
|
|
91
|
+
handleGoogleSignIn,
|
|
92
|
+
handleAppleSignIn,
|
|
93
|
+
}),
|
|
94
|
+
[
|
|
95
|
+
modalRef,
|
|
96
|
+
googleLoading,
|
|
97
|
+
appleLoading,
|
|
98
|
+
mode,
|
|
99
|
+
providers,
|
|
100
|
+
handleDismiss,
|
|
101
|
+
handleClose,
|
|
102
|
+
handleNavigateToRegister,
|
|
103
|
+
handleNavigateToLogin,
|
|
104
|
+
handleGoogleSignIn,
|
|
105
|
+
handleAppleSignIn,
|
|
106
|
+
]
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// TODO: Fix appleAvailable and googleConfigured references
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Social Auth Handlers
|
|
3
|
+
* Handles social authentication logic
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback, useState } from 'react';
|
|
7
|
+
import { useGoogleAuth } from '../useGoogleAuth';
|
|
8
|
+
import { useAppleAuth } from '../useAppleAuth';
|
|
9
|
+
import type { SocialAuthConfiguration, SocialAuthHandlers } from './types';
|
|
10
|
+
|
|
11
|
+
export function useSocialAuthHandlers(
|
|
12
|
+
socialConfig?: SocialAuthConfiguration,
|
|
13
|
+
onGoogleSignIn?: () => Promise<void>,
|
|
14
|
+
onAppleSignIn?: () => Promise<void>
|
|
15
|
+
): SocialAuthHandlers {
|
|
16
|
+
// Social Auth Hooks
|
|
17
|
+
const { signInWithGoogle, googleConfigured } = useGoogleAuth(socialConfig?.google);
|
|
18
|
+
const { signInWithApple, appleAvailable } = useAppleAuth();
|
|
19
|
+
|
|
20
|
+
// Social auth loading states
|
|
21
|
+
const [googleLoading, setGoogleLoading] = useState(false);
|
|
22
|
+
const [appleLoading, setAppleLoading] = useState(false);
|
|
23
|
+
|
|
24
|
+
const handleGoogleSignIn = useCallback(async () => {
|
|
25
|
+
setGoogleLoading(true);
|
|
26
|
+
try {
|
|
27
|
+
if (onGoogleSignIn) {
|
|
28
|
+
await onGoogleSignIn();
|
|
29
|
+
} else if (signInWithGoogle) {
|
|
30
|
+
await signInWithGoogle();
|
|
31
|
+
}
|
|
32
|
+
} finally {
|
|
33
|
+
setGoogleLoading(false);
|
|
34
|
+
}
|
|
35
|
+
}, [onGoogleSignIn, signInWithGoogle]);
|
|
36
|
+
|
|
37
|
+
const handleAppleSignIn = useCallback(async () => {
|
|
38
|
+
setAppleLoading(true);
|
|
39
|
+
try {
|
|
40
|
+
if (onAppleSignIn) {
|
|
41
|
+
await onAppleSignIn();
|
|
42
|
+
} else if (signInWithApple) {
|
|
43
|
+
await signInWithApple();
|
|
44
|
+
}
|
|
45
|
+
} finally {
|
|
46
|
+
setAppleLoading(false);
|
|
47
|
+
}
|
|
48
|
+
}, [onAppleSignIn, signInWithApple]);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
handleGoogleSignIn,
|
|
52
|
+
handleAppleSignIn,
|
|
53
|
+
googleLoading,
|
|
54
|
+
appleLoading,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handler
|
|
3
|
+
* Centralized error handling logic
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ErrorMap } from '../types/ErrorTypes';
|
|
7
|
+
import { ErrorMapper, DEFAULT_AUTH_ERROR_MAPPINGS } from '../mappers/ErrorMapper';
|
|
8
|
+
|
|
9
|
+
export class ErrorHandler {
|
|
10
|
+
private mapper: ErrorMapper;
|
|
11
|
+
private translations?: ErrorMap;
|
|
12
|
+
|
|
13
|
+
constructor(translations?: ErrorMap, config?: typeof DEFAULT_AUTH_ERROR_MAPPINGS) {
|
|
14
|
+
this.mapper = new ErrorMapper(config || DEFAULT_AUTH_ERROR_MAPPINGS);
|
|
15
|
+
this.translations = translations;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Handle error and return user-friendly message
|
|
20
|
+
*/
|
|
21
|
+
handle(error: unknown): string {
|
|
22
|
+
const key = this.mapper.getLocalizationKey(error);
|
|
23
|
+
return this.mapper.resolveMessage(key, this.translations);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get localization key for error
|
|
28
|
+
*/
|
|
29
|
+
getErrorKey(error: unknown): string {
|
|
30
|
+
return this.mapper.getLocalizationKey(error);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Update translations
|
|
35
|
+
*/
|
|
36
|
+
setTranslations(translations: ErrorMap): void {
|
|
37
|
+
this.translations = translations;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Set error mappings
|
|
42
|
+
*/
|
|
43
|
+
setMappings(config: Partial<typeof DEFAULT_AUTH_ERROR_MAPPINGS>): void {
|
|
44
|
+
this.mapper.setMappings(config);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if error is of specific type
|
|
49
|
+
*/
|
|
50
|
+
isErrorType(error: unknown, errorName: string): boolean {
|
|
51
|
+
return error instanceof Error && error.name === errorName;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Log error in development
|
|
56
|
+
*/
|
|
57
|
+
logError(context: string, error: unknown): void {
|
|
58
|
+
if (__DEV__) {
|
|
59
|
+
console.error(`[${context}] Error:`, error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Handle and log error
|
|
65
|
+
*/
|
|
66
|
+
handleAndLog(context: string, error: unknown): string {
|
|
67
|
+
this.logError(context, error);
|
|
68
|
+
return this.handle(error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form Error Handler
|
|
3
|
+
* Handles form-specific error logic
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { FieldError, ErrorMap } from '../types/ErrorTypes';
|
|
7
|
+
import { ErrorHandler } from './ErrorHandler';
|
|
8
|
+
import { FieldErrorMapper } from '../mappers/FieldErrorMapper';
|
|
9
|
+
|
|
10
|
+
export interface FormErrorHandlerConfig {
|
|
11
|
+
translations?: ErrorMap;
|
|
12
|
+
errorMappings?: Partial<typeof DEFAULT_AUTH_ERROR_MAPPINGS>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class FormErrorHandler extends ErrorHandler {
|
|
16
|
+
constructor(config: FormErrorHandlerConfig = {}) {
|
|
17
|
+
super(config.translations, config.errorMappings);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handle validation result errors
|
|
22
|
+
*/
|
|
23
|
+
handleValidationErrors(
|
|
24
|
+
errors: FieldError[],
|
|
25
|
+
translations?: ErrorMap
|
|
26
|
+
): Record<string, string> {
|
|
27
|
+
const mappedErrors = translations
|
|
28
|
+
? FieldErrorMapper.mapErrorsToMessages(errors, translations)
|
|
29
|
+
: errors;
|
|
30
|
+
|
|
31
|
+
return FieldErrorMapper.toRecord(mappedErrors);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get form field errors with null support
|
|
36
|
+
*/
|
|
37
|
+
getFormFieldErrors(
|
|
38
|
+
errors: FieldError[],
|
|
39
|
+
fields: string[]
|
|
40
|
+
): Record<string, string | null> {
|
|
41
|
+
return FieldErrorMapper.toFormFieldErrors(errors, fields);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if form has errors
|
|
46
|
+
*/
|
|
47
|
+
formHasErrors(errors: FieldError[]): boolean {
|
|
48
|
+
return errors.length > 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if specific field has error
|
|
53
|
+
*/
|
|
54
|
+
fieldHasError(errors: FieldError[], field: string): boolean {
|
|
55
|
+
return FieldErrorMapper.extractFieldError(errors, field) !== null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get first form error message
|
|
60
|
+
*/
|
|
61
|
+
getFirstFormError(errors: FieldError[]): string | null {
|
|
62
|
+
return FieldErrorMapper.getFirstErrorMessage(errors);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Handle auth error for form
|
|
67
|
+
*/
|
|
68
|
+
handleAuthError(error: unknown): string {
|
|
69
|
+
return this.handle(error);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Clear field errors for specific fields
|
|
74
|
+
*/
|
|
75
|
+
clearFieldErrors(errors: FieldError[], fields: string[]): FieldError[] {
|
|
76
|
+
return FieldErrorMapper.excludeFields(errors, fields);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Filter errors to only specific fields
|
|
81
|
+
*/
|
|
82
|
+
filterToFields(errors: FieldError[], fields: string[]): FieldError[] {
|
|
83
|
+
return FieldErrorMapper.filterByFields(errors, fields);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create field error
|
|
88
|
+
*/
|
|
89
|
+
createFieldError(field: string, message: string): FieldError {
|
|
90
|
+
return FieldErrorMapper.createFieldError(field, message);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Merge multiple error arrays
|
|
95
|
+
*/
|
|
96
|
+
mergeErrors(...errorArrays: FieldError[][]): FieldError[] {
|
|
97
|
+
return FieldErrorMapper.mergeFieldErrors(...errorArrays);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Module Public API
|
|
3
|
+
* Centralized error handling system
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Types
|
|
7
|
+
export type {
|
|
8
|
+
FieldError,
|
|
9
|
+
FormFieldErrors,
|
|
10
|
+
ErrorMap,
|
|
11
|
+
ErrorMappingConfig,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
// Mappers
|
|
15
|
+
export { ErrorMapper, DEFAULT_AUTH_ERROR_MAPPINGS, FieldErrorMapper } from './mappers';
|
|
16
|
+
|
|
17
|
+
// Handlers
|
|
18
|
+
export { ErrorHandler, FormErrorHandler } from './handlers';
|
|
19
|
+
export type { FormErrorHandlerConfig } from './handlers';
|