@umituz/react-native-auth 2.5.9 → 2.6.0
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/application/ports/IAuthProvider.ts +26 -0
- package/src/domain/utils/guestNameGenerator.ts +79 -0
- package/src/domain/value-objects/AuthConfig.ts +37 -0
- package/src/index.ts +21 -2
- package/src/presentation/components/AuthBottomSheet.tsx +45 -1
- package/src/presentation/components/SocialLoginButtons.tsx +184 -0
- package/src/presentation/hooks/useUserProfile.ts +6 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
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",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { AuthUser } from "../../domain/entities/AuthUser";
|
|
7
|
+
import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
|
|
7
8
|
|
|
8
9
|
export interface AuthCredentials {
|
|
9
10
|
email: string;
|
|
@@ -14,6 +15,14 @@ export interface SignUpCredentials extends AuthCredentials {
|
|
|
14
15
|
displayName?: string;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Result from social sign-in operations
|
|
20
|
+
*/
|
|
21
|
+
export interface SocialSignInResult {
|
|
22
|
+
user: AuthUser;
|
|
23
|
+
isNewUser: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
export interface IAuthProvider {
|
|
18
27
|
/**
|
|
19
28
|
* Initialize the auth provider
|
|
@@ -35,6 +44,23 @@ export interface IAuthProvider {
|
|
|
35
44
|
*/
|
|
36
45
|
signUp(credentials: SignUpCredentials): Promise<AuthUser>;
|
|
37
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Sign in with Google
|
|
49
|
+
* @returns User and whether this is a new user
|
|
50
|
+
*/
|
|
51
|
+
signInWithGoogle?(): Promise<SocialSignInResult>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Sign in with Apple
|
|
55
|
+
* @returns User and whether this is a new user
|
|
56
|
+
*/
|
|
57
|
+
signInWithApple?(): Promise<SocialSignInResult>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check if a social provider is supported
|
|
61
|
+
*/
|
|
62
|
+
isSocialProviderSupported?(provider: SocialAuthProvider): boolean;
|
|
63
|
+
|
|
38
64
|
/**
|
|
39
65
|
* Sign out current user
|
|
40
66
|
*/
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anonymous User Name Generator
|
|
3
|
+
* Generates friendly, random names for anonymous users
|
|
4
|
+
* Fully generic - no app-specific content
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const DEFAULT_NAMES = [
|
|
8
|
+
'Alex',
|
|
9
|
+
'Sam',
|
|
10
|
+
'Jordan',
|
|
11
|
+
'Taylor',
|
|
12
|
+
'Morgan',
|
|
13
|
+
'Casey',
|
|
14
|
+
'Riley',
|
|
15
|
+
'Avery',
|
|
16
|
+
'Quinn',
|
|
17
|
+
'Blake',
|
|
18
|
+
'Charlie',
|
|
19
|
+
'Dakota',
|
|
20
|
+
'Eden',
|
|
21
|
+
'Finley',
|
|
22
|
+
'Harper',
|
|
23
|
+
'Sage',
|
|
24
|
+
'River',
|
|
25
|
+
'Skylar',
|
|
26
|
+
'Rowan',
|
|
27
|
+
'Phoenix',
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export interface GuestNameConfig {
|
|
31
|
+
names?: string[];
|
|
32
|
+
prefixes?: string[];
|
|
33
|
+
usePrefixes?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generate a random guest name
|
|
38
|
+
* Uses userId to ensure consistency per user
|
|
39
|
+
*/
|
|
40
|
+
export function generateGuestName(
|
|
41
|
+
userId?: string,
|
|
42
|
+
config?: GuestNameConfig,
|
|
43
|
+
): string {
|
|
44
|
+
const names = config?.names || DEFAULT_NAMES;
|
|
45
|
+
const prefixes = config?.prefixes || [];
|
|
46
|
+
const usePrefixes = config?.usePrefixes ?? false;
|
|
47
|
+
|
|
48
|
+
if (!userId) {
|
|
49
|
+
const randomIndex = Math.floor(Math.random() * names.length);
|
|
50
|
+
return names[randomIndex];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Use userId hash for consistent name per user
|
|
54
|
+
const hash = userId.split('').reduce((acc, char) => {
|
|
55
|
+
return ((acc << 5) - acc + char.charCodeAt(0)) | 0;
|
|
56
|
+
}, 0);
|
|
57
|
+
|
|
58
|
+
const nameIndex = Math.abs(hash) % names.length;
|
|
59
|
+
const name = names[nameIndex];
|
|
60
|
+
|
|
61
|
+
if (usePrefixes && prefixes.length > 0) {
|
|
62
|
+
const prefixIndex = Math.abs(hash >> 8) % prefixes.length;
|
|
63
|
+
return `${prefixes[prefixIndex]} ${name}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return name;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get guest display name with fallback
|
|
71
|
+
*/
|
|
72
|
+
export function getGuestDisplayName(
|
|
73
|
+
userId?: string,
|
|
74
|
+
fallback = 'Guest',
|
|
75
|
+
config?: GuestNameConfig,
|
|
76
|
+
): string {
|
|
77
|
+
if (!userId) return fallback;
|
|
78
|
+
return generateGuestName(userId, config);
|
|
79
|
+
}
|
|
@@ -11,8 +11,39 @@ export interface PasswordConfig {
|
|
|
11
11
|
requireSpecialChar: boolean;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Social authentication provider configuration
|
|
16
|
+
*/
|
|
17
|
+
export interface SocialProviderConfig {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface GoogleAuthConfig extends SocialProviderConfig {
|
|
22
|
+
webClientId?: string;
|
|
23
|
+
iosClientId?: string;
|
|
24
|
+
androidClientId?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AppleAuthConfig extends SocialProviderConfig {
|
|
28
|
+
// Apple Sign In doesn't require additional config for basic usage
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Social authentication configuration
|
|
33
|
+
*/
|
|
34
|
+
export interface SocialAuthConfig {
|
|
35
|
+
google?: GoogleAuthConfig;
|
|
36
|
+
apple?: AppleAuthConfig;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Supported social auth providers
|
|
41
|
+
*/
|
|
42
|
+
export type SocialAuthProvider = "google" | "apple";
|
|
43
|
+
|
|
14
44
|
export interface AuthConfig {
|
|
15
45
|
password: PasswordConfig;
|
|
46
|
+
social?: SocialAuthConfig;
|
|
16
47
|
}
|
|
17
48
|
|
|
18
49
|
export const DEFAULT_PASSWORD_CONFIG: PasswordConfig = {
|
|
@@ -23,7 +54,13 @@ export const DEFAULT_PASSWORD_CONFIG: PasswordConfig = {
|
|
|
23
54
|
requireSpecialChar: false,
|
|
24
55
|
};
|
|
25
56
|
|
|
57
|
+
export const DEFAULT_SOCIAL_CONFIG: SocialAuthConfig = {
|
|
58
|
+
google: { enabled: false },
|
|
59
|
+
apple: { enabled: false },
|
|
60
|
+
};
|
|
61
|
+
|
|
26
62
|
export const DEFAULT_AUTH_CONFIG: AuthConfig = {
|
|
27
63
|
password: DEFAULT_PASSWORD_CONFIG,
|
|
64
|
+
social: DEFAULT_SOCIAL_CONFIG,
|
|
28
65
|
};
|
|
29
66
|
|
package/src/index.ts
CHANGED
|
@@ -43,8 +43,20 @@ export {
|
|
|
43
43
|
// DOMAIN LAYER - Value Objects
|
|
44
44
|
// =============================================================================
|
|
45
45
|
|
|
46
|
-
export type {
|
|
47
|
-
|
|
46
|
+
export type {
|
|
47
|
+
AuthConfig,
|
|
48
|
+
PasswordConfig,
|
|
49
|
+
SocialAuthConfig,
|
|
50
|
+
SocialProviderConfig,
|
|
51
|
+
GoogleAuthConfig,
|
|
52
|
+
AppleAuthConfig,
|
|
53
|
+
SocialAuthProvider,
|
|
54
|
+
} from './domain/value-objects/AuthConfig';
|
|
55
|
+
export {
|
|
56
|
+
DEFAULT_AUTH_CONFIG,
|
|
57
|
+
DEFAULT_PASSWORD_CONFIG,
|
|
58
|
+
DEFAULT_SOCIAL_CONFIG,
|
|
59
|
+
} from './domain/value-objects/AuthConfig';
|
|
48
60
|
|
|
49
61
|
// =============================================================================
|
|
50
62
|
// APPLICATION LAYER - Ports
|
|
@@ -55,6 +67,7 @@ export type {
|
|
|
55
67
|
IAuthProvider,
|
|
56
68
|
AuthCredentials,
|
|
57
69
|
SignUpCredentials,
|
|
70
|
+
SocialSignInResult,
|
|
58
71
|
} from './application/ports/IAuthProvider';
|
|
59
72
|
|
|
60
73
|
// =============================================================================
|
|
@@ -113,6 +126,10 @@ export type { UseProfileEditReturn, ProfileEditFormState } from './presentation/
|
|
|
113
126
|
|
|
114
127
|
export type { UserProfile, UpdateProfileParams } from './domain/entities/UserProfile';
|
|
115
128
|
|
|
129
|
+
// Domain Utils - Guest Names
|
|
130
|
+
export { generateGuestName, getGuestDisplayName } from './domain/utils/guestNameGenerator';
|
|
131
|
+
export type { GuestNameConfig } from './domain/utils/guestNameGenerator';
|
|
132
|
+
|
|
116
133
|
// =============================================================================
|
|
117
134
|
// PRESENTATION LAYER - Screens & Navigation
|
|
118
135
|
// =============================================================================
|
|
@@ -147,6 +164,8 @@ export { PasswordMatchIndicator } from './presentation/components/PasswordMatchI
|
|
|
147
164
|
export type { PasswordMatchIndicatorProps } from './presentation/components/PasswordMatchIndicator';
|
|
148
165
|
export { AuthBottomSheet } from './presentation/components/AuthBottomSheet';
|
|
149
166
|
export type { AuthBottomSheetProps } from './presentation/components/AuthBottomSheet';
|
|
167
|
+
export { SocialLoginButtons } from './presentation/components/SocialLoginButtons';
|
|
168
|
+
export type { SocialLoginButtonsProps } from './presentation/components/SocialLoginButtons';
|
|
150
169
|
export { ProfileSection } from './presentation/components/ProfileSection';
|
|
151
170
|
export type { ProfileSectionConfig, ProfileSectionProps } from './presentation/components/ProfileSection';
|
|
152
171
|
export { AccountActions } from './presentation/components/AccountActions';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Bottom sheet modal for authentication (Login/Register)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React, { useEffect, useCallback, useRef } from "react";
|
|
6
|
+
import React, { useEffect, useCallback, useRef, useState } from "react";
|
|
7
7
|
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
|
|
8
8
|
import {
|
|
9
9
|
BottomSheetModal,
|
|
@@ -17,12 +17,20 @@ import { useAuthModalStore } from "../stores/authModalStore";
|
|
|
17
17
|
import { useAuth } from "../hooks/useAuth";
|
|
18
18
|
import { LoginForm } from "./LoginForm";
|
|
19
19
|
import { RegisterForm } from "./RegisterForm";
|
|
20
|
+
import { SocialLoginButtons } from "./SocialLoginButtons";
|
|
21
|
+
import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
|
|
20
22
|
|
|
21
23
|
export interface AuthBottomSheetProps {
|
|
22
24
|
termsUrl?: string;
|
|
23
25
|
privacyUrl?: string;
|
|
24
26
|
onTermsPress?: () => void;
|
|
25
27
|
onPrivacyPress?: () => void;
|
|
28
|
+
/** Enabled social auth providers */
|
|
29
|
+
socialProviders?: SocialAuthProvider[];
|
|
30
|
+
/** Called when Google sign-in is requested */
|
|
31
|
+
onGoogleSignIn?: () => Promise<void>;
|
|
32
|
+
/** Called when Apple sign-in is requested */
|
|
33
|
+
onAppleSignIn?: () => Promise<void>;
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
@@ -30,11 +38,17 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
30
38
|
privacyUrl,
|
|
31
39
|
onTermsPress,
|
|
32
40
|
onPrivacyPress,
|
|
41
|
+
socialProviders = [],
|
|
42
|
+
onGoogleSignIn,
|
|
43
|
+
onAppleSignIn,
|
|
33
44
|
}) => {
|
|
34
45
|
const tokens = useAppDesignTokens();
|
|
35
46
|
const { t } = useLocalization();
|
|
36
47
|
const modalRef = useRef<BottomSheetModal>(null);
|
|
37
48
|
|
|
49
|
+
const [googleLoading, setGoogleLoading] = useState(false);
|
|
50
|
+
const [appleLoading, setAppleLoading] = useState(false);
|
|
51
|
+
|
|
38
52
|
const { isVisible, mode, hideAuthModal, setMode, executePendingCallback, clearPendingCallback } =
|
|
39
53
|
useAuthModalStore();
|
|
40
54
|
const { isAuthenticated, isGuest } = useAuth();
|
|
@@ -72,6 +86,26 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
72
86
|
setMode("login");
|
|
73
87
|
}, [setMode]);
|
|
74
88
|
|
|
89
|
+
const handleGoogleSignIn = useCallback(async () => {
|
|
90
|
+
if (!onGoogleSignIn) return;
|
|
91
|
+
setGoogleLoading(true);
|
|
92
|
+
try {
|
|
93
|
+
await onGoogleSignIn();
|
|
94
|
+
} finally {
|
|
95
|
+
setGoogleLoading(false);
|
|
96
|
+
}
|
|
97
|
+
}, [onGoogleSignIn]);
|
|
98
|
+
|
|
99
|
+
const handleAppleSignIn = useCallback(async () => {
|
|
100
|
+
if (!onAppleSignIn) return;
|
|
101
|
+
setAppleLoading(true);
|
|
102
|
+
try {
|
|
103
|
+
await onAppleSignIn();
|
|
104
|
+
} finally {
|
|
105
|
+
setAppleLoading(false);
|
|
106
|
+
}
|
|
107
|
+
}, [onAppleSignIn]);
|
|
108
|
+
|
|
75
109
|
const renderBackdrop = useCallback(
|
|
76
110
|
(props: BottomSheetBackdropProps) => (
|
|
77
111
|
<BottomSheetBackdrop {...props} appearsOnIndex={0} disappearsOnIndex={-1} pressBehavior="close" />
|
|
@@ -130,6 +164,16 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
130
164
|
onPrivacyPress={onPrivacyPress}
|
|
131
165
|
/>
|
|
132
166
|
)}
|
|
167
|
+
|
|
168
|
+
{socialProviders.length > 0 && (
|
|
169
|
+
<SocialLoginButtons
|
|
170
|
+
enabledProviders={socialProviders}
|
|
171
|
+
onGooglePress={handleGoogleSignIn}
|
|
172
|
+
onApplePress={handleAppleSignIn}
|
|
173
|
+
googleLoading={googleLoading}
|
|
174
|
+
appleLoading={appleLoading}
|
|
175
|
+
/>
|
|
176
|
+
)}
|
|
133
177
|
</View>
|
|
134
178
|
</BottomSheetScrollView>
|
|
135
179
|
</BottomSheetModal>
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SocialLoginButtons Component
|
|
3
|
+
* Renders social sign-in buttons (Google, Apple)
|
|
4
|
+
* Configurable via props - enabled providers shown dynamically
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import {
|
|
9
|
+
View,
|
|
10
|
+
Text,
|
|
11
|
+
TouchableOpacity,
|
|
12
|
+
StyleSheet,
|
|
13
|
+
Platform,
|
|
14
|
+
ActivityIndicator,
|
|
15
|
+
} from "react-native";
|
|
16
|
+
import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
|
|
17
|
+
import { useLocalization } from "@umituz/react-native-localization";
|
|
18
|
+
import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
|
|
19
|
+
|
|
20
|
+
export interface SocialLoginButtonsProps {
|
|
21
|
+
/** Enabled providers to display */
|
|
22
|
+
enabledProviders: SocialAuthProvider[];
|
|
23
|
+
/** Called when Google sign-in is pressed */
|
|
24
|
+
onGooglePress?: () => void;
|
|
25
|
+
/** Called when Apple sign-in is pressed */
|
|
26
|
+
onApplePress?: () => void;
|
|
27
|
+
/** Loading state for Google button */
|
|
28
|
+
googleLoading?: boolean;
|
|
29
|
+
/** Loading state for Apple button */
|
|
30
|
+
appleLoading?: boolean;
|
|
31
|
+
/** Disable all buttons */
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const SocialLoginButtons: React.FC<SocialLoginButtonsProps> = ({
|
|
36
|
+
enabledProviders,
|
|
37
|
+
onGooglePress,
|
|
38
|
+
onApplePress,
|
|
39
|
+
googleLoading = false,
|
|
40
|
+
appleLoading = false,
|
|
41
|
+
disabled = false,
|
|
42
|
+
}) => {
|
|
43
|
+
const tokens = useAppDesignTokens();
|
|
44
|
+
const { t } = useLocalization();
|
|
45
|
+
|
|
46
|
+
const showGoogle = enabledProviders.includes("google");
|
|
47
|
+
const showApple = enabledProviders.includes("apple") && Platform.OS === "ios";
|
|
48
|
+
|
|
49
|
+
if (!showGoogle && !showApple) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<View style={styles.container}>
|
|
55
|
+
<View style={styles.dividerContainer}>
|
|
56
|
+
<View style={[styles.divider, { backgroundColor: tokens.colors.border }]} />
|
|
57
|
+
<Text style={[styles.dividerText, { color: tokens.colors.textSecondary }]}>
|
|
58
|
+
{t("auth.orContinueWith")}
|
|
59
|
+
</Text>
|
|
60
|
+
<View style={[styles.divider, { backgroundColor: tokens.colors.border }]} />
|
|
61
|
+
</View>
|
|
62
|
+
|
|
63
|
+
<View style={styles.buttonsContainer}>
|
|
64
|
+
{showGoogle && (
|
|
65
|
+
<TouchableOpacity
|
|
66
|
+
style={[
|
|
67
|
+
styles.socialButton,
|
|
68
|
+
{ borderColor: tokens.colors.border },
|
|
69
|
+
disabled && styles.disabledButton,
|
|
70
|
+
]}
|
|
71
|
+
onPress={onGooglePress}
|
|
72
|
+
disabled={disabled || googleLoading}
|
|
73
|
+
activeOpacity={0.7}
|
|
74
|
+
>
|
|
75
|
+
{googleLoading ? (
|
|
76
|
+
<ActivityIndicator size="small" color={tokens.colors.textPrimary} />
|
|
77
|
+
) : (
|
|
78
|
+
<>
|
|
79
|
+
<GoogleIcon />
|
|
80
|
+
<Text style={[styles.buttonText, { color: tokens.colors.textPrimary }]}>
|
|
81
|
+
Google
|
|
82
|
+
</Text>
|
|
83
|
+
</>
|
|
84
|
+
)}
|
|
85
|
+
</TouchableOpacity>
|
|
86
|
+
)}
|
|
87
|
+
|
|
88
|
+
{showApple && (
|
|
89
|
+
<TouchableOpacity
|
|
90
|
+
style={[
|
|
91
|
+
styles.socialButton,
|
|
92
|
+
{ borderColor: tokens.colors.border },
|
|
93
|
+
disabled && styles.disabledButton,
|
|
94
|
+
]}
|
|
95
|
+
onPress={onApplePress}
|
|
96
|
+
disabled={disabled || appleLoading}
|
|
97
|
+
activeOpacity={0.7}
|
|
98
|
+
>
|
|
99
|
+
{appleLoading ? (
|
|
100
|
+
<ActivityIndicator size="small" color={tokens.colors.textPrimary} />
|
|
101
|
+
) : (
|
|
102
|
+
<>
|
|
103
|
+
<AppleIcon color={tokens.colors.textPrimary} />
|
|
104
|
+
<Text style={[styles.buttonText, { color: tokens.colors.textPrimary }]}>
|
|
105
|
+
Apple
|
|
106
|
+
</Text>
|
|
107
|
+
</>
|
|
108
|
+
)}
|
|
109
|
+
</TouchableOpacity>
|
|
110
|
+
)}
|
|
111
|
+
</View>
|
|
112
|
+
</View>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Google Icon SVG as React Native component
|
|
118
|
+
*/
|
|
119
|
+
const GoogleIcon: React.FC = () => (
|
|
120
|
+
<View style={styles.iconContainer}>
|
|
121
|
+
<Text style={styles.googleIcon}>G</Text>
|
|
122
|
+
</View>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Apple Icon as simple text (uses SF Symbol on iOS)
|
|
127
|
+
*/
|
|
128
|
+
const AppleIcon: React.FC<{ color: string }> = ({ color }) => (
|
|
129
|
+
<Text style={[styles.appleIcon, { color }]}></Text>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const styles = StyleSheet.create({
|
|
133
|
+
container: {
|
|
134
|
+
marginTop: 24,
|
|
135
|
+
},
|
|
136
|
+
dividerContainer: {
|
|
137
|
+
flexDirection: "row",
|
|
138
|
+
alignItems: "center",
|
|
139
|
+
marginBottom: 20,
|
|
140
|
+
},
|
|
141
|
+
divider: {
|
|
142
|
+
flex: 1,
|
|
143
|
+
height: 1,
|
|
144
|
+
},
|
|
145
|
+
dividerText: {
|
|
146
|
+
marginHorizontal: 16,
|
|
147
|
+
fontSize: 14,
|
|
148
|
+
},
|
|
149
|
+
buttonsContainer: {
|
|
150
|
+
flexDirection: "row",
|
|
151
|
+
gap: 12,
|
|
152
|
+
},
|
|
153
|
+
socialButton: {
|
|
154
|
+
flex: 1,
|
|
155
|
+
flexDirection: "row",
|
|
156
|
+
alignItems: "center",
|
|
157
|
+
justifyContent: "center",
|
|
158
|
+
paddingVertical: 14,
|
|
159
|
+
borderRadius: 12,
|
|
160
|
+
borderWidth: 1,
|
|
161
|
+
gap: 8,
|
|
162
|
+
},
|
|
163
|
+
disabledButton: {
|
|
164
|
+
opacity: 0.5,
|
|
165
|
+
},
|
|
166
|
+
buttonText: {
|
|
167
|
+
fontSize: 16,
|
|
168
|
+
fontWeight: "600",
|
|
169
|
+
},
|
|
170
|
+
iconContainer: {
|
|
171
|
+
width: 20,
|
|
172
|
+
height: 20,
|
|
173
|
+
alignItems: "center",
|
|
174
|
+
justifyContent: "center",
|
|
175
|
+
},
|
|
176
|
+
googleIcon: {
|
|
177
|
+
fontSize: 18,
|
|
178
|
+
fontWeight: "700",
|
|
179
|
+
color: "#4285F4",
|
|
180
|
+
},
|
|
181
|
+
appleIcon: {
|
|
182
|
+
fontSize: 20,
|
|
183
|
+
},
|
|
184
|
+
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { useMemo } from "react";
|
|
8
8
|
import { useAuth } from "./useAuth";
|
|
9
|
+
import { generateGuestName, type GuestNameConfig } from "../../domain/utils/guestNameGenerator";
|
|
9
10
|
|
|
10
11
|
export interface UserProfileData {
|
|
11
12
|
displayName: string;
|
|
@@ -16,9 +17,9 @@ export interface UserProfileData {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export interface UseUserProfileParams {
|
|
19
|
-
anonymousDisplayName?: string;
|
|
20
20
|
guestDisplayName?: string;
|
|
21
21
|
accountRoute?: string;
|
|
22
|
+
guestNameConfig?: GuestNameConfig;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export const useUserProfile = (
|
|
@@ -26,9 +27,9 @@ export const useUserProfile = (
|
|
|
26
27
|
): UserProfileData | undefined => {
|
|
27
28
|
const { user } = useAuth();
|
|
28
29
|
|
|
29
|
-
const
|
|
30
|
-
const guestName = params?.guestDisplayName || "Guest User";
|
|
30
|
+
const guestName = params?.guestDisplayName || "Guest";
|
|
31
31
|
const accountRoute = params?.accountRoute || "Account";
|
|
32
|
+
const nameConfig = params?.guestNameConfig;
|
|
32
33
|
|
|
33
34
|
return useMemo(() => {
|
|
34
35
|
if (!user) {
|
|
@@ -39,7 +40,7 @@ export const useUserProfile = (
|
|
|
39
40
|
|
|
40
41
|
if (isAnonymous) {
|
|
41
42
|
return {
|
|
42
|
-
displayName:
|
|
43
|
+
displayName: generateGuestName(user.uid, nameConfig),
|
|
43
44
|
userId: user.uid,
|
|
44
45
|
isAnonymous: true,
|
|
45
46
|
};
|
|
@@ -52,5 +53,5 @@ export const useUserProfile = (
|
|
|
52
53
|
isAnonymous: false,
|
|
53
54
|
avatarUrl: user.photoURL || undefined,
|
|
54
55
|
};
|
|
55
|
-
}, [user,
|
|
56
|
+
}, [user, guestName, accountRoute, nameConfig]);
|
|
56
57
|
};
|