@umituz/react-native-auth 4.3.80 → 4.3.82
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 +4 -1
- package/src/index.ts +303 -6
- package/src/infrastructure/services/AnonymousModeService.ts +18 -8
- package/src/infrastructure/services/AuthService.ts +9 -4
- package/src/infrastructure/utils/AuthValidation.ts +2 -2
- package/src/infrastructure/utils/calculators/passwordStrengthCalculator.ts +2 -2
- package/src/infrastructure/utils/listener/anonymousSignIn/createAnonymousSignInHandler.ts +0 -1
- package/src/infrastructure/utils/listener/anonymousSignIn/types.ts +3 -1
- package/src/infrastructure/utils/listener/listenerState.util.ts +2 -1
- package/src/infrastructure/utils/validation/types.ts +2 -28
- package/src/presentation/components/PasswordStrengthIndicator.tsx +1 -1
- package/src/presentation/components/RegisterForm/RegisterForm.tsx +3 -1
- package/src/presentation/components/RegisterForm/RegisterFormFields.tsx +3 -2
- package/src/presentation/hooks/auth/types.ts +0 -1
- package/src/presentation/hooks/auth/useSocialAuthHandlers.ts +2 -2
- package/src/presentation/hooks/useAuthBottomSheet.ts +2 -2
- package/src/presentation/hooks/useAuthHandlers.ts +4 -2
- package/src/presentation/navigation/AuthNavigator.tsx +28 -7
- package/src/presentation/utils/form/usePasswordValidation.hook.ts +3 -3
- package/src/shared/error-handling/handlers/FormErrorHandler.ts +1 -6
- package/src/shared/error-handling/handlers/index.ts +1 -1
- package/src/shared/error-handling/mappers/ErrorMapper.ts +5 -1
- package/src/shared/error-handling/types/ErrorTypes.ts +5 -0
- package/src/shared/error-handling/types/index.ts +1 -0
- package/src/shared/form/builders/FieldBuilder.ts +1 -2
- package/src/shared/form/builders/FormBuilder.ts +1 -3
- package/src/shared/form/utils/formUtils.ts +1 -1
- package/src/shared/validation/rules/ValidationRule.ts +3 -3
- package/src/shared/validation/sanitizers/NameSanitizer.ts +22 -0
- package/src/shared/validation/sanitizers/index.ts +1 -0
- package/src/shared/validation/types.ts +1 -1
- package/src/shared/validation/validators/EmailValidator.ts +1 -1
- package/src/shared/validation/validators/NameValidator.ts +1 -1
- package/src/shared/validation/validators/PasswordValidator.ts +2 -2
- package/src/exports/domain.ts +0 -32
- package/src/exports/infrastructure.ts +0 -80
- package/src/exports/presentation.ts +0 -115
- package/src/exports/shared.ts +0 -92
- package/src/exports/utils.ts +0 -10
- package/src/shared/error-handling/index.ts +0 -19
- package/src/shared/form/index.ts +0 -51
- package/src/shared/form/utils/index.ts +0 -6
- package/src/shared/validation/index.ts +0 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.82",
|
|
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",
|
|
@@ -65,6 +65,9 @@
|
|
|
65
65
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
66
66
|
"@react-native-community/datetimepicker": "^8.2.0",
|
|
67
67
|
"@react-native-community/slider": "^5.1.1",
|
|
68
|
+
"@react-navigation/bottom-tabs": "^7.15.5",
|
|
69
|
+
"@react-navigation/native": "^7.1.33",
|
|
70
|
+
"@react-navigation/stack": "^7.8.5",
|
|
68
71
|
"@tanstack/query-async-storage-persister": "^5.66.7",
|
|
69
72
|
"@tanstack/react-query": "^5.0.0",
|
|
70
73
|
"@tanstack/react-query-persist-client": "^5.66.7",
|
package/src/index.ts
CHANGED
|
@@ -8,24 +8,321 @@
|
|
|
8
8
|
// =============================================================================
|
|
9
9
|
// DOMAIN LAYER
|
|
10
10
|
// =============================================================================
|
|
11
|
-
export
|
|
11
|
+
export type { AuthUser, AuthProviderType } from './domain/entities/AuthUser';
|
|
12
|
+
export type { UserProfile, UpdateProfileParams } from './domain/entities/UserProfile';
|
|
13
|
+
export {
|
|
14
|
+
AuthError,
|
|
15
|
+
AuthInitializationError,
|
|
16
|
+
AuthConfigurationError,
|
|
17
|
+
AuthValidationError,
|
|
18
|
+
AuthNetworkError,
|
|
19
|
+
AuthUserNotFoundError,
|
|
20
|
+
AuthWrongPasswordError,
|
|
21
|
+
AuthEmailAlreadyInUseError,
|
|
22
|
+
AuthWeakPasswordError,
|
|
23
|
+
AuthInvalidEmailError,
|
|
24
|
+
} from './domain/errors/AuthError';
|
|
25
|
+
export type {
|
|
26
|
+
AuthConfig,
|
|
27
|
+
PasswordConfig,
|
|
28
|
+
SocialAuthConfig,
|
|
29
|
+
SocialProviderConfig,
|
|
30
|
+
GoogleAuthConfig,
|
|
31
|
+
AppleAuthConfig,
|
|
32
|
+
SocialAuthProvider,
|
|
33
|
+
} from './domain/value-objects/AuthConfig';
|
|
34
|
+
export {
|
|
35
|
+
DEFAULT_AUTH_CONFIG,
|
|
36
|
+
DEFAULT_PASSWORD_CONFIG,
|
|
37
|
+
DEFAULT_SOCIAL_CONFIG,
|
|
38
|
+
} from './domain/value-objects/AuthConfig';
|
|
12
39
|
|
|
13
40
|
// =============================================================================
|
|
14
41
|
// INFRASTRUCTURE LAYER
|
|
15
42
|
// =============================================================================
|
|
16
|
-
export
|
|
43
|
+
export type { AuthCredentials, SignUpCredentials } from './infrastructure/repositories/AuthRepository';
|
|
44
|
+
|
|
45
|
+
// Services
|
|
46
|
+
export {
|
|
47
|
+
AuthService,
|
|
48
|
+
initializeAuthService,
|
|
49
|
+
getAuthService,
|
|
50
|
+
resetAuthService,
|
|
51
|
+
} from './infrastructure/services/AuthService';
|
|
52
|
+
export {
|
|
53
|
+
initializeAuth,
|
|
54
|
+
isAuthInitialized,
|
|
55
|
+
resetAuthInitialization,
|
|
56
|
+
} from './infrastructure/services/initializeAuth';
|
|
57
|
+
export type { InitializeAuthOptions } from './infrastructure/services/initializeAuth';
|
|
58
|
+
|
|
59
|
+
// Storage
|
|
60
|
+
export type { IStorageProvider } from './infrastructure/types/Storage.types';
|
|
61
|
+
export {
|
|
62
|
+
createStorageProvider,
|
|
63
|
+
StorageProviderAdapter,
|
|
64
|
+
} from './infrastructure/adapters/StorageProviderAdapter';
|
|
65
|
+
|
|
66
|
+
// Validation
|
|
67
|
+
export {
|
|
68
|
+
validateEmail,
|
|
69
|
+
validatePasswordForLogin,
|
|
70
|
+
validatePasswordForRegister,
|
|
71
|
+
validatePasswordConfirmation,
|
|
72
|
+
validateDisplayName,
|
|
73
|
+
} from './infrastructure/utils/AuthValidation';
|
|
74
|
+
export type {
|
|
75
|
+
FormValidationError,
|
|
76
|
+
FormValidationResult,
|
|
77
|
+
} from './infrastructure/utils/validation/types';
|
|
78
|
+
export {
|
|
79
|
+
SECURITY_LIMITS,
|
|
80
|
+
sanitizeEmail,
|
|
81
|
+
sanitizePassword,
|
|
82
|
+
sanitizeName,
|
|
83
|
+
} from './infrastructure/utils/validation/sanitization';
|
|
84
|
+
export type { SecurityLimitKey } from './infrastructure/utils/validation/sanitization';
|
|
85
|
+
export {
|
|
86
|
+
isEmpty,
|
|
87
|
+
isEmptyEmail,
|
|
88
|
+
isEmptyPassword,
|
|
89
|
+
isEmptyName,
|
|
90
|
+
isNotEmpty,
|
|
91
|
+
hasContent,
|
|
92
|
+
} from './infrastructure/utils/validation/validationHelpers';
|
|
93
|
+
export { safeCallback, safeCallbackSync } from './infrastructure/utils/safeCallback';
|
|
94
|
+
|
|
95
|
+
// Calculators
|
|
96
|
+
export {
|
|
97
|
+
calculateUserId,
|
|
98
|
+
calculateHasFirebaseUser,
|
|
99
|
+
calculateIsAnonymous,
|
|
100
|
+
calculateIsAuthenticated,
|
|
101
|
+
calculateUserType,
|
|
102
|
+
calculateIsAuthReady,
|
|
103
|
+
calculateDerivedAuthState,
|
|
104
|
+
collectFieldErrors,
|
|
105
|
+
extractFieldError,
|
|
106
|
+
hasFieldErrors,
|
|
107
|
+
getFirstErrorMessage,
|
|
108
|
+
calculateUserProfileDisplay,
|
|
109
|
+
calculatePasswordRequirements,
|
|
110
|
+
calculatePasswordsMatch,
|
|
111
|
+
calculatePasswordValidation,
|
|
112
|
+
calculatePasswordStrength,
|
|
113
|
+
} from './infrastructure/utils/calculators';
|
|
114
|
+
|
|
115
|
+
// =============================================================================
|
|
116
|
+
// PRESENTATION LAYER - Hooks
|
|
117
|
+
// =============================================================================
|
|
118
|
+
export { useAuth } from './presentation/hooks/useAuth';
|
|
119
|
+
export type { UseAuthResult } from './presentation/hooks/useAuth';
|
|
120
|
+
export { useLoginForm } from './presentation/hooks/useLoginForm';
|
|
121
|
+
export type { UseLoginFormConfig, UseLoginFormResult } from './presentation/hooks/useLoginForm';
|
|
122
|
+
export { useRegisterForm } from './presentation/hooks/useRegisterForm';
|
|
123
|
+
export type {
|
|
124
|
+
UseRegisterFormConfig,
|
|
125
|
+
UseRegisterFormResult,
|
|
126
|
+
} from './presentation/hooks/useRegisterForm';
|
|
127
|
+
export { useAuthRequired } from './presentation/hooks/useAuthRequired';
|
|
128
|
+
export { useRequireAuth, useUserId } from './presentation/hooks/useRequireAuth';
|
|
129
|
+
export { useUserProfile } from './presentation/hooks/useUserProfile';
|
|
130
|
+
export type {
|
|
131
|
+
UserProfileData,
|
|
132
|
+
UseUserProfileParams,
|
|
133
|
+
} from './presentation/hooks/useUserProfile';
|
|
134
|
+
export { useAccountManagement } from './presentation/hooks/useAccountManagement';
|
|
135
|
+
export type {
|
|
136
|
+
UseAccountManagementReturn,
|
|
137
|
+
UseAccountManagementOptions,
|
|
138
|
+
} from './presentation/hooks/useAccountManagement';
|
|
139
|
+
export { useAppleAuth } from './presentation/hooks/useAppleAuth';
|
|
140
|
+
export type { UseAppleAuthResult } from './presentation/hooks/useAppleAuth';
|
|
141
|
+
export { useAuthBottomSheet } from './presentation/hooks/useAuthBottomSheet';
|
|
142
|
+
export type {
|
|
143
|
+
SocialAuthConfiguration,
|
|
144
|
+
} from './presentation/hooks/useAuthBottomSheet';
|
|
145
|
+
export { useAuthHandlers } from './presentation/hooks/useAuthHandlers';
|
|
146
|
+
export type {
|
|
147
|
+
AuthHandlersAppInfo,
|
|
148
|
+
AuthHandlersTranslations,
|
|
149
|
+
} from './presentation/hooks/useAuthHandlers';
|
|
150
|
+
export { usePasswordPromptNavigation } from './presentation/hooks/usePasswordPromptNavigation';
|
|
151
|
+
export type {
|
|
152
|
+
UsePasswordPromptNavigationOptions,
|
|
153
|
+
UsePasswordPromptNavigationReturn,
|
|
154
|
+
} from './presentation/hooks/usePasswordPromptNavigation';
|
|
155
|
+
export { useAuthErrorHandler } from './presentation/hooks/useAuthErrorHandler';
|
|
156
|
+
export type {
|
|
157
|
+
UseAuthErrorHandlerConfig,
|
|
158
|
+
UseAuthErrorHandlerResult,
|
|
159
|
+
} from './presentation/hooks/useAuthErrorHandler';
|
|
160
|
+
export { useLocalError } from './presentation/hooks/useLocalError';
|
|
161
|
+
export type { UseLocalErrorResult } from './presentation/hooks/useLocalError';
|
|
162
|
+
|
|
163
|
+
// =============================================================================
|
|
164
|
+
// PRESENTATION LAYER - Components
|
|
165
|
+
// =============================================================================
|
|
166
|
+
export { AuthProvider } from './presentation/providers/AuthProvider';
|
|
167
|
+
export type { ErrorFallbackProps } from './presentation/providers/AuthProvider';
|
|
168
|
+
export { LoginScreen } from './presentation/screens/LoginScreen';
|
|
169
|
+
export type { LoginScreenProps } from './presentation/screens/LoginScreen';
|
|
170
|
+
export { RegisterScreen } from './presentation/screens/RegisterScreen';
|
|
171
|
+
export type { RegisterScreenProps } from './presentation/screens/RegisterScreen';
|
|
172
|
+
export { AccountScreen } from './presentation/screens/AccountScreen';
|
|
173
|
+
export type {
|
|
174
|
+
AccountScreenProps,
|
|
175
|
+
AccountScreenConfig,
|
|
176
|
+
} from './presentation/screens/AccountScreen';
|
|
177
|
+
export { EditProfileScreen } from './presentation/screens/EditProfileScreen';
|
|
178
|
+
export type {
|
|
179
|
+
EditProfileScreenProps,
|
|
180
|
+
} from './presentation/screens/EditProfileScreen';
|
|
181
|
+
export { PasswordPromptScreen } from './presentation/screens/PasswordPromptScreen';
|
|
182
|
+
export type {
|
|
183
|
+
PasswordPromptScreenProps,
|
|
184
|
+
} from './presentation/screens/PasswordPromptScreen';
|
|
185
|
+
export { AuthNavigator } from './presentation/navigation/AuthNavigator';
|
|
186
|
+
export type { AuthStackParamList } from './presentation/navigation/AuthNavigator';
|
|
187
|
+
export { AuthBottomSheet } from './presentation/components/AuthBottomSheet';
|
|
188
|
+
export type {
|
|
189
|
+
AuthBottomSheetProps,
|
|
190
|
+
AuthBottomSheetTranslations,
|
|
191
|
+
} from './presentation/components/AuthBottomSheet';
|
|
192
|
+
export { ProfileSection } from './presentation/components/ProfileSection';
|
|
193
|
+
export type {
|
|
194
|
+
ProfileSectionProps,
|
|
195
|
+
ProfileSectionConfig,
|
|
196
|
+
} from './presentation/components/ProfileSection';
|
|
17
197
|
|
|
18
198
|
// =============================================================================
|
|
19
|
-
// PRESENTATION LAYER
|
|
199
|
+
// PRESENTATION LAYER - Stores
|
|
20
200
|
// =============================================================================
|
|
21
|
-
export
|
|
201
|
+
export { useAuthStore } from './presentation/stores/authStore';
|
|
202
|
+
export { useAuthModalStore } from './presentation/stores/authModalStore';
|
|
203
|
+
export {
|
|
204
|
+
initializeAuthListener,
|
|
205
|
+
resetAuthListener,
|
|
206
|
+
isAuthListenerInitialized,
|
|
207
|
+
} from './presentation/stores/initializeAuthListener';
|
|
208
|
+
export type {
|
|
209
|
+
AuthState,
|
|
210
|
+
AuthActions,
|
|
211
|
+
UserType,
|
|
212
|
+
AuthListenerOptions,
|
|
213
|
+
} from './types/auth-store.types';
|
|
214
|
+
export type { AuthModalMode } from './presentation/stores/auth.selectors';
|
|
215
|
+
export {
|
|
216
|
+
selectUser,
|
|
217
|
+
selectLoading,
|
|
218
|
+
selectError,
|
|
219
|
+
selectSetLoading,
|
|
220
|
+
selectSetError,
|
|
221
|
+
selectSetIsAnonymous,
|
|
222
|
+
selectShowAuthModal,
|
|
223
|
+
selectUserId,
|
|
224
|
+
selectIsAuthenticated,
|
|
225
|
+
selectHasFirebaseUser,
|
|
226
|
+
selectIsAnonymous,
|
|
227
|
+
selectUserType,
|
|
228
|
+
selectIsAuthReady,
|
|
229
|
+
selectFirebaseUserId,
|
|
230
|
+
selectAuthState,
|
|
231
|
+
} from './presentation/stores/auth.selectors';
|
|
22
232
|
|
|
23
233
|
// =============================================================================
|
|
24
234
|
// SHARED LAYER (New Modular Utilities)
|
|
25
235
|
// =============================================================================
|
|
26
|
-
|
|
236
|
+
// Validation
|
|
237
|
+
export { EmailValidator, PasswordValidator, NameValidator } from './shared/validation/validators';
|
|
238
|
+
export {
|
|
239
|
+
EmailSanitizer,
|
|
240
|
+
PasswordSanitizer,
|
|
241
|
+
NameSanitizer,
|
|
242
|
+
} from './shared/validation/sanitizers';
|
|
243
|
+
export {
|
|
244
|
+
BaseValidationRule,
|
|
245
|
+
RequiredRule,
|
|
246
|
+
RegexRule,
|
|
247
|
+
MinLengthRule,
|
|
248
|
+
} from './shared/validation/rules';
|
|
249
|
+
export type {
|
|
250
|
+
ValidationResult,
|
|
251
|
+
PasswordRequirements,
|
|
252
|
+
PasswordStrengthResult,
|
|
253
|
+
ValidationRule,
|
|
254
|
+
ValidatorConfig,
|
|
255
|
+
} from './shared/validation/types';
|
|
256
|
+
|
|
257
|
+
// Error Handling
|
|
258
|
+
export {
|
|
259
|
+
ErrorMapper,
|
|
260
|
+
DEFAULT_AUTH_ERROR_MAPPINGS,
|
|
261
|
+
FieldErrorMapper,
|
|
262
|
+
} from './shared/error-handling/mappers';
|
|
263
|
+
export { ErrorHandler, FormErrorHandler } from './shared/error-handling/handlers';
|
|
264
|
+
export type {
|
|
265
|
+
FieldError,
|
|
266
|
+
FormFieldErrors,
|
|
267
|
+
ErrorMap,
|
|
268
|
+
ErrorMappingConfig,
|
|
269
|
+
FormErrorHandlerConfig,
|
|
270
|
+
} from './shared/error-handling/types';
|
|
271
|
+
|
|
272
|
+
// Form
|
|
273
|
+
export { useField, useForm } from './shared/form/builders';
|
|
274
|
+
export type {
|
|
275
|
+
UseFieldOptions,
|
|
276
|
+
UseFieldResult,
|
|
277
|
+
UseFormOptions,
|
|
278
|
+
UseFormResult,
|
|
279
|
+
} from './shared/form/builders';
|
|
280
|
+
export {
|
|
281
|
+
isFormValid,
|
|
282
|
+
isFormDirty,
|
|
283
|
+
isFormTouched,
|
|
284
|
+
getFormErrors,
|
|
285
|
+
getFirstFormError,
|
|
286
|
+
countFormErrors,
|
|
287
|
+
getFieldError,
|
|
288
|
+
fieldHasError,
|
|
289
|
+
isFieldTouched,
|
|
290
|
+
resetFormState,
|
|
291
|
+
} from './shared/form/state';
|
|
292
|
+
export {
|
|
293
|
+
sanitizeFormValues,
|
|
294
|
+
extractFields,
|
|
295
|
+
areRequiredFieldsFilled,
|
|
296
|
+
getEmptyRequiredFields,
|
|
297
|
+
createFieldOptions,
|
|
298
|
+
mergeFormErrors,
|
|
299
|
+
clearFieldErrors,
|
|
300
|
+
} from './shared/form/utils/formUtils';
|
|
301
|
+
export {
|
|
302
|
+
createFieldChangeHandler,
|
|
303
|
+
createFieldBlurHandler,
|
|
304
|
+
debounceFieldChange,
|
|
305
|
+
isFieldValueEmpty,
|
|
306
|
+
sanitizeFieldValue,
|
|
307
|
+
formatFieldValue,
|
|
308
|
+
validateFieldValue,
|
|
309
|
+
getFieldDisplayValue,
|
|
310
|
+
truncateFieldValue,
|
|
311
|
+
} from './shared/form/utils/fieldUtils';
|
|
312
|
+
export type {
|
|
313
|
+
FieldState,
|
|
314
|
+
FormState,
|
|
315
|
+
FieldChangeHandler,
|
|
316
|
+
FormFieldConfig,
|
|
317
|
+
FormConfig,
|
|
318
|
+
} from './shared/form/types';
|
|
27
319
|
|
|
28
320
|
// =============================================================================
|
|
29
321
|
// UTILITIES & INIT
|
|
30
322
|
// =============================================================================
|
|
31
|
-
export
|
|
323
|
+
export {
|
|
324
|
+
getAuthErrorLocalizationKey,
|
|
325
|
+
resolveErrorMessage,
|
|
326
|
+
} from './presentation/utils/getAuthErrorMessage';
|
|
327
|
+
export { createAuthInitModule } from './init/createAuthInitModule';
|
|
328
|
+
export type { AuthInitModuleConfig } from './init/createAuthInitModule';
|
|
@@ -54,16 +54,26 @@ export class AnonymousModeService {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
async enable(storageProvider: IStorageProvider): Promise<void> {
|
|
57
|
-
|
|
58
|
-
// This prevents TOCTOU: memory is never set to true unless storage confirms the write.
|
|
59
|
-
const saveSuccess = await this.save(storageProvider, true);
|
|
57
|
+
const previousState = this.isAnonymousMode;
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
try {
|
|
60
|
+
// Save to storage first, then update memory to maintain consistency.
|
|
61
|
+
// This prevents TOCTOU: memory is never set to true unless storage confirms the write.
|
|
62
|
+
const saveSuccess = await this.save(storageProvider, true);
|
|
63
|
+
|
|
64
|
+
if (!saveSuccess) {
|
|
65
|
+
// Rollback to previous state on failure
|
|
66
|
+
this.isAnonymousMode = previousState;
|
|
67
|
+
throw new Error('Failed to save anonymous mode state');
|
|
68
|
+
}
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
this.isAnonymousMode = true;
|
|
71
|
+
emitAnonymousModeEnabled();
|
|
72
|
+
} catch (error) {
|
|
73
|
+
// Ensure state is rolled back on any error
|
|
74
|
+
this.isAnonymousMode = previousState;
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
getIsAnonymousMode(): boolean {
|
|
@@ -38,8 +38,8 @@ export class AuthService {
|
|
|
38
38
|
|
|
39
39
|
if (this.initialized) return;
|
|
40
40
|
|
|
41
|
-
//
|
|
42
|
-
|
|
41
|
+
// Store reference to current promise to prevent race condition
|
|
42
|
+
const initPromise = (async () => {
|
|
43
43
|
this.repository = new AuthRepository(this.config);
|
|
44
44
|
|
|
45
45
|
if (this.storageProvider) {
|
|
@@ -48,10 +48,15 @@ export class AuthService {
|
|
|
48
48
|
this.initialized = true;
|
|
49
49
|
})();
|
|
50
50
|
|
|
51
|
+
this.initializationPromise = initPromise;
|
|
52
|
+
|
|
51
53
|
try {
|
|
52
|
-
await
|
|
54
|
+
await initPromise;
|
|
53
55
|
} finally {
|
|
54
|
-
|
|
56
|
+
// Only null out if it's still the same promise (prevents race condition)
|
|
57
|
+
if (this.initializationPromise === initPromise) {
|
|
58
|
+
this.initializationPromise = null;
|
|
59
|
+
}
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { PasswordConfig } from "
|
|
1
|
+
import type { PasswordConfig } from "@domain/value-objects/AuthConfig";
|
|
2
2
|
import { isEmptyEmail, isEmptyPassword, isEmptyName } from "./validation/validationHelpers";
|
|
3
3
|
import type {
|
|
4
4
|
ValidationResult,
|
|
5
5
|
PasswordStrengthResult,
|
|
6
6
|
PasswordRequirements,
|
|
7
|
-
} from "
|
|
7
|
+
} from "@shared/validation/types";
|
|
8
8
|
|
|
9
9
|
// Export validation types
|
|
10
10
|
export type {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
validatePasswordConfirmation,
|
|
10
10
|
} from "../AuthValidation";
|
|
11
11
|
import type { PasswordConfig } from "../../../domain/value-objects/AuthConfig";
|
|
12
|
-
import type { PasswordRequirements } from "
|
|
12
|
+
import type { PasswordRequirements } from "@shared/validation/types";
|
|
13
13
|
|
|
14
14
|
interface PasswordValidationInput {
|
|
15
15
|
password: string;
|
|
@@ -36,7 +36,7 @@ export function calculatePasswordRequirements(
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const result = validatePasswordForRegister(password, config);
|
|
39
|
-
return result.requirements;
|
|
39
|
+
return result.requirements ?? { hasMinLength: false };
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Anonymous Sign-In Types
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { User } from 'firebase/auth';
|
|
6
|
+
|
|
5
7
|
export interface AnonymousSignInCallbacks {
|
|
6
8
|
onSignInSuccess: () => void;
|
|
7
9
|
onSignInFailure: (error: Error) => void;
|
|
@@ -14,7 +16,7 @@ export interface AnonymousSignInOptions {
|
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export interface AnonymousStore {
|
|
17
|
-
setFirebaseUser: (user:
|
|
19
|
+
setFirebaseUser: (user: User | null) => void;
|
|
18
20
|
setLoading: (loading: boolean) => void;
|
|
19
21
|
setInitialized: (initialized: boolean) => void;
|
|
20
22
|
setError: (error: string | null) => void;
|
|
@@ -123,11 +123,12 @@ export function resetListenerState(): void {
|
|
|
123
123
|
console.error('[ListenerState] Error during unsubscribe:', error);
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
+
// Always set to null even if unsubscribe fails to prevent retry loops
|
|
127
|
+
state.unsubscribe = null;
|
|
126
128
|
}
|
|
127
129
|
state.initialized = false;
|
|
128
130
|
state.refCount = 0;
|
|
129
131
|
state.initializationInProgress = false;
|
|
130
132
|
state.anonymousSignInInProgress = false;
|
|
131
133
|
state.cleanupInProgress = false;
|
|
132
|
-
state.unsubscribe = null;
|
|
133
134
|
}
|
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Validation Types
|
|
3
|
-
*
|
|
2
|
+
* Form Validation Types
|
|
3
|
+
* Infrastructure-specific form validation types
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Single field validation result
|
|
8
|
-
* Used for validating individual fields (email, password, etc.)
|
|
9
|
-
*/
|
|
10
|
-
export interface ValidationResult {
|
|
11
|
-
isValid: boolean;
|
|
12
|
-
error?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
6
|
/**
|
|
16
7
|
* Form validation error
|
|
17
8
|
* Represents an error for a specific form field
|
|
@@ -30,20 +21,3 @@ export interface FormValidationResult {
|
|
|
30
21
|
errors: FormValidationError[];
|
|
31
22
|
}
|
|
32
23
|
|
|
33
|
-
/**
|
|
34
|
-
* Password requirements
|
|
35
|
-
* Tracks which password requirements are met
|
|
36
|
-
*/
|
|
37
|
-
export interface PasswordRequirements {
|
|
38
|
-
hasMinLength: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Password strength validation result
|
|
43
|
-
* Extends ValidationResult with password-specific requirements
|
|
44
|
-
*/
|
|
45
|
-
export interface PasswordStrengthResult {
|
|
46
|
-
isValid: boolean;
|
|
47
|
-
error?: string;
|
|
48
|
-
requirements: PasswordRequirements;
|
|
49
|
-
}
|
|
@@ -3,7 +3,7 @@ import { View, StyleSheet } from "react-native";
|
|
|
3
3
|
import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
|
|
4
4
|
import { AtomicText } from "@umituz/react-native-design-system/atoms";
|
|
5
5
|
import type { ColorVariant } from "@umituz/react-native-design-system/typography";
|
|
6
|
-
import type { PasswordRequirements } from "
|
|
6
|
+
import type { PasswordRequirements } from "@shared/validation/types";
|
|
7
7
|
|
|
8
8
|
export interface PasswordStrengthTranslations {
|
|
9
9
|
minLength: string;
|
|
@@ -61,7 +61,9 @@ export const RegisterForm = memo<RegisterFormProps>(({
|
|
|
61
61
|
|
|
62
62
|
<AtomicButton
|
|
63
63
|
variant="primary"
|
|
64
|
-
onPress={() =>
|
|
64
|
+
onPress={() => {
|
|
65
|
+
handleSignUp().catch(() => {});
|
|
66
|
+
}}
|
|
65
67
|
disabled={loading || !email.trim() || !password || !confirmPassword}
|
|
66
68
|
fullWidth
|
|
67
69
|
style={styles.signUpButton}
|
|
@@ -11,6 +11,7 @@ import { FormPasswordInput } from '../form/FormPasswordInput';
|
|
|
11
11
|
import { PasswordStrengthIndicator } from '../PasswordStrengthIndicator';
|
|
12
12
|
import { PasswordMatchIndicator } from '../PasswordMatchIndicator';
|
|
13
13
|
import type { RegisterFormTranslations } from './types';
|
|
14
|
+
import type { PasswordRequirements } from '@shared/validation/types';
|
|
14
15
|
|
|
15
16
|
export interface RegisterFormFieldsProps {
|
|
16
17
|
displayName: string;
|
|
@@ -19,14 +20,14 @@ export interface RegisterFormFieldsProps {
|
|
|
19
20
|
confirmPassword: string;
|
|
20
21
|
fieldErrors: Record<string, string | null>;
|
|
21
22
|
loading: boolean;
|
|
22
|
-
passwordRequirements:
|
|
23
|
+
passwordRequirements: PasswordRequirements;
|
|
23
24
|
passwordsMatch: boolean;
|
|
24
25
|
translations: RegisterFormTranslations;
|
|
25
26
|
onDisplayNameChange: (value: string) => void;
|
|
26
27
|
onEmailChange: (value: string) => void;
|
|
27
28
|
onPasswordChange: (value: string) => void;
|
|
28
29
|
onConfirmPasswordChange: (value: string) => void;
|
|
29
|
-
onSubmit: () => void
|
|
30
|
+
onSubmit: () => Promise<void>;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export const RegisterFormFields: React.FC<RegisterFormFieldsProps> = ({
|
|
@@ -14,8 +14,8 @@ export function useSocialAuthHandlers(
|
|
|
14
14
|
onAppleSignIn?: () => Promise<void>
|
|
15
15
|
): SocialAuthHandlers {
|
|
16
16
|
// Social Auth Hooks
|
|
17
|
-
const { signInWithGoogle
|
|
18
|
-
const { signInWithApple
|
|
17
|
+
const { signInWithGoogle } = useGoogleAuth(socialConfig?.google);
|
|
18
|
+
const { signInWithApple } = useAppleAuth();
|
|
19
19
|
|
|
20
20
|
// Social auth loading states
|
|
21
21
|
const [googleLoading, setGoogleLoading] = useState(false);
|
|
@@ -84,11 +84,11 @@ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
|
|
|
84
84
|
hideAuthModal();
|
|
85
85
|
onAuthSuccess?.();
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
executeAfterAuth(() => {
|
|
88
88
|
executePendingCallback();
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
return
|
|
91
|
+
return undefined;
|
|
92
92
|
}
|
|
93
93
|
return undefined;
|
|
94
94
|
}
|
|
@@ -59,7 +59,8 @@ export const useAuthHandlers = (appInfo: AuthHandlersAppInfo, translations?: Aut
|
|
|
59
59
|
if (__DEV__) {
|
|
60
60
|
console.error("[useAuthHandlers] Failed to open app store:", error);
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
63
|
+
Alert.alert(translations?.common || "", translations?.failedToOpenAppStore || errorMessage);
|
|
63
64
|
}
|
|
64
65
|
}, [appInfo.appStoreUrl, translations]);
|
|
65
66
|
|
|
@@ -70,9 +71,10 @@ export const useAuthHandlers = (appInfo: AuthHandlersAppInfo, translations?: Aut
|
|
|
70
71
|
if (__DEV__) {
|
|
71
72
|
console.error("[useAuthHandlers] Sign out failed:", error);
|
|
72
73
|
}
|
|
74
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
73
75
|
AlertService.createErrorAlert(
|
|
74
76
|
translations?.common || "",
|
|
75
|
-
translations?.unknown ||
|
|
77
|
+
translations?.unknown || errorMessage
|
|
76
78
|
);
|
|
77
79
|
}
|
|
78
80
|
}, [signOut, translations]);
|