@umituz/react-native-auth 4.3.69 → 4.3.72

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/index.ts +1 -11
  3. package/src/infrastructure/services/AnonymousModeService.ts +9 -3
  4. package/src/infrastructure/services/AuthEventService.ts +3 -1
  5. package/src/infrastructure/services/AuthService.ts +3 -1
  6. package/src/infrastructure/utils/authStateHandler.ts +3 -1
  7. package/src/infrastructure/utils/calculators/index.ts +0 -6
  8. package/src/infrastructure/utils/calculators/passwordStrengthCalculator.ts +6 -34
  9. package/src/infrastructure/utils/calculators/userProfileCalculator.ts +0 -29
  10. package/src/infrastructure/utils/listener/authListenerStateHandler.ts +3 -1
  11. package/src/infrastructure/utils/listener/listenerState.util.ts +3 -1
  12. package/src/infrastructure/utils/listener/setupListener.ts +10 -4
  13. package/src/infrastructure/utils/safeCallback.ts +12 -4
  14. package/src/infrastructure/utils/validation/types.ts +7 -11
  15. package/src/init/createAuthInitModule.ts +9 -3
  16. package/src/presentation/components/AccountActions.tsx +19 -8
  17. package/src/presentation/components/AuthErrorDisplay.tsx +6 -5
  18. package/src/presentation/components/AuthHeader.tsx +11 -3
  19. package/src/presentation/components/AuthLink.tsx +7 -12
  20. package/src/presentation/components/PasswordMatchIndicator.tsx +5 -6
  21. package/src/presentation/components/PasswordStrengthIndicator.tsx +14 -17
  22. package/src/presentation/components/SocialLoginButtons.tsx +17 -12
  23. package/src/presentation/hooks/README.md +0 -57
  24. package/src/presentation/hooks/useAccountManagement.md +0 -1
  25. package/src/presentation/hooks/useLoginForm.ts +10 -1
  26. package/src/presentation/hooks/usePasswordPromptNavigation.ts +3 -1
  27. package/src/presentation/providers/AuthProvider.tsx +3 -1
  28. package/src/presentation/screens/AccountScreen.tsx +6 -3
  29. package/src/presentation/screens/LoginScreen.tsx +14 -5
  30. package/src/presentation/screens/RegisterScreen.tsx +14 -11
  31. package/src/presentation/stores/authModalStore.ts +6 -2
  32. package/src/presentation/utils/passwordPromptCallback.ts +3 -1
  33. package/src/presentation/hooks/useProfileEdit.ts +0 -83
  34. package/src/presentation/hooks/useSocialLogin.md +0 -381
  35. package/src/presentation/hooks/useSocialLogin.ts +0 -95
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useMemo, memo } from "react";
2
2
  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";
@@ -22,12 +22,7 @@ interface RequirementDotProps {
22
22
  pendingColor: ColorVariant;
23
23
  }
24
24
 
25
- const RequirementDot: React.FC<RequirementDotProps> = ({
26
- label,
27
- isValid,
28
- successColor,
29
- pendingColor,
30
- }) => {
25
+ const RequirementDot = memo<RequirementDotProps>(({ label, isValid, successColor, pendingColor }) => {
31
26
  const tokens = useAppDesignTokens();
32
27
  const colorKey = isValid ? successColor : pendingColor;
33
28
  const dotColor = (tokens.colors as Record<string, string>)[colorKey] || tokens.colors.textTertiary;
@@ -40,20 +35,20 @@ const RequirementDot: React.FC<RequirementDotProps> = ({
40
35
  </AtomicText>
41
36
  </View>
42
37
  );
43
- };
38
+ });
39
+
40
+ RequirementDot.displayName = 'RequirementDot';
44
41
 
45
- export const PasswordStrengthIndicator: React.FC<PasswordStrengthIndicatorProps> = ({
46
- translations,
47
- requirements,
48
- showLabels = true,
49
- }) => {
42
+ export const PasswordStrengthIndicator = memo<PasswordStrengthIndicatorProps>(({ translations, requirements, showLabels = true }) => {
50
43
  const tokens = useAppDesignTokens();
51
44
  const successColor: ColorVariant = "success";
52
45
  const pendingColor: ColorVariant = "textTertiary";
53
46
 
54
- const items = [
55
- { key: "minLength", label: translations.minLength, isValid: requirements.hasMinLength },
56
- ];
47
+ // PERFORMANCE: Memoize items array to prevent recreation on every render
48
+ const items = useMemo(
49
+ () => [{ key: "minLength" as const, label: translations.minLength, isValid: requirements.hasMinLength }],
50
+ [translations.minLength, requirements.hasMinLength]
51
+ );
57
52
 
58
53
  if (!showLabels) {
59
54
  return (
@@ -86,7 +81,9 @@ export const PasswordStrengthIndicator: React.FC<PasswordStrengthIndicatorProps>
86
81
  ))}
87
82
  </View>
88
83
  );
89
- };
84
+ });
85
+
86
+ PasswordStrengthIndicator.displayName = 'PasswordStrengthIndicator';
90
87
 
91
88
  const styles = StyleSheet.create({
92
89
  container: {
@@ -1,4 +1,10 @@
1
- import React from "react";
1
+ /**
2
+ * Social Login Buttons Component
3
+ * Google and Apple sign-in buttons
4
+ * PERFORMANCE: Memoized and provider checks memoized to prevent re-renders
5
+ */
6
+
7
+ import React, { useMemo, memo } from "react";
2
8
  import { View, StyleSheet } from "react-native";
3
9
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
4
10
  import { AtomicText, AtomicButton } from "@umituz/react-native-design-system/atoms";
@@ -19,17 +25,14 @@ interface SocialLoginButtonsProps {
19
25
  appleLoading?: boolean;
20
26
  }
21
27
 
22
- export const SocialLoginButtons: React.FC<SocialLoginButtonsProps> = ({
23
- translations,
24
- enabledProviders,
25
- onGooglePress,
26
- onApplePress,
27
- googleLoading = false,
28
- appleLoading = false,
29
- }) => {
28
+ export const SocialLoginButtons = memo<SocialLoginButtonsProps>(({ translations, enabledProviders, onGooglePress, onApplePress, googleLoading = false, appleLoading = false }) => {
30
29
  const tokens = useAppDesignTokens();
31
- const hasGoogle = enabledProviders.includes("google");
32
- const hasApple = enabledProviders.includes("apple");
30
+
31
+ // PERFORMANCE: Memoize provider checks to prevent recalculation on every render
32
+ const { hasGoogle, hasApple } = useMemo(() => ({
33
+ hasGoogle: enabledProviders.includes("google"),
34
+ hasApple: enabledProviders.includes("apple"),
35
+ }), [enabledProviders]);
33
36
 
34
37
  if (!hasGoogle && !hasApple) {
35
38
  return null;
@@ -74,7 +77,9 @@ export const SocialLoginButtons: React.FC<SocialLoginButtonsProps> = ({
74
77
  </View>
75
78
  </View>
76
79
  );
77
- };
80
+ });
81
+
82
+ SocialLoginButtons.displayName = 'SocialLoginButtons';
78
83
 
79
84
  const styles = StyleSheet.create({
80
85
  container: {},
@@ -125,32 +125,6 @@ import { useUserProfile } from '@umituz/react-native-auth';
125
125
 
126
126
  **Documentation**: `useUserProfile.md`
127
127
 
128
- ---
129
-
130
-
131
- **Purpose**: Profile update operations and form management
132
-
133
- **When to Use**:
134
- - Profile editing screens
135
- - Settings screens
136
- - Form state management
137
- - Profile modifications
138
-
139
- **Import Path**:
140
- ```typescript
141
- import {
142
- useProfileEdit
143
- } from '@umituz/react-native-auth';
144
- ```
145
-
146
-
147
- **Rules**:
148
- - MUST validate before update
149
- - MUST handle loading state
150
- - MUST show errors to user
151
- - MUST not allow anonymous updates
152
-
153
-
154
128
  ---
155
129
 
156
130
  ### useAccountManagement
@@ -180,37 +154,6 @@ import { useAccountManagement } from '@umituz/react-native-auth';
180
154
 
181
155
  ---
182
156
 
183
- ### useSocialLogin
184
-
185
- **Purpose**: Google and Apple authentication
186
-
187
- **When to Use**:
188
- - Social authentication needed
189
- - Want Google sign-in
190
- - Want Apple sign-in (iOS)
191
- - Unified social auth interface
192
-
193
- **Import Path**:
194
- ```typescript
195
- import {
196
- useSocialLogin,
197
- useGoogleAuth,
198
- useAppleAuth
199
- } from '@umituz/react-native-auth';
200
- ```
201
-
202
- **File**: `useSocialLogin.ts`
203
-
204
- **Rules**:
205
- - MUST configure providers before use
206
- - MUST check provider availability
207
- - MUST handle loading states
208
- - MUST handle platform differences
209
-
210
- **Documentation**: `useSocialLogin.md`
211
-
212
- ---
213
-
214
157
  ### useAuthBottomSheet
215
158
 
216
159
  **Purpose**: Auth modal management
@@ -330,7 +330,6 @@ import { useAccountManagement } from '@umituz/react-native-auth';
330
330
 
331
331
  - **`useAuth`** (`src/presentation/hooks/useAuth.ts`) - Authentication state
332
332
  - **`useUserProfile`** (`src/presentation/hooks/useUserProfile.ts`) - Profile data
333
- - **`useProfileEdit`** (`src/presentation/hooks/useProfileEdit.ts`) - Profile editing form
334
333
 
335
334
  ## Related Components
336
335
 
@@ -98,7 +98,16 @@ export function useLoginForm(config?: UseLoginFormConfig): UseLoginFormResult {
98
98
  } catch (err: unknown) {
99
99
  setLocalError(handleAuthError(err));
100
100
  }
101
- }, [fields, signIn, translations, handleAuthError, getErrorMessage, clearFieldErrorsState, setLocalError]);
101
+ }, [
102
+ fields.email,
103
+ fields.password,
104
+ signIn,
105
+ translations,
106
+ handleAuthError,
107
+ getErrorMessage,
108
+ clearFieldErrorsState,
109
+ setLocalError,
110
+ ]);
102
111
 
103
112
  const handleContinueAnonymously = useCallback(async () => {
104
113
  try {
@@ -39,7 +39,9 @@ export const usePasswordPromptNavigation = (
39
39
  })
40
40
  );
41
41
  } catch (error) {
42
- console.error('[usePasswordPromptNavigation] Navigation failed:', error);
42
+ if (__DEV__) {
43
+ console.error('[usePasswordPromptNavigation] Navigation failed:', error);
44
+ }
43
45
  setPasswordPromptCallback(null);
44
46
  resolve(null);
45
47
  }
@@ -74,7 +74,9 @@ export function AuthProvider({ children, ErrorFallback = DefaultErrorFallback }:
74
74
  try {
75
75
  unsubscribe();
76
76
  } catch (error) {
77
- console.error('[AuthProvider] Cleanup failed:', error instanceof Error ? error.message : String(error));
77
+ if (__DEV__) {
78
+ console.error('[AuthProvider] Cleanup failed:', error instanceof Error ? error.message : String(error));
79
+ }
78
80
  }
79
81
  }
80
82
  };
@@ -2,9 +2,10 @@
2
2
  * Account Screen
3
3
  * Pure UI component for account management
4
4
  * Business logic provided via props from app layer
5
+ * PERFORMANCE: Memoized to prevent unnecessary re-renders
5
6
  */
6
7
 
7
- import React from "react";
8
+ import React, { memo } from "react";
8
9
  import { View, TouchableOpacity, StyleSheet } from "react-native";
9
10
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
10
11
  import { AtomicIcon, AtomicText } from "@umituz/react-native-design-system/atoms";
@@ -29,7 +30,7 @@ export interface AccountScreenProps {
29
30
  config: AccountScreenConfig;
30
31
  }
31
32
 
32
- export const AccountScreen: React.FC<AccountScreenProps> = ({ config }) => {
33
+ export const AccountScreen = memo<AccountScreenProps>(({ config }) => {
33
34
  const tokens = useAppDesignTokens();
34
35
 
35
36
  return (
@@ -74,7 +75,9 @@ export const AccountScreen: React.FC<AccountScreenProps> = ({ config }) => {
74
75
  {config.PasswordPromptComponent}
75
76
  </>
76
77
  );
77
- };
78
+ });
79
+
80
+ AccountScreen.displayName = 'AccountScreen';
78
81
 
79
82
  const styles = StyleSheet.create({
80
83
  content: {
@@ -1,4 +1,10 @@
1
- import React from "react";
1
+ /**
2
+ * Login Screen Component
3
+ * Login form screen with navigation
4
+ * PERFORMANCE: Memoized to prevent unnecessary re-renders
5
+ */
6
+
7
+ import React, { memo, useCallback } from "react";
2
8
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
3
9
  import { AtomicCard } from "@umituz/react-native-design-system/atoms";
4
10
  import { useAppNavigation } from "@umituz/react-native-design-system/molecules";
@@ -16,13 +22,14 @@ export interface LoginScreenProps {
16
22
  translations: LoginScreenTranslations;
17
23
  }
18
24
 
19
- export const LoginScreen: React.FC<LoginScreenProps> = ({ translations }) => {
25
+ export const LoginScreen = memo<LoginScreenProps>(({ translations }) => {
20
26
  const navigation = useAppNavigation();
21
27
  const tokens = useAppDesignTokens();
22
28
 
23
- const handleNavigateToRegister = () => {
29
+ // PERFORMANCE: Stable callback reference
30
+ const handleNavigateToRegister = useCallback(() => {
24
31
  navigation.navigate("Register");
25
- };
32
+ }, [navigation]);
26
33
 
27
34
  return (
28
35
  <ScreenLayout
@@ -41,4 +48,6 @@ export const LoginScreen: React.FC<LoginScreenProps> = ({ translations }) => {
41
48
  </AtomicCard>
42
49
  </ScreenLayout>
43
50
  );
44
- };
51
+ });
52
+
53
+ LoginScreen.displayName = 'LoginScreen';
@@ -1,4 +1,10 @@
1
- import React from "react";
1
+ /**
2
+ * Register Screen Component
3
+ * Registration form screen with navigation
4
+ * PERFORMANCE: Memoized to prevent unnecessary re-renders
5
+ */
6
+
7
+ import React, { memo, useCallback } from "react";
2
8
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
3
9
  import { AtomicCard } from "@umituz/react-native-design-system/atoms";
4
10
  import { useAppNavigation } from "@umituz/react-native-design-system/molecules";
@@ -20,19 +26,14 @@ export interface RegisterScreenProps {
20
26
  onPrivacyPress?: () => void;
21
27
  }
22
28
 
23
- export const RegisterScreen: React.FC<RegisterScreenProps> = ({
24
- translations,
25
- termsUrl,
26
- privacyUrl,
27
- onTermsPress,
28
- onPrivacyPress,
29
- }) => {
29
+ export const RegisterScreen = memo<RegisterScreenProps>(({ translations, termsUrl, privacyUrl, onTermsPress, onPrivacyPress }) => {
30
30
  const navigation = useAppNavigation();
31
31
  const tokens = useAppDesignTokens();
32
32
 
33
- const handleNavigateToLogin = () => {
33
+ // PERFORMANCE: Stable callback reference
34
+ const handleNavigateToLogin = useCallback(() => {
34
35
  navigation.navigate("Login");
35
- };
36
+ }, [navigation]);
36
37
 
37
38
  return (
38
39
  <ScreenLayout
@@ -55,4 +56,6 @@ export const RegisterScreen: React.FC<RegisterScreenProps> = ({
55
56
  </AtomicCard>
56
57
  </ScreenLayout>
57
58
  );
58
- };
59
+ });
60
+
61
+ RegisterScreen.displayName = 'RegisterScreen';
@@ -78,11 +78,15 @@ export const useAuthModalStore = createStore<AuthModalState, AuthModalActions>({
78
78
  // If it's a promise, catch rejections
79
79
  if (result && typeof result.then === 'function') {
80
80
  result.catch((error) => {
81
- console.error('[AuthModalStore] Pending callback error:', error);
81
+ if (__DEV__) {
82
+ console.error('[AuthModalStore] Pending callback error:', error);
83
+ }
82
84
  });
83
85
  }
84
86
  } catch (error) {
85
- console.error('[AuthModalStore] Pending callback error:', error);
87
+ if (__DEV__) {
88
+ console.error('[AuthModalStore] Pending callback error:', error);
89
+ }
86
90
  }
87
91
  set({ pendingCallback: null });
88
92
  }
@@ -3,7 +3,9 @@ let callback: ((value: string | null) => void) | null = null;
3
3
  export const setPasswordPromptCallback = (cb: ((value: string | null) => void) | null): void => {
4
4
  // Warn if overriding an existing callback (indicates potential bug)
5
5
  if (callback && cb) {
6
- console.warn('[passwordPromptCallback] Overriding existing callback - this may indicate a bug');
6
+ if (__DEV__) {
7
+ console.warn('[passwordPromptCallback] Overriding existing callback - this may indicate a bug');
8
+ }
7
9
  }
8
10
  callback = cb;
9
11
  };
@@ -1,83 +0,0 @@
1
- /**
2
- * Profile Edit Hook
3
- * Simple profile editing with form state management
4
- * Apps provide image picker and backend update logic
5
- */
6
-
7
- import { useState, useCallback } from "react";
8
- import { validateProfileForm } from "../utils/form/validation/formValidators";
9
-
10
- export interface ProfileEditFormState {
11
- displayName: string;
12
- email: string;
13
- photoURL: string | null;
14
- isModified: boolean;
15
- }
16
-
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[] };
24
- }
25
-
26
- export const useProfileEdit = (
27
- initialState: Partial<ProfileEditFormState> = {},
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
- }, []);
47
-
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
- }, []);
56
-
57
- const validateForm = useCallback((): {
58
- isValid: boolean;
59
- errors: string[];
60
- } => {
61
- const result = validateProfileForm(
62
- {
63
- displayName: formState.displayName,
64
- email: formState.email,
65
- },
66
- key => key // Identity function for translation
67
- );
68
-
69
- return {
70
- isValid: result.isValid,
71
- errors: result.errors.map((e) => e.message),
72
- };
73
- }, [formState]);
74
-
75
- return {
76
- formState,
77
- setDisplayName,
78
- setEmail,
79
- setPhotoURL,
80
- resetForm,
81
- validateForm,
82
- };
83
- };