@umituz/react-native-auth 4.3.94 → 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.94",
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,
@@ -11,8 +11,8 @@ export const styles = StyleSheet.create({
11
11
  borderRadius: 2,
12
12
  },
13
13
  scrollContent: {
14
- paddingHorizontal: 24,
15
- paddingBottom: 80,
14
+ // paddingHorizontal ve paddingBottom artık component içinden responsive olarak verilecek
15
+ flexGrow: 1,
16
16
  },
17
17
  closeButton: {
18
18
  position: "absolute",
@@ -23,9 +23,9 @@ export const styles = StyleSheet.create({
23
23
  },
24
24
  header: {
25
25
  alignItems: "center",
26
- marginBottom: 24,
27
- marginTop: 8,
28
- paddingTop: 16,
26
+ // marginBottom artık component içinden responsive olarak verilecek
27
+ // marginTop artık component içinden responsive olarak verilecek
28
+ // paddingTop artık component içinden responsive olarak verilecek
29
29
  },
30
30
  title: {
31
31
  marginBottom: 8,
@@ -1,6 +1,7 @@
1
- import React from "react";
1
+ import React, { useMemo } from "react";
2
2
  import { View, TouchableOpacity, ScrollView } from "react-native";
3
3
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
4
+ import { useResponsive } from "@umituz/react-native-design-system/responsive";
4
5
  import { AtomicText, AtomicIcon, AtomicKeyboardAvoidingView } from "@umituz/react-native-design-system/atoms";
5
6
  import { BottomSheetModal } from "@umituz/react-native-design-system/molecules";
6
7
  import { useAuthBottomSheet, type SocialAuthConfiguration } from "../hooks/useAuthBottomSheet";
@@ -44,6 +45,7 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
44
45
  onAuthSuccess,
45
46
  }) => {
46
47
  const tokens = useAppDesignTokens();
48
+ const responsive = useResponsive();
47
49
 
48
50
  const {
49
51
  modalRef,
@@ -59,6 +61,23 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
59
61
  handleAppleSignIn,
60
62
  } = useAuthBottomSheet({ socialConfig, onGoogleSignIn, onAppleSignIn, onAuthSuccess });
61
63
 
64
+ const scrollContentStyle = useMemo(() => [
65
+ styles.scrollContent,
66
+ {
67
+ paddingHorizontal: responsive.horizontalPadding,
68
+ paddingBottom: responsive.verticalPadding * 3,
69
+ },
70
+ ], [responsive.horizontalPadding, responsive.verticalPadding]);
71
+
72
+ const headerStyle = useMemo(() => [
73
+ styles.header,
74
+ {
75
+ marginBottom: responsive.verticalPadding,
76
+ marginTop: responsive.horizontalPadding,
77
+ paddingTop: responsive.horizontalPadding,
78
+ },
79
+ ], [responsive.verticalPadding, responsive.horizontalPadding]);
80
+
62
81
  return (
63
82
  <BottomSheetModal
64
83
  ref={modalRef}
@@ -80,11 +99,11 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
80
99
  </TouchableOpacity>
81
100
 
82
101
  <ScrollView
83
- contentContainerStyle={styles.scrollContent}
102
+ contentContainerStyle={scrollContentStyle}
84
103
  showsVerticalScrollIndicator={false}
85
104
  keyboardShouldPersistTaps="handled"
86
105
  >
87
- <View style={styles.header}>
106
+ <View style={headerStyle}>
88
107
  <AtomicText type="headlineLarge" color="textPrimary" style={styles.title}>
89
108
  {mode === "login" ? translations.signIn : translations.createAccount}
90
109
  </AtomicText>
@@ -7,6 +7,7 @@
7
7
  import React, { memo } from "react";
8
8
  import { View, StyleSheet } from "react-native";
9
9
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
10
+ import { useResponsive } from "@umituz/react-native-design-system/responsive";
10
11
  import { AtomicText } from "@umituz/react-native-design-system/atoms";
11
12
 
12
13
  interface AuthHeaderProps {
@@ -16,9 +17,10 @@ interface AuthHeaderProps {
16
17
 
17
18
  export const AuthHeader = memo<AuthHeaderProps>(({ title, subtitle }) => {
18
19
  const tokens = useAppDesignTokens();
20
+ const responsive = useResponsive();
19
21
 
20
22
  return (
21
- <View style={[styles.header, { marginBottom: tokens.spacing.xl, paddingHorizontal: tokens.spacing.md }]}>
23
+ <View style={[styles.header, { marginBottom: tokens.spacing.xl, paddingHorizontal: responsive.horizontalPadding }]}>
22
24
  <AtomicText
23
25
  type="headlineLarge"
24
26
  color="textPrimary"
@@ -1,6 +1,7 @@
1
- import React, { useRef, memo } from "react";
1
+ import React, { useRef, memo, useMemo } from "react";
2
2
  import { StyleSheet, TextInput } from "react-native";
3
3
  import { AtomicButton } from "@umituz/react-native-design-system/atoms";
4
+ import { useResponsive } from "@umituz/react-native-design-system/responsive";
4
5
  import { useLoginForm } from "../hooks/useLoginForm";
5
6
  import { AuthErrorDisplay } from "./AuthErrorDisplay";
6
7
  import { AuthLink } from "./AuthLink";
@@ -27,6 +28,7 @@ export const LoginForm = memo<LoginFormProps>(({
27
28
  onNavigateToRegister,
28
29
  }) => {
29
30
  const passwordRef = useRef<React.ElementRef<typeof TextInput>>(null);
31
+ const responsive = useResponsive();
30
32
  const {
31
33
  email,
32
34
  password,
@@ -39,6 +41,11 @@ export const LoginForm = memo<LoginFormProps>(({
39
41
  displayError,
40
42
  } = useLoginForm();
41
43
 
44
+ const signInButtonStyle = useMemo(() => [
45
+ styles.signInButton,
46
+ { marginBottom: responsive.verticalPadding },
47
+ ], [responsive.verticalPadding]);
48
+
42
49
  return (
43
50
  <>
44
51
  <FormEmailInput
@@ -71,7 +78,7 @@ export const LoginForm = memo<LoginFormProps>(({
71
78
  onPress={() => { void handleSignIn(); }}
72
79
  disabled={loading || !email.trim() || !password}
73
80
  fullWidth
74
- style={styles.signInButton}
81
+ style={signInButtonStyle}
75
82
  >
76
83
  {translations.signIn}
77
84
  </AtomicButton>
@@ -87,9 +94,7 @@ export const LoginForm = memo<LoginFormProps>(({
87
94
  });
88
95
 
89
96
  const styles = StyleSheet.create({
90
- signInButton: {
91
- marginBottom: 16,
92
- },
97
+ signInButton: {},
93
98
  });
94
99
 
95
100
  LoginForm.displayName = 'LoginForm';
@@ -7,6 +7,7 @@
7
7
  import React, { useMemo, memo } from "react";
8
8
  import { View, StyleSheet } from "react-native";
9
9
  import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
10
+ import { useResponsive } from "@umituz/react-native-design-system/responsive";
10
11
  import { AtomicText, AtomicButton } from "@umituz/react-native-design-system/atoms";
11
12
  import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
12
13
 
@@ -27,6 +28,7 @@ interface SocialLoginButtonsProps {
27
28
 
28
29
  export const SocialLoginButtons = memo<SocialLoginButtonsProps>(({ translations, enabledProviders, onGooglePress, onApplePress, googleLoading = false, appleLoading = false }) => {
29
30
  const tokens = useAppDesignTokens();
31
+ const responsive = useResponsive();
30
32
 
31
33
  // PERFORMANCE: Memoize provider checks to prevent recalculation on every render
32
34
  const { hasGoogle, hasApple } = useMemo(() => ({
@@ -34,12 +36,17 @@ export const SocialLoginButtons = memo<SocialLoginButtonsProps>(({ translations,
34
36
  hasApple: enabledProviders.includes("apple"),
35
37
  }), [enabledProviders]);
36
38
 
39
+ const containerStyle = useMemo(() => [
40
+ styles.container,
41
+ { marginTop: responsive.verticalPadding },
42
+ ], [responsive.verticalPadding]);
43
+
37
44
  if (!hasGoogle && !hasApple) {
38
45
  return null;
39
46
  }
40
47
 
41
48
  return (
42
- <View style={[styles.container, { marginTop: tokens.spacing.lg }]}>
49
+ <View style={containerStyle}>
43
50
  <View style={styles.dividerContainer}>
44
51
  <View style={[styles.divider, { backgroundColor: tokens.colors.border }]} />
45
52
  <AtomicText type="bodySmall" color="textSecondary" style={styles.dividerText}>
@@ -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,53 +1,122 @@
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";
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";
12
16
  import { AuthHeader } from "../components/AuthHeader";
13
17
  import { LoginForm, type LoginFormTranslations } from "../components/LoginForm";
18
+ import { SocialLoginButtons, type SocialLoginButtonsTranslations } from "../components/SocialLoginButtons";
14
19
 
15
20
  export interface LoginScreenTranslations {
16
21
  title: string;
17
22
  subtitle?: string;
18
23
  form: LoginFormTranslations;
24
+ socialButtons?: SocialLoginButtonsTranslations;
19
25
  }
20
26
 
21
27
  export interface LoginScreenProps {
22
28
  translations: LoginScreenTranslations;
29
+ socialConfig?: SocialAuthConfiguration;
30
+ onGoogleSignIn?: () => Promise<void>;
31
+ onAppleSignIn?: () => Promise<void>;
32
+ renderLogo?: () => React.ReactNode;
23
33
  }
24
34
 
25
- export const LoginScreen = memo<LoginScreenProps>(({ translations }) => {
35
+ export const LoginScreen = memo<LoginScreenProps>(({
36
+ translations,
37
+ socialConfig,
38
+ onGoogleSignIn,
39
+ onAppleSignIn,
40
+ renderLogo,
41
+ }) => {
26
42
  const navigation = useAppNavigation();
27
43
  const tokens = useAppDesignTokens();
44
+ const responsive = useResponsive();
28
45
 
29
46
  // PERFORMANCE: Stable callback reference
30
47
  const handleNavigateToRegister = useCallback(() => {
31
48
  navigation.navigate("Register");
32
49
  }, [navigation]);
33
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
+
34
80
  return (
35
81
  <ScreenLayout
36
82
  scrollable
37
83
  keyboardAvoiding
38
- maxWidth={440}
84
+ maxWidth={responsive.maxContentWidth}
39
85
  contentContainerStyle={{ justifyContent: "center" }}
40
86
  backgroundColor={tokens.colors.backgroundPrimary}
41
87
  >
88
+ {/* Optional Logo/Illustration */}
89
+ {renderLogo && (
90
+ <View style={styles.logoContainer}>{renderLogo()}</View>
91
+ )}
92
+
42
93
  <AuthHeader title={translations.title} subtitle={translations.subtitle} />
94
+
43
95
  <AtomicCard variant="elevated" padding="lg">
44
96
  <LoginForm
45
97
  translations={translations.form}
46
98
  onNavigateToRegister={handleNavigateToRegister}
47
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
+ )}
48
110
  </AtomicCard>
49
111
  </ScreenLayout>
50
112
  );
51
113
  });
52
114
 
53
115
  LoginScreen.displayName = 'LoginScreen';
116
+
117
+ const styles = StyleSheet.create({
118
+ logoContainer: {
119
+ alignItems: "center",
120
+ marginBottom: 24,
121
+ },
122
+ });
@@ -1,21 +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";
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";
12
16
  import { AuthHeader } from "../components/AuthHeader";
13
17
  import { RegisterForm, type RegisterFormTranslations } from "../components/RegisterForm";
18
+ import { SocialLoginButtons, type SocialLoginButtonsTranslations } from "../components/SocialLoginButtons";
14
19
 
15
20
  export interface RegisterScreenTranslations {
16
21
  title: string;
17
22
  subtitle?: string;
18
23
  form: RegisterFormTranslations;
24
+ socialButtons?: SocialLoginButtonsTranslations;
19
25
  }
20
26
 
21
27
  export interface RegisterScreenProps {
@@ -24,26 +30,76 @@ export interface RegisterScreenProps {
24
30
  privacyUrl?: string;
25
31
  onTermsPress?: () => void;
26
32
  onPrivacyPress?: () => void;
33
+ socialConfig?: SocialAuthConfiguration;
34
+ onGoogleSignIn?: () => Promise<void>;
35
+ onAppleSignIn?: () => Promise<void>;
36
+ renderLogo?: () => React.ReactNode;
27
37
  }
28
38
 
29
- 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
+ }) => {
30
50
  const navigation = useAppNavigation();
31
51
  const tokens = useAppDesignTokens();
52
+ const responsive = useResponsive();
32
53
 
33
54
  // PERFORMANCE: Stable callback reference
34
55
  const handleNavigateToLogin = useCallback(() => {
35
56
  navigation.navigate("Login");
36
57
  }, [navigation]);
37
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
+
38
88
  return (
39
89
  <ScreenLayout
40
90
  scrollable
41
91
  keyboardAvoiding
42
- maxWidth={440}
92
+ maxWidth={responsive.maxContentWidth}
43
93
  contentContainerStyle={{ justifyContent: "center" }}
44
94
  backgroundColor={tokens.colors.backgroundPrimary}
45
95
  >
96
+ {/* Optional Logo/Illustration */}
97
+ {renderLogo && (
98
+ <View style={styles.logoContainer}>{renderLogo()}</View>
99
+ )}
100
+
46
101
  <AuthHeader title={translations.title} subtitle={translations.subtitle} />
102
+
47
103
  <AtomicCard variant="elevated" padding="lg">
48
104
  <RegisterForm
49
105
  translations={translations.form}
@@ -53,9 +109,26 @@ export const RegisterScreen = memo<RegisterScreenProps>(({ translations, termsUr
53
109
  onTermsPress={onTermsPress}
54
110
  onPrivacyPress={onPrivacyPress}
55
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
+ )}
56
122
  </AtomicCard>
57
123
  </ScreenLayout>
58
124
  );
59
125
  });
60
126
 
61
127
  RegisterScreen.displayName = 'RegisterScreen';
128
+
129
+ const styles = StyleSheet.create({
130
+ logoContainer: {
131
+ alignItems: "center",
132
+ marginBottom: 24,
133
+ },
134
+ });