@umituz/react-native-auth 4.3.95 → 4.3.96

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-auth",
3
- "version": "4.3.95",
3
+ "version": "4.3.96",
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",
package/src/index.ts CHANGED
@@ -166,9 +166,15 @@ export type { UseLocalErrorResult } from './presentation/hooks/useLocalError';
166
166
  export { AuthProvider } from './presentation/providers/AuthProvider';
167
167
  export type { ErrorFallbackProps } from './presentation/providers/AuthProvider';
168
168
  export { LoginScreen } from './presentation/screens/LoginScreen';
169
- export type { LoginScreenProps } from './presentation/screens/LoginScreen';
169
+ export type {
170
+ LoginScreenProps,
171
+ LoginScreenTranslations,
172
+ } from './presentation/screens/LoginScreen';
170
173
  export { RegisterScreen } from './presentation/screens/RegisterScreen';
171
- export type { RegisterScreenProps } from './presentation/screens/RegisterScreen';
174
+ export type {
175
+ RegisterScreenProps,
176
+ RegisterScreenTranslations,
177
+ } from './presentation/screens/RegisterScreen';
172
178
  export { AccountScreen } from './presentation/screens/AccountScreen';
173
179
  export type {
174
180
  AccountScreenProps,
@@ -183,12 +189,20 @@ export type {
183
189
  PasswordPromptScreenProps,
184
190
  } from './presentation/screens/PasswordPromptScreen';
185
191
  export { AuthNavigator } from './presentation/navigation/AuthNavigator';
186
- export type { AuthStackParamList } from './presentation/navigation/AuthNavigator';
192
+ export type {
193
+ AuthStackParamList,
194
+ AuthNavigatorProps,
195
+ AuthNavigatorTranslations,
196
+ } from './presentation/navigation/AuthNavigator';
187
197
  export { AuthBottomSheet } from './presentation/components/AuthBottomSheet';
188
198
  export type {
189
199
  AuthBottomSheetProps,
190
200
  AuthBottomSheetTranslations,
191
201
  } from './presentation/components/AuthBottomSheet';
202
+ export { SocialLoginButtons } from './presentation/components/SocialLoginButtons';
203
+ export type {
204
+ SocialLoginButtonsTranslations,
205
+ } from './presentation/components/SocialLoginButtons';
192
206
  export { ProfileSection } from './presentation/components/ProfileSection';
193
207
  export type {
194
208
  ProfileSectionProps,
@@ -1,7 +1,8 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from "react";
1
+ import React, { useEffect, useMemo, useState } from "react";
2
2
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
3
3
  import { StackNavigator, type StackNavigatorConfig, type StackScreenProps } from "@umituz/react-native-design-system/molecules";
4
4
  import { storageRepository, unwrap } from "@umituz/react-native-design-system/storage";
5
+ import type { SocialAuthConfiguration } from "../hooks/useAuthBottomSheet";
5
6
  import { LoginScreen, type LoginScreenTranslations } from "../screens/LoginScreen";
6
7
  import { RegisterScreen, type RegisterScreenTranslations } from "../screens/RegisterScreen";
7
8
 
@@ -10,28 +11,28 @@ export type AuthStackParamList = {
10
11
  Register: undefined;
11
12
  };
12
13
 
14
+ // Storage key for persisting initial route preference across navigation
13
15
  const SHOW_REGISTER_KEY = "auth_show_register";
14
16
 
15
- interface AuthNavigatorTranslations {
17
+ export interface AuthNavigatorTranslations {
16
18
  login: LoginScreenTranslations;
17
19
  register: RegisterScreenTranslations;
18
20
  }
19
21
 
20
- interface AuthNavigatorProps {
22
+ export interface AuthNavigatorProps {
21
23
  translations: AuthNavigatorTranslations;
22
24
  termsUrl?: string;
23
25
  privacyUrl?: string;
24
26
  onTermsPress?: () => void;
25
27
  onPrivacyPress?: () => void;
28
+ socialConfig?: SocialAuthConfiguration;
29
+ onGoogleSignIn?: () => Promise<void>;
30
+ onAppleSignIn?: () => Promise<void>;
31
+ renderLogo?: () => React.ReactNode;
26
32
  }
27
33
 
28
- export const AuthNavigator: React.FC<AuthNavigatorProps> = ({
29
- translations,
30
- termsUrl,
31
- privacyUrl,
32
- onTermsPress,
33
- onPrivacyPress,
34
- }) => {
34
+ export const AuthNavigator: React.FC<AuthNavigatorProps> = (props) => {
35
+ const { translations, termsUrl, privacyUrl, onTermsPress, onPrivacyPress, socialConfig, onGoogleSignIn, onAppleSignIn, renderLogo } = props;
35
36
  const tokens = useAppDesignTokens();
36
37
  const [initialRouteName, setInitialRouteName] = useState<
37
38
  "Login" | "Register" | undefined
@@ -77,29 +78,42 @@ export const AuthNavigator: React.FC<AuthNavigatorProps> = ({
77
78
  const loginTranslations = useMemo(() => translations.login, [translations.login]);
78
79
  const registerTranslations = useMemo(() => translations.register, [translations.register]);
79
80
 
80
- const LoginScreenWrapper = useCallback(
81
- (props: StackScreenProps<AuthStackParamList, 'Login'>) => (
81
+ // Memoize social auth props
82
+ const socialAuthProps = useMemo(() => ({
83
+ socialConfig,
84
+ onGoogleSignIn,
85
+ onAppleSignIn,
86
+ renderLogo,
87
+ }), [socialConfig, onGoogleSignIn, onAppleSignIn, renderLogo]);
88
+
89
+ // Create screen components with proper types
90
+ const LoginScreenComponent = useMemo(() => {
91
+ // Use FC with generic props to satisfy StackNavigator type, then cast for internal use
92
+ const LoginScreenWrapper: React.FC<{ navigation: unknown; route: unknown }> = (props) => (
82
93
  <LoginScreen
83
- {...props}
94
+ {...(props as StackScreenProps<AuthStackParamList, 'Login'>)}
84
95
  translations={loginTranslations}
96
+ {...socialAuthProps}
85
97
  />
86
- ),
87
- [loginTranslations]
88
- );
98
+ );
99
+ return React.memo(LoginScreenWrapper);
100
+ }, [loginTranslations, socialAuthProps]);
89
101
 
90
- const RegisterScreenWrapper = useCallback(
91
- (props: StackScreenProps<AuthStackParamList, 'Register'>) => (
102
+ const RegisterScreenComponent = useMemo(() => {
103
+ // Use FC with generic props to satisfy StackNavigator type, then cast for internal use
104
+ const RegisterScreenWrapper: React.FC<{ navigation: unknown; route: unknown }> = (props) => (
92
105
  <RegisterScreen
93
- {...props}
106
+ {...(props as StackScreenProps<AuthStackParamList, 'Register'>)}
94
107
  translations={registerTranslations}
95
108
  termsUrl={termsUrl}
96
109
  privacyUrl={privacyUrl}
97
110
  onTermsPress={onTermsPress}
98
111
  onPrivacyPress={onPrivacyPress}
112
+ {...socialAuthProps}
99
113
  />
100
- ),
101
- [registerTranslations, termsUrl, privacyUrl, onTermsPress, onPrivacyPress]
102
- );
114
+ );
115
+ return React.memo(RegisterScreenWrapper);
116
+ }, [registerTranslations, termsUrl, privacyUrl, onTermsPress, onPrivacyPress, socialAuthProps]);
103
117
 
104
118
  if (initialRouteName === undefined) {
105
119
  return null;
@@ -112,8 +126,8 @@ export const AuthNavigator: React.FC<AuthNavigatorProps> = ({
112
126
  cardStyle: { backgroundColor: tokens.colors.backgroundPrimary },
113
127
  },
114
128
  screens: [
115
- { name: "Login", component: LoginScreenWrapper as React.ComponentType<any> },
116
- { name: "Register", component: RegisterScreenWrapper as React.ComponentType<any> },
129
+ { name: "Login", component: LoginScreenComponent },
130
+ { name: "Register", component: RegisterScreenComponent },
117
131
  ],
118
132
  };
119
133
 
@@ -1,29 +1,44 @@
1
1
  /**
2
2
  * Login Screen Component
3
- * Login form screen with navigation
3
+ * Login form screen with navigation and social auth support
4
4
  * PERFORMANCE: Memoized to prevent unnecessary re-renders
5
5
  */
6
6
 
7
- import React, { memo, useCallback } from "react";
7
+ import React, { memo, useCallback, useMemo } from "react";
8
+ import { View, StyleSheet } from "react-native";
8
9
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
9
10
  import { AtomicCard } from "@umituz/react-native-design-system/atoms";
10
11
  import { useAppNavigation } from "@umituz/react-native-design-system/molecules";
11
12
  import { ScreenLayout } from "@umituz/react-native-design-system/layouts";
12
13
  import { useResponsive } from "@umituz/react-native-design-system/responsive";
14
+ import type { SocialAuthConfiguration } from "../hooks/useAuthBottomSheet";
15
+ import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
13
16
  import { AuthHeader } from "../components/AuthHeader";
14
17
  import { LoginForm, type LoginFormTranslations } from "../components/LoginForm";
18
+ import { SocialLoginButtons, type SocialLoginButtonsTranslations } from "../components/SocialLoginButtons";
15
19
 
16
20
  export interface LoginScreenTranslations {
17
21
  title: string;
18
22
  subtitle?: string;
19
23
  form: LoginFormTranslations;
24
+ socialButtons?: SocialLoginButtonsTranslations;
20
25
  }
21
26
 
22
27
  export interface LoginScreenProps {
23
28
  translations: LoginScreenTranslations;
29
+ socialConfig?: SocialAuthConfiguration;
30
+ onGoogleSignIn?: () => Promise<void>;
31
+ onAppleSignIn?: () => Promise<void>;
32
+ renderLogo?: () => React.ReactNode;
24
33
  }
25
34
 
26
- export const LoginScreen = memo<LoginScreenProps>(({ translations }) => {
35
+ export const LoginScreen = memo<LoginScreenProps>(({
36
+ translations,
37
+ socialConfig,
38
+ onGoogleSignIn,
39
+ onAppleSignIn,
40
+ renderLogo,
41
+ }) => {
27
42
  const navigation = useAppNavigation();
28
43
  const tokens = useAppDesignTokens();
29
44
  const responsive = useResponsive();
@@ -33,6 +48,35 @@ export const LoginScreen = memo<LoginScreenProps>(({ translations }) => {
33
48
  navigation.navigate("Register");
34
49
  }, [navigation]);
35
50
 
51
+ // Determine enabled social providers
52
+ const enabledProviders = useMemo<SocialAuthProvider[]>(() => {
53
+ if (!socialConfig) return [];
54
+ const providers: SocialAuthProvider[] = [];
55
+ // For Google, check if config exists
56
+ if (socialConfig.google) providers.push("google");
57
+ // For Apple, check if enabled flag is true
58
+ if (socialConfig.apple?.enabled) providers.push("apple");
59
+ return providers;
60
+ }, [socialConfig]);
61
+
62
+ const hasSocialAuth = enabledProviders.length > 0 && translations.socialButtons;
63
+ // Check if required handlers exist for enabled providers (only need handler if provider is enabled)
64
+ const hasGoogle = enabledProviders.includes("google") && onGoogleSignIn;
65
+ const hasApple = enabledProviders.includes("apple") && onAppleSignIn;
66
+ const showSocialButtons = hasSocialAuth && (hasGoogle || hasApple);
67
+
68
+ // Store social buttons translations in const to satisfy type checker (safe because we check hasSocialAuth first)
69
+ const socialButtonsTranslations = translations.socialButtons!;
70
+
71
+ // PERFORMANCE: Stable callbacks for social sign-in (wrap async handlers)
72
+ const handleGooglePress = useCallback(() => {
73
+ void onGoogleSignIn?.();
74
+ }, [onGoogleSignIn]);
75
+
76
+ const handleApplePress = useCallback(() => {
77
+ void onAppleSignIn?.();
78
+ }, [onAppleSignIn]);
79
+
36
80
  return (
37
81
  <ScreenLayout
38
82
  scrollable
@@ -41,15 +85,38 @@ export const LoginScreen = memo<LoginScreenProps>(({ translations }) => {
41
85
  contentContainerStyle={{ justifyContent: "center" }}
42
86
  backgroundColor={tokens.colors.backgroundPrimary}
43
87
  >
88
+ {/* Optional Logo/Illustration */}
89
+ {renderLogo && (
90
+ <View style={styles.logoContainer}>{renderLogo()}</View>
91
+ )}
92
+
44
93
  <AuthHeader title={translations.title} subtitle={translations.subtitle} />
94
+
45
95
  <AtomicCard variant="elevated" padding="lg">
46
96
  <LoginForm
47
97
  translations={translations.form}
48
98
  onNavigateToRegister={handleNavigateToRegister}
49
99
  />
100
+
101
+ {/* Social Login Buttons */}
102
+ {showSocialButtons && (
103
+ <SocialLoginButtons
104
+ translations={socialButtonsTranslations}
105
+ enabledProviders={enabledProviders}
106
+ onGooglePress={hasGoogle ? handleGooglePress : undefined}
107
+ onApplePress={hasApple ? handleApplePress : undefined}
108
+ />
109
+ )}
50
110
  </AtomicCard>
51
111
  </ScreenLayout>
52
112
  );
53
113
  });
54
114
 
55
115
  LoginScreen.displayName = 'LoginScreen';
116
+
117
+ const styles = StyleSheet.create({
118
+ logoContainer: {
119
+ alignItems: "center",
120
+ marginBottom: 24,
121
+ },
122
+ });
@@ -1,22 +1,27 @@
1
1
  /**
2
2
  * Register Screen Component
3
- * Registration form screen with navigation
3
+ * Registration form screen with navigation and social auth support
4
4
  * PERFORMANCE: Memoized to prevent unnecessary re-renders
5
5
  */
6
6
 
7
- import React, { memo, useCallback } from "react";
7
+ import React, { memo, useCallback, useMemo } from "react";
8
+ import { View, StyleSheet } from "react-native";
8
9
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
9
10
  import { AtomicCard } from "@umituz/react-native-design-system/atoms";
10
11
  import { useAppNavigation } from "@umituz/react-native-design-system/molecules";
11
12
  import { ScreenLayout } from "@umituz/react-native-design-system/layouts";
12
13
  import { useResponsive } from "@umituz/react-native-design-system/responsive";
14
+ import type { SocialAuthConfiguration } from "../hooks/useAuthBottomSheet";
15
+ import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
13
16
  import { AuthHeader } from "../components/AuthHeader";
14
17
  import { RegisterForm, type RegisterFormTranslations } from "../components/RegisterForm";
18
+ import { SocialLoginButtons, type SocialLoginButtonsTranslations } from "../components/SocialLoginButtons";
15
19
 
16
20
  export interface RegisterScreenTranslations {
17
21
  title: string;
18
22
  subtitle?: string;
19
23
  form: RegisterFormTranslations;
24
+ socialButtons?: SocialLoginButtonsTranslations;
20
25
  }
21
26
 
22
27
  export interface RegisterScreenProps {
@@ -25,9 +30,23 @@ export interface RegisterScreenProps {
25
30
  privacyUrl?: string;
26
31
  onTermsPress?: () => void;
27
32
  onPrivacyPress?: () => void;
33
+ socialConfig?: SocialAuthConfiguration;
34
+ onGoogleSignIn?: () => Promise<void>;
35
+ onAppleSignIn?: () => Promise<void>;
36
+ renderLogo?: () => React.ReactNode;
28
37
  }
29
38
 
30
- export const RegisterScreen = memo<RegisterScreenProps>(({ translations, termsUrl, privacyUrl, onTermsPress, onPrivacyPress }) => {
39
+ export const RegisterScreen = memo<RegisterScreenProps>(({
40
+ translations,
41
+ termsUrl,
42
+ privacyUrl,
43
+ onTermsPress,
44
+ onPrivacyPress,
45
+ socialConfig,
46
+ onGoogleSignIn,
47
+ onAppleSignIn,
48
+ renderLogo,
49
+ }) => {
31
50
  const navigation = useAppNavigation();
32
51
  const tokens = useAppDesignTokens();
33
52
  const responsive = useResponsive();
@@ -37,6 +56,35 @@ export const RegisterScreen = memo<RegisterScreenProps>(({ translations, termsUr
37
56
  navigation.navigate("Login");
38
57
  }, [navigation]);
39
58
 
59
+ // Determine enabled social providers
60
+ const enabledProviders = useMemo<SocialAuthProvider[]>(() => {
61
+ if (!socialConfig) return [];
62
+ const providers: SocialAuthProvider[] = [];
63
+ // For Google, check if config exists
64
+ if (socialConfig.google) providers.push("google");
65
+ // For Apple, check if enabled flag is true
66
+ if (socialConfig.apple?.enabled) providers.push("apple");
67
+ return providers;
68
+ }, [socialConfig]);
69
+
70
+ const hasSocialAuth = enabledProviders.length > 0 && translations.socialButtons;
71
+ // Check if required handlers exist for enabled providers (only need handler if provider is enabled)
72
+ const hasGoogle = enabledProviders.includes("google") && onGoogleSignIn;
73
+ const hasApple = enabledProviders.includes("apple") && onAppleSignIn;
74
+ const showSocialButtons = hasSocialAuth && (hasGoogle || hasApple);
75
+
76
+ // Store social buttons translations in const to satisfy type checker (safe because we check hasSocialAuth first)
77
+ const socialButtonsTranslations = translations.socialButtons!;
78
+
79
+ // PERFORMANCE: Stable callbacks for social sign-in (wrap async handlers)
80
+ const handleGooglePress = useCallback(() => {
81
+ void onGoogleSignIn?.();
82
+ }, [onGoogleSignIn]);
83
+
84
+ const handleApplePress = useCallback(() => {
85
+ void onAppleSignIn?.();
86
+ }, [onAppleSignIn]);
87
+
40
88
  return (
41
89
  <ScreenLayout
42
90
  scrollable
@@ -45,7 +93,13 @@ export const RegisterScreen = memo<RegisterScreenProps>(({ translations, termsUr
45
93
  contentContainerStyle={{ justifyContent: "center" }}
46
94
  backgroundColor={tokens.colors.backgroundPrimary}
47
95
  >
96
+ {/* Optional Logo/Illustration */}
97
+ {renderLogo && (
98
+ <View style={styles.logoContainer}>{renderLogo()}</View>
99
+ )}
100
+
48
101
  <AuthHeader title={translations.title} subtitle={translations.subtitle} />
102
+
49
103
  <AtomicCard variant="elevated" padding="lg">
50
104
  <RegisterForm
51
105
  translations={translations.form}
@@ -55,9 +109,26 @@ export const RegisterScreen = memo<RegisterScreenProps>(({ translations, termsUr
55
109
  onTermsPress={onTermsPress}
56
110
  onPrivacyPress={onPrivacyPress}
57
111
  />
112
+
113
+ {/* Social Login Buttons */}
114
+ {showSocialButtons && (
115
+ <SocialLoginButtons
116
+ translations={socialButtonsTranslations}
117
+ enabledProviders={enabledProviders}
118
+ onGooglePress={hasGoogle ? handleGooglePress : undefined}
119
+ onApplePress={hasApple ? handleApplePress : undefined}
120
+ />
121
+ )}
58
122
  </AtomicCard>
59
123
  </ScreenLayout>
60
124
  );
61
125
  });
62
126
 
63
127
  RegisterScreen.displayName = 'RegisterScreen';
128
+
129
+ const styles = StyleSheet.create({
130
+ logoContainer: {
131
+ alignItems: "center",
132
+ marginBottom: 24,
133
+ },
134
+ });