@umituz/react-native-auth 3.4.37 → 3.4.39
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/index.ts +0 -2
- package/src/presentation/components/ProfileBenefitsList.tsx +1 -1
- package/src/presentation/hooks/useAccountManagement.ts +40 -8
- package/src/presentation/hooks/useUserProfile.ts +35 -42
- package/src/presentation/utils/accountDeleteHandler.util.ts +1 -1
- package/src/domain/README.md +0 -444
- package/src/domain/utils/anonymousNameGenerator.ts +0 -79
- package/src/presentation/hooks/useUserProfile.md +0 -297
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.39",
|
|
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
|
@@ -151,8 +151,6 @@ export type { SocialAuthConfiguration } from './presentation/hooks/useAuthBottom
|
|
|
151
151
|
|
|
152
152
|
// DOMAIN ENTITIES & UTILS
|
|
153
153
|
export type { UserProfile, UpdateProfileParams } from './domain/entities/UserProfile';
|
|
154
|
-
export { generateAnonymousName, getAnonymousDisplayName } from './domain/utils/anonymousNameGenerator';
|
|
155
|
-
export type { AnonymousNameConfig } from './domain/utils/anonymousNameGenerator';
|
|
156
154
|
export { migrateUserData, configureMigration } from './domain/utils/migration';
|
|
157
155
|
export type { MigrationConfig } from './domain/utils/migration';
|
|
158
156
|
|
|
@@ -17,7 +17,7 @@ export const ProfileBenefitsList: React.FC<ProfileBenefitsListProps> = ({ benefi
|
|
|
17
17
|
<View style={styles.benefitsContainer}>
|
|
18
18
|
{benefits.map((benefit, index) => (
|
|
19
19
|
<View key={index} style={styles.benefitItem}>
|
|
20
|
-
<AtomicIcon name="
|
|
20
|
+
<AtomicIcon name="checkmark-circle" size="sm" color="primary" />
|
|
21
21
|
<AtomicText
|
|
22
22
|
type="bodyMedium"
|
|
23
23
|
color="secondary"
|
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useAccountManagement Hook
|
|
3
3
|
* Provides account management functionality (logout, delete)
|
|
4
|
-
* Generic hook - reauthentication is handled via callback from calling app
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import { useCallback, useState } from "react";
|
|
7
|
+
import { Alert } from "react-native";
|
|
8
8
|
import { useAuth } from "./useAuth";
|
|
9
9
|
import { handleAccountDeletion } from "../utils/accountDeleteHandler.util";
|
|
10
10
|
|
|
11
11
|
export interface UseAccountManagementOptions {
|
|
12
12
|
/**
|
|
13
13
|
* Callback invoked when reauthentication is required (for Google/Apple)
|
|
14
|
-
* App should show appropriate UI (Google/Apple sign-in) and return success status
|
|
15
|
-
* If not provided, reauthentication errors will be thrown
|
|
16
14
|
*/
|
|
17
15
|
onReauthRequired?: () => Promise<boolean>;
|
|
18
16
|
/**
|
|
19
|
-
* Callback invoked when password reauthentication is required
|
|
20
|
-
*
|
|
17
|
+
* Callback invoked when password reauthentication is required
|
|
18
|
+
* If not provided, built-in Alert.prompt will be used
|
|
21
19
|
*/
|
|
22
20
|
onPasswordRequired?: () => Promise<string | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Translations for built-in password prompt
|
|
23
|
+
*/
|
|
24
|
+
passwordPromptTitle?: string;
|
|
25
|
+
passwordPromptMessage?: string;
|
|
26
|
+
passwordPromptCancel?: string;
|
|
27
|
+
passwordPromptConfirm?: string;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
export interface UseAccountManagementReturn {
|
|
@@ -34,7 +39,31 @@ export const useAccountManagement = (
|
|
|
34
39
|
): UseAccountManagementReturn => {
|
|
35
40
|
const { user, loading, signOut } = useAuth();
|
|
36
41
|
const [isDeletingAccount, setIsDeletingAccount] = useState(false);
|
|
37
|
-
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
onReauthRequired,
|
|
45
|
+
onPasswordRequired,
|
|
46
|
+
passwordPromptTitle = "Password Required",
|
|
47
|
+
passwordPromptMessage = "Enter your password to delete account",
|
|
48
|
+
passwordPromptCancel = "Cancel",
|
|
49
|
+
passwordPromptConfirm = "Confirm",
|
|
50
|
+
} = options;
|
|
51
|
+
|
|
52
|
+
const defaultPasswordPrompt = useCallback((): Promise<string | null> => {
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
Alert.prompt(
|
|
55
|
+
passwordPromptTitle,
|
|
56
|
+
passwordPromptMessage,
|
|
57
|
+
[
|
|
58
|
+
{ text: passwordPromptCancel, style: "cancel", onPress: () => resolve(null) },
|
|
59
|
+
{ text: passwordPromptConfirm, onPress: (pwd) => resolve(pwd || null) },
|
|
60
|
+
],
|
|
61
|
+
"secure-text"
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
}, [passwordPromptTitle, passwordPromptMessage, passwordPromptCancel, passwordPromptConfirm]);
|
|
65
|
+
|
|
66
|
+
const passwordHandler = onPasswordRequired || defaultPasswordPrompt;
|
|
38
67
|
|
|
39
68
|
const logout = useCallback(async () => {
|
|
40
69
|
await signOut();
|
|
@@ -59,11 +88,14 @@ export const useAccountManagement = (
|
|
|
59
88
|
setIsDeletingAccount(true);
|
|
60
89
|
|
|
61
90
|
try {
|
|
62
|
-
await handleAccountDeletion({
|
|
91
|
+
await handleAccountDeletion({
|
|
92
|
+
onReauthRequired,
|
|
93
|
+
onPasswordRequired: passwordHandler,
|
|
94
|
+
});
|
|
63
95
|
} finally {
|
|
64
96
|
setIsDeletingAccount(false);
|
|
65
97
|
}
|
|
66
|
-
}, [user, onReauthRequired,
|
|
98
|
+
}, [user, onReauthRequired, passwordHandler]);
|
|
67
99
|
|
|
68
100
|
return {
|
|
69
101
|
logout,
|
|
@@ -1,59 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useUserProfile Hook
|
|
3
|
-
* Generic hook for user profile configuration
|
|
4
3
|
* Returns profile data for display in settings or profile screens
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import { useMemo } from "react";
|
|
8
7
|
import { useAuth } from "./useAuth";
|
|
9
|
-
import { generateAnonymousName, type AnonymousNameConfig } from "../../domain/utils/anonymousNameGenerator";
|
|
10
8
|
|
|
11
9
|
export interface UserProfileData {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
displayName?: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
isAnonymous: boolean;
|
|
13
|
+
avatarUrl?: string;
|
|
14
|
+
accountSettingsRoute?: string;
|
|
15
|
+
benefits?: string[];
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
export interface UseUserProfileParams {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
anonymousNameConfig?: AnonymousNameConfig;
|
|
19
|
+
anonymousDisplayName?: string;
|
|
20
|
+
accountRoute?: string;
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
export const useUserProfile = (
|
|
27
|
-
|
|
24
|
+
params?: UseUserProfileParams,
|
|
28
25
|
): UserProfileData | undefined => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
isAnonymous: false,
|
|
56
|
-
avatarUrl: user.photoURL || undefined,
|
|
57
|
-
};
|
|
58
|
-
}, [user, anonymousName, accountRoute, nameConfig]);
|
|
26
|
+
const { user } = useAuth();
|
|
27
|
+
const anonymousName = params?.anonymousDisplayName ?? "Anonymous User";
|
|
28
|
+
const accountRoute = params?.accountRoute;
|
|
29
|
+
|
|
30
|
+
return useMemo(() => {
|
|
31
|
+
if (!user) return undefined;
|
|
32
|
+
|
|
33
|
+
const isAnonymous = user.isAnonymous || false;
|
|
34
|
+
|
|
35
|
+
if (isAnonymous) {
|
|
36
|
+
return {
|
|
37
|
+
displayName: anonymousName,
|
|
38
|
+
userId: user.uid,
|
|
39
|
+
isAnonymous: true,
|
|
40
|
+
accountSettingsRoute: accountRoute,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
accountSettingsRoute: accountRoute,
|
|
46
|
+
displayName: user.displayName || user.email || anonymousName,
|
|
47
|
+
userId: user.uid,
|
|
48
|
+
isAnonymous: false,
|
|
49
|
+
avatarUrl: user.photoURL || undefined,
|
|
50
|
+
};
|
|
51
|
+
}, [user, anonymousName, accountRoute]);
|
|
59
52
|
};
|
|
@@ -58,7 +58,7 @@ async function handleReauthentication(
|
|
|
58
58
|
const { onReauthRequired, onPasswordRequired } = callbacks;
|
|
59
59
|
|
|
60
60
|
// Handle password reauth
|
|
61
|
-
if (initialResult.error?.code === "auth/password-reauth
|
|
61
|
+
if (initialResult.error?.code === "auth/password-reauth" && onPasswordRequired) {
|
|
62
62
|
await retryWithPassword(onPasswordRequired);
|
|
63
63
|
return;
|
|
64
64
|
}
|
package/src/domain/README.md
DELETED
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
# Domain Layer
|
|
2
|
-
|
|
3
|
-
Core business logic, domain entities, value objects, and domain rules.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Strategy
|
|
8
|
-
|
|
9
|
-
**Purpose**: Contains business rules and domain models independent of external dependencies. Represents the core authentication logic.
|
|
10
|
-
|
|
11
|
-
**When to Use**:
|
|
12
|
-
- Understanding business rules
|
|
13
|
-
- Working with domain entities
|
|
14
|
-
- Implementing validation
|
|
15
|
-
- Learning about data structures
|
|
16
|
-
|
|
17
|
-
**Location**: `src/domain/`
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Structure
|
|
22
|
-
|
|
23
|
-
### Entities
|
|
24
|
-
|
|
25
|
-
**entities/AuthUser.ts** - Provider-agnostic user entity
|
|
26
|
-
**entities/UserProfile.ts** - User profile for Firestore
|
|
27
|
-
|
|
28
|
-
### Value Objects
|
|
29
|
-
|
|
30
|
-
**value-objects/AuthConfig.ts** - Authentication configuration
|
|
31
|
-
|
|
32
|
-
### Errors
|
|
33
|
-
|
|
34
|
-
**errors/AuthError.ts** - Domain-specific error hierarchy
|
|
35
|
-
|
|
36
|
-
### Utils
|
|
37
|
-
|
|
38
|
-
**utils/anonymousNameGenerator.ts** - Anonymous name generation
|
|
39
|
-
**utils/migration.ts** - Data migration utilities
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Domain Entities
|
|
44
|
-
|
|
45
|
-
### AuthUser
|
|
46
|
-
|
|
47
|
-
**PURPOSE**: Provider-agnostic user entity for authentication
|
|
48
|
-
|
|
49
|
-
**IMPORT PATH**:
|
|
50
|
-
```typescript
|
|
51
|
-
import type { AuthUser } from '@umituz/react-native-auth';
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
**PROPERTIES**:
|
|
55
|
-
- `uid: string` - Unique user identifier
|
|
56
|
-
- `email: string | null` - Email address
|
|
57
|
-
- `displayName: string | null` - Display name
|
|
58
|
-
- `photoURL: string | null` - Profile photo URL
|
|
59
|
-
- `isAnonymous: boolean` - Anonymous flag
|
|
60
|
-
- `emailVerified: boolean` - Email verification status
|
|
61
|
-
- `provider: AuthProviderType` - Auth provider type
|
|
62
|
-
|
|
63
|
-
**Rules**:
|
|
64
|
-
- MUST have unique uid
|
|
65
|
-
- MUST NOT have empty uid
|
|
66
|
-
- Anonymous users have null email
|
|
67
|
-
- Provider indicates auth method
|
|
68
|
-
- Email can be null for social auth
|
|
69
|
-
|
|
70
|
-
**MUST NOT**:
|
|
71
|
-
- Allow empty uid
|
|
72
|
-
- Change uid after creation
|
|
73
|
-
- Have anonymous user with email
|
|
74
|
-
- Use invalid provider type
|
|
75
|
-
|
|
76
|
-
**Documentation**: `entities/AuthUser.md`
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
### UserProfile
|
|
81
|
-
|
|
82
|
-
**PURPOSE**: User profile entity for Firestore document storage
|
|
83
|
-
|
|
84
|
-
**IMPORT PATH**:
|
|
85
|
-
```typescript
|
|
86
|
-
import type { UserProfile } from '@umituz/react-native-auth';
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
**PROPERTIES**:
|
|
90
|
-
- `uid: string` - User ID
|
|
91
|
-
- `email: string | null` - Email address
|
|
92
|
-
- `displayName: string | null` - Display name
|
|
93
|
-
- `photoURL: string | null` - Profile photo URL
|
|
94
|
-
- `isAnonymous: boolean` - Anonymous flag
|
|
95
|
-
- `createdAt: Date | null` - Account creation date
|
|
96
|
-
- `lastLoginAt: Date | null` - Last login timestamp
|
|
97
|
-
|
|
98
|
-
**Rules**:
|
|
99
|
-
- MUST create on user registration
|
|
100
|
-
- MUST include uid
|
|
101
|
-
- MUST set initial timestamps
|
|
102
|
-
- MUST validate updates
|
|
103
|
-
- MUST handle partial updates
|
|
104
|
-
|
|
105
|
-
**MUST NOT**:
|
|
106
|
-
- Delete existing profiles
|
|
107
|
-
- Skip validation
|
|
108
|
-
- Use client timestamps
|
|
109
|
-
- Overwrite without checking
|
|
110
|
-
|
|
111
|
-
**Documentation**: `entities/UserProfile.md`
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## Value Objects
|
|
116
|
-
|
|
117
|
-
### AuthConfig
|
|
118
|
-
|
|
119
|
-
**PURPOSE**: Authentication configuration value object
|
|
120
|
-
|
|
121
|
-
**IMPORT PATH**:
|
|
122
|
-
```typescript
|
|
123
|
-
import type { AuthConfig } from '@umituz/react-native-auth';
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**COMPONENTS**:
|
|
127
|
-
- `password: PasswordConfig` - Password requirements
|
|
128
|
-
- `social?: SocialAuthConfig` - Social provider config
|
|
129
|
-
|
|
130
|
-
**PasswordConfig**:
|
|
131
|
-
- `minLength: number` - Minimum password length
|
|
132
|
-
- `requireUppercase: boolean` - Require uppercase
|
|
133
|
-
- `requireLowercase: boolean` - Require lowercase
|
|
134
|
-
- `requireNumber: boolean` - Require number
|
|
135
|
-
- `requireSpecialChar: boolean` - Require special character
|
|
136
|
-
|
|
137
|
-
**Rules**:
|
|
138
|
-
- MUST set minLength between 4-128
|
|
139
|
-
- MUST validate password against config
|
|
140
|
-
- MUST provide password config
|
|
141
|
-
- MAY provide social config
|
|
142
|
-
|
|
143
|
-
**MUST NOT**:
|
|
144
|
-
- Set minLength < 4
|
|
145
|
-
- Set minLength > 128
|
|
146
|
-
- Skip validation
|
|
147
|
-
|
|
148
|
-
**Documentation**: `ConfigAndErrors.md`
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Domain Errors
|
|
153
|
-
|
|
154
|
-
### AuthError Hierarchy
|
|
155
|
-
|
|
156
|
-
**PURPOSE**: Type-safe error handling with clear error types
|
|
157
|
-
|
|
158
|
-
**IMPORT PATH**:
|
|
159
|
-
```typescript
|
|
160
|
-
import {
|
|
161
|
-
AuthError,
|
|
162
|
-
AuthUserNotFoundError,
|
|
163
|
-
AuthWrongPasswordError,
|
|
164
|
-
AuthEmailAlreadyInUseError,
|
|
165
|
-
AuthWeakPasswordError,
|
|
166
|
-
AuthInvalidEmailError,
|
|
167
|
-
AuthNetworkError
|
|
168
|
-
} from '@umituz/react-native-auth';
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
**ERROR TYPES**:
|
|
172
|
-
- `AuthUserNotFoundError` - User not found
|
|
173
|
-
- `AuthWrongPasswordError` - Incorrect password
|
|
174
|
-
- `AuthEmailAlreadyInUseError` - Email already registered
|
|
175
|
-
- `AuthWeakPasswordError` - Password too weak
|
|
176
|
-
- `AuthInvalidEmailError` - Invalid email format
|
|
177
|
-
- `AuthNetworkError` - Network connection failure
|
|
178
|
-
|
|
179
|
-
**Rules**:
|
|
180
|
-
- MUST use domain errors (not Firebase)
|
|
181
|
-
- MUST map Firebase errors to domain
|
|
182
|
-
- MUST preserve error context
|
|
183
|
-
- MUST show user-friendly messages
|
|
184
|
-
- MUST not expose system details
|
|
185
|
-
|
|
186
|
-
**MUST NOT**:
|
|
187
|
-
- Throw Firebase errors directly
|
|
188
|
-
- Expose error codes
|
|
189
|
-
- Show stack traces
|
|
190
|
-
- Reveal sensitive information
|
|
191
|
-
|
|
192
|
-
**Documentation**: `ConfigAndErrors.md`
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
## Domain Utilities
|
|
197
|
-
|
|
198
|
-
### Anonymous Name Generator
|
|
199
|
-
|
|
200
|
-
**PURPOSE**: Generate random names for anonymous users
|
|
201
|
-
|
|
202
|
-
**IMPORT PATH**:
|
|
203
|
-
```typescript
|
|
204
|
-
import {
|
|
205
|
-
generateAnonymousName,
|
|
206
|
-
getAnonymousDisplayName
|
|
207
|
-
} from '@umituz/react-native-auth';
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
**FUNCTIONS**:
|
|
211
|
-
- `generateAnonymousName(uid, config?)` - Generate anonymous name
|
|
212
|
-
- `getAnonymousDisplayName(uid)` - Get display name only
|
|
213
|
-
|
|
214
|
-
**CONFIGURATION**:
|
|
215
|
-
- `prefix` - Name prefix (default: 'User')
|
|
216
|
-
- `adjectiveCount` - Number of adjectives (default: 2)
|
|
217
|
-
- `nounCount` - Number of nouns (default: 1)
|
|
218
|
-
- `showNumbers` - Include numbers (default: true)
|
|
219
|
-
|
|
220
|
-
**Rules**:
|
|
221
|
-
- MUST be deterministic for same uid
|
|
222
|
-
- MUST generate unique names
|
|
223
|
-
- MUST be human-readable
|
|
224
|
-
- MUST not contain offensive words
|
|
225
|
-
- MUST support custom configuration
|
|
226
|
-
|
|
227
|
-
**MUST NOT**:
|
|
228
|
-
- Generate duplicate names
|
|
229
|
-
- Use offensive language
|
|
230
|
-
- Be non-deterministic
|
|
231
|
-
- Ignore configuration
|
|
232
|
-
|
|
233
|
-
---
|
|
234
|
-
|
|
235
|
-
### Migration Utilities
|
|
236
|
-
|
|
237
|
-
**PURPOSE**: Migrate user data between collections
|
|
238
|
-
|
|
239
|
-
**IMPORT PATH**:
|
|
240
|
-
```typescript
|
|
241
|
-
import {
|
|
242
|
-
migrateUserData,
|
|
243
|
-
configureMigration
|
|
244
|
-
} from '@umituz/react-native-auth';
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
**FUNCTIONS**:
|
|
248
|
-
- `configureMigration(config)` - Configure migration
|
|
249
|
-
- `migrateUserData(userId)` - Run migration
|
|
250
|
-
|
|
251
|
-
**CONFIGURATION**:
|
|
252
|
-
- `from` - Source collection name
|
|
253
|
-
- `to` - Target collection name
|
|
254
|
-
- `transform` - Data transformation function
|
|
255
|
-
- `verify` - Verification function (optional)
|
|
256
|
-
|
|
257
|
-
**Rules**:
|
|
258
|
-
- MUST configure before migrating
|
|
259
|
-
- MUST transform data correctly
|
|
260
|
-
- MUST handle migration errors
|
|
261
|
-
- MUST verify migration success
|
|
262
|
-
- MUST not delete source automatically
|
|
263
|
-
|
|
264
|
-
**MUST NOT**:
|
|
265
|
-
- Skip configuration
|
|
266
|
-
- Lose data during migration
|
|
267
|
-
- Assume identical schemas
|
|
268
|
-
- Delete source without verification
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Type Guards
|
|
273
|
-
|
|
274
|
-
### User Type Guards
|
|
275
|
-
|
|
276
|
-
**PURPOSE**: Type-safe user checking
|
|
277
|
-
|
|
278
|
-
**IMPORT PATH**:
|
|
279
|
-
```typescript
|
|
280
|
-
import {
|
|
281
|
-
isAuthenticatedUser,
|
|
282
|
-
isAnonymousUser,
|
|
283
|
-
hasEmail
|
|
284
|
-
} from '@umituz/react-native-auth';
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
**GUARDS**:
|
|
288
|
-
- `isAuthenticatedUser(user)` - Check if authenticated user
|
|
289
|
-
- `isAnonymousUser(user)` - Check if anonymous user
|
|
290
|
-
- `hasEmail(user)` - Check if has email
|
|
291
|
-
|
|
292
|
-
**Rules**:
|
|
293
|
-
- MUST use for type narrowing
|
|
294
|
-
- MUST validate before operations
|
|
295
|
-
- MUST check null cases
|
|
296
|
-
- MUST return boolean
|
|
297
|
-
|
|
298
|
-
**MUST NOT**:
|
|
299
|
-
- Skip type guards
|
|
300
|
-
- Assume user type
|
|
301
|
-
- Skip null checks
|
|
302
|
-
- Return non-boolean
|
|
303
|
-
|
|
304
|
-
---
|
|
305
|
-
|
|
306
|
-
## Domain Rules
|
|
307
|
-
|
|
308
|
-
### AuthUser Rules
|
|
309
|
-
|
|
310
|
-
**MUST**:
|
|
311
|
-
- Have unique uid
|
|
312
|
-
- Validate uid not empty
|
|
313
|
-
- Validate email format if provided
|
|
314
|
-
- Check provider is valid
|
|
315
|
-
- Verify required fields
|
|
316
|
-
|
|
317
|
-
**MUST NOT**:
|
|
318
|
-
- Allow empty uid
|
|
319
|
-
- Accept invalid email
|
|
320
|
-
- Use wrong provider type
|
|
321
|
-
- Have anonymous user with email
|
|
322
|
-
|
|
323
|
-
**Constraints**:
|
|
324
|
-
- uid required
|
|
325
|
-
- Email format validated
|
|
326
|
-
- Provider must be known type
|
|
327
|
-
- Anonymous users have null email
|
|
328
|
-
|
|
329
|
-
---
|
|
330
|
-
|
|
331
|
-
### UserProfile Rules
|
|
332
|
-
|
|
333
|
-
**MUST**:
|
|
334
|
-
- Validate display name 2-50 characters
|
|
335
|
-
- Validate photo URL format
|
|
336
|
-
- Use server timestamps
|
|
337
|
-
- Handle partial updates
|
|
338
|
-
- Preserve existing data
|
|
339
|
-
|
|
340
|
-
**MUST NOT**:
|
|
341
|
-
- Allow display name < 2 chars
|
|
342
|
-
- Accept invalid URLs
|
|
343
|
-
- Use client timestamps
|
|
344
|
-
- Overwrite existing data
|
|
345
|
-
|
|
346
|
-
**Constraints**:
|
|
347
|
-
- Display name min 2, max 100 chars
|
|
348
|
-
- Cannot be only whitespace
|
|
349
|
-
- Valid URL required for photoURL
|
|
350
|
-
- One profile per uid
|
|
351
|
-
|
|
352
|
-
---
|
|
353
|
-
|
|
354
|
-
### Password Rules
|
|
355
|
-
|
|
356
|
-
**MUST**:
|
|
357
|
-
- Enforce minimum length
|
|
358
|
-
- Check uppercase if required
|
|
359
|
-
- Check lowercase if required
|
|
360
|
-
- Check number if required
|
|
361
|
-
- Check special char if required
|
|
362
|
-
|
|
363
|
-
**MUST NOT**:
|
|
364
|
-
- Allow weak passwords
|
|
365
|
-
- Skip validation checks
|
|
366
|
-
- Accept passwords below minimum
|
|
367
|
-
|
|
368
|
-
**Constraints**:
|
|
369
|
-
- Minimum length: 4-128 chars
|
|
370
|
-
- Requirements configurable
|
|
371
|
-
- All requirements optional
|
|
372
|
-
- Default min length: 6 (lenient)
|
|
373
|
-
|
|
374
|
-
---
|
|
375
|
-
|
|
376
|
-
## Best Practices
|
|
377
|
-
|
|
378
|
-
### Entity Usage
|
|
379
|
-
|
|
380
|
-
**MUST**:
|
|
381
|
-
- Use type guards for type narrowing
|
|
382
|
-
- Validate before operations
|
|
383
|
-
- Handle null values appropriately
|
|
384
|
-
- Follow domain rules
|
|
385
|
-
- Use proper error types
|
|
386
|
-
|
|
387
|
-
**MUST NOT**:
|
|
388
|
-
- Skip validation
|
|
389
|
-
- Assume required fields present
|
|
390
|
-
- Ignore null cases
|
|
391
|
-
- Use generic error types
|
|
392
|
-
- Break domain rules
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
|
-
### Error Handling
|
|
397
|
-
|
|
398
|
-
**MUST**:
|
|
399
|
-
- Use domain error types
|
|
400
|
-
- Map provider errors to domain
|
|
401
|
-
- Provide context
|
|
402
|
-
- Show user-friendly messages
|
|
403
|
-
- Preserve error information
|
|
404
|
-
|
|
405
|
-
**MUST NOT**:
|
|
406
|
-
- Throw provider errors
|
|
407
|
-
- Expose technical details
|
|
408
|
-
- Lose error context
|
|
409
|
-
- Show stack traces
|
|
410
|
-
- Reveal sensitive data
|
|
411
|
-
|
|
412
|
-
---
|
|
413
|
-
|
|
414
|
-
### Configuration
|
|
415
|
-
|
|
416
|
-
**MUST**:
|
|
417
|
-
- Validate configuration
|
|
418
|
-
- Use appropriate defaults
|
|
419
|
-
- Test with production config
|
|
420
|
-
- Not use dev config in production
|
|
421
|
-
|
|
422
|
-
**MUST NOT**:
|
|
423
|
-
- Skip validation
|
|
424
|
-
- Use invalid config
|
|
425
|
-
- Ignore environment differences
|
|
426
|
-
- Hardcode production values
|
|
427
|
-
|
|
428
|
-
---
|
|
429
|
-
|
|
430
|
-
## Related Modules
|
|
431
|
-
|
|
432
|
-
- **Application** (`../application/README.md`) - Ports and interfaces
|
|
433
|
-
- **Infrastructure** (`../infrastructure/README.md`) - Implementations
|
|
434
|
-
- **Presentation** (`../presentation/README.md`) - UI components
|
|
435
|
-
|
|
436
|
-
---
|
|
437
|
-
|
|
438
|
-
## Entity Documentation
|
|
439
|
-
|
|
440
|
-
### Detailed Entity Docs
|
|
441
|
-
|
|
442
|
-
**AuthUser**: `entities/AuthUser.md`
|
|
443
|
-
**UserProfile**: `entities/UserProfile.md`
|
|
444
|
-
**AuthConfig & AuthError**: `ConfigAndErrors.md`
|
|
@@ -1,79 +0,0 @@
|
|
|
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 AnonymousNameConfig {
|
|
31
|
-
names?: string[];
|
|
32
|
-
prefixes?: string[];
|
|
33
|
-
usePrefixes?: boolean;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Generate a random anonymous name
|
|
38
|
-
* Uses userId to ensure consistency per user
|
|
39
|
-
*/
|
|
40
|
-
export function generateAnonymousName(
|
|
41
|
-
userId?: string,
|
|
42
|
-
config?: AnonymousNameConfig,
|
|
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] ?? "Anonymous";
|
|
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] ?? "Anonymous";
|
|
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 anonymous display name with fallback
|
|
71
|
-
*/
|
|
72
|
-
export function getAnonymousDisplayName(
|
|
73
|
-
userId?: string,
|
|
74
|
-
fallback = 'Anonymous',
|
|
75
|
-
config?: AnonymousNameConfig,
|
|
76
|
-
): string {
|
|
77
|
-
if (!userId) return fallback;
|
|
78
|
-
return generateAnonymousName(userId, config);
|
|
79
|
-
}
|
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
# useUserProfile
|
|
2
|
-
|
|
3
|
-
Hook for fetching and displaying user profile data.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Strategy
|
|
8
|
-
|
|
9
|
-
**Purpose**: Provides formatted user profile data for display in UI components. Handles authenticated vs anonymous user differences automatically.
|
|
10
|
-
|
|
11
|
-
**When to Use**:
|
|
12
|
-
- Displaying user profile information
|
|
13
|
-
- Need user display name and avatar
|
|
14
|
-
- Showing account settings links
|
|
15
|
-
- Differentiating authenticated vs anonymous users
|
|
16
|
-
- Profile headers or user identification
|
|
17
|
-
|
|
18
|
-
**Import Path**:
|
|
19
|
-
```typescript
|
|
20
|
-
import { useUserProfile } from '@umituz/react-native-auth';
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**Hook Location**: `src/presentation/hooks/useUserProfile.ts`
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Return Value
|
|
28
|
-
|
|
29
|
-
### UserProfileData Structure
|
|
30
|
-
|
|
31
|
-
**PROPERTIES**:
|
|
32
|
-
- `displayName: string` - User's display name or generated name
|
|
33
|
-
- `userId: string` - User ID (undefined for anonymous)
|
|
34
|
-
- `isAnonymous: boolean` - Anonymous user status
|
|
35
|
-
- `avatarUrl: string | undefined` - Profile photo URL
|
|
36
|
-
- `accountSettingsRoute: string | undefined` - Navigation route for settings
|
|
37
|
-
|
|
38
|
-
**RETURNS**: `UserProfileData | undefined`
|
|
39
|
-
- `undefined` if no user exists
|
|
40
|
-
- Object with above properties if user exists
|
|
41
|
-
|
|
42
|
-
**Rules**:
|
|
43
|
-
- MUST handle `undefined` return value
|
|
44
|
-
- MUST check `isAnonymous` before showing account actions
|
|
45
|
-
- MUST provide fallback for missing avatar
|
|
46
|
-
- MUST NOT assume all properties exist
|
|
47
|
-
|
|
48
|
-
**Constraints**:
|
|
49
|
-
- Memoized for performance
|
|
50
|
-
- Auto-updates on auth state changes
|
|
51
|
-
- Generates names for anonymous users
|
|
52
|
-
- Uses `useAuth` hook internally
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
## Parameters
|
|
57
|
-
|
|
58
|
-
### Configuration Options
|
|
59
|
-
|
|
60
|
-
**anonymousDisplayName** (optional)
|
|
61
|
-
- Type: `string`
|
|
62
|
-
- Default: `undefined`
|
|
63
|
-
- Purpose: Fallback name for anonymous users
|
|
64
|
-
- Used when: Anonymous user has no custom name
|
|
65
|
-
|
|
66
|
-
**accountRoute** (optional)
|
|
67
|
-
- Type: `string`
|
|
68
|
-
- Default: `undefined`
|
|
69
|
-
- Purpose: Navigation route for account settings
|
|
70
|
-
- Used when: Need to navigate to settings
|
|
71
|
-
|
|
72
|
-
**anonymousNameConfig** (optional)
|
|
73
|
-
- Type: `AnonymousNameConfig`
|
|
74
|
-
- Default: `undefined`
|
|
75
|
-
- Purpose: Customize anonymous name generation
|
|
76
|
-
- Used when: Want custom anonymous naming
|
|
77
|
-
|
|
78
|
-
**Rules**:
|
|
79
|
-
- SHOULD provide `accountRoute` for profile editing
|
|
80
|
-
- MAY provide `anonymousDisplayName` for better UX
|
|
81
|
-
- MAY customize `anonymousNameConfig` for branding
|
|
82
|
-
|
|
83
|
-
**Constraints**:
|
|
84
|
-
- All parameters optional
|
|
85
|
-
- Configuration affects return values
|
|
86
|
-
- Changes trigger re-calculation
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Anonymous User Handling
|
|
91
|
-
|
|
92
|
-
### Strategy
|
|
93
|
-
|
|
94
|
-
**Purpose**: Automatically generate appropriate names for anonymous users.
|
|
95
|
-
|
|
96
|
-
**Rules**:
|
|
97
|
-
- MUST generate random name for anonymous users
|
|
98
|
-
- MUST indicate anonymous status clearly
|
|
99
|
-
- SHOULD encourage account creation
|
|
100
|
-
- MUST NOT hide anonymous nature
|
|
101
|
-
|
|
102
|
-
**MUST NOT**:
|
|
103
|
-
- Show anonymous users as authenticated
|
|
104
|
-
- Use misleading names for anonymous users
|
|
105
|
-
- Hide anonymous limitations
|
|
106
|
-
|
|
107
|
-
### Constraints
|
|
108
|
-
|
|
109
|
-
**NAME GENERATION**:
|
|
110
|
-
- Default format: "User_Witty_Badger_1234"
|
|
111
|
-
- Components: Prefix + Adjectives + Noun + ID
|
|
112
|
-
- Ensures uniqueness with user ID
|
|
113
|
-
- Memoized per anonymous user
|
|
114
|
-
|
|
115
|
-
**CUSTOMIZATION**:
|
|
116
|
-
```typescript
|
|
117
|
-
anonymousNameConfig = {
|
|
118
|
-
prefix: string; // Default: "User"
|
|
119
|
-
adjectiveCount: number; // Default: 2
|
|
120
|
-
nounCount: number; // Default: 1
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
**DISPLAY STRATEGY**:
|
|
125
|
-
- Always show generated name
|
|
126
|
-
- Indicate guest status
|
|
127
|
-
- Offer upgrade path
|
|
128
|
-
- Preserve name across sessions
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## Performance Optimization
|
|
133
|
-
|
|
134
|
-
### Strategy
|
|
135
|
-
|
|
136
|
-
**Purpose**: Efficient profile data computation and updates.
|
|
137
|
-
|
|
138
|
-
**Rules**:
|
|
139
|
-
- MUST memoize computed profile data
|
|
140
|
-
- MUST minimize re-computations
|
|
141
|
-
- MUST use dependency arrays correctly
|
|
142
|
-
- MUST avoid unnecessary recalculations
|
|
143
|
-
|
|
144
|
-
**Constraints**:
|
|
145
|
-
- Uses `useMemo` internally
|
|
146
|
-
- Only recalculates when dependencies change
|
|
147
|
-
- Dependencies: user object, config params
|
|
148
|
-
- No unnecessary re-renders
|
|
149
|
-
|
|
150
|
-
**OPTIMIZATION TECHNIQUES**:
|
|
151
|
-
- Memoized anonymous name generation
|
|
152
|
-
- Cached avatar URL processing
|
|
153
|
-
- Efficient auth state subscription
|
|
154
|
-
- Minimal prop drilling
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Avatar Handling
|
|
159
|
-
|
|
160
|
-
### Strategy
|
|
161
|
-
|
|
162
|
-
**Purpose**: Proper display of user profile photos with fallbacks.
|
|
163
|
-
|
|
164
|
-
**Rules**:
|
|
165
|
-
- MUST handle missing/invalid avatar URLs
|
|
166
|
-
- MUST provide fallback avatar
|
|
167
|
-
- MUST NOT break on missing photos
|
|
168
|
-
- SHOULD show placeholder or initials
|
|
169
|
-
|
|
170
|
-
**MUST NOT**:
|
|
171
|
-
- Show broken image icons
|
|
172
|
-
- Crash on invalid URLs
|
|
173
|
-
- Leave avatar space empty
|
|
174
|
-
|
|
175
|
-
### Constraints
|
|
176
|
-
|
|
177
|
-
**AVATAR URLS**:
|
|
178
|
-
- From Firebase user.photoURL
|
|
179
|
-
- May be `null` or `undefined`
|
|
180
|
-
- May be invalid URL
|
|
181
|
-
- Should be validated before display
|
|
182
|
-
|
|
183
|
-
**FALLBACK OPTIONS**:
|
|
184
|
-
- Show user initials
|
|
185
|
-
- Show generic avatar icon
|
|
186
|
-
- Use placeholder image
|
|
187
|
-
- Generate colored placeholder with initials
|
|
188
|
-
|
|
189
|
-
**DISPLAY RULES**:
|
|
190
|
-
- Use `avatarUrl` if valid and present
|
|
191
|
-
- Fallback to initials placeholder
|
|
192
|
-
- Default to generic icon
|
|
193
|
-
- Consistent sizing across app
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## Navigation Integration
|
|
198
|
-
|
|
199
|
-
### Strategy
|
|
200
|
-
|
|
201
|
-
**Purpose**: Provide navigation paths for profile-related actions.
|
|
202
|
-
|
|
203
|
-
**Rules**:
|
|
204
|
-
- SHOULD provide `accountRoute` parameter
|
|
205
|
-
- MUST handle navigation in parent component
|
|
206
|
-
- MUST NOT hardcode navigation paths
|
|
207
|
-
- MUST support different navigation patterns
|
|
208
|
-
|
|
209
|
-
**Constraints**:
|
|
210
|
-
|
|
211
|
-
**ROUTE HANDLING**:
|
|
212
|
-
- `accountRoute` passed through to return value
|
|
213
|
-
- Parent component uses route for navigation
|
|
214
|
-
- Supports string routes or navigation objects
|
|
215
|
-
- Compatible with React Navigation
|
|
216
|
-
|
|
217
|
-
**INTEGRATION PATTERNS**:
|
|
218
|
-
- Navigate to account settings on press
|
|
219
|
-
- Pass route to navigation component
|
|
220
|
-
- Handle deep linking if needed
|
|
221
|
-
- Support custom navigation handlers
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## Security & Privacy
|
|
226
|
-
|
|
227
|
-
### Strategy
|
|
228
|
-
|
|
229
|
-
**Purpose**: Protect user information while displaying profile.
|
|
230
|
-
|
|
231
|
-
**Rules**:
|
|
232
|
-
- MUST NOT display full user ID in UI
|
|
233
|
-
- MUST NOT expose sensitive tokens
|
|
234
|
-
- MUST handle email display appropriately
|
|
235
|
-
- MUST respect user privacy settings
|
|
236
|
-
|
|
237
|
-
**MUST NOT**:
|
|
238
|
-
- Show internal user identifiers
|
|
239
|
-
- Display raw email publicly
|
|
240
|
-
- Expose authentication tokens
|
|
241
|
-
- Log profile data with sensitive info
|
|
242
|
-
|
|
243
|
-
### Constraints
|
|
244
|
-
|
|
245
|
-
**SAFE TO DISPLAY**:
|
|
246
|
-
- Display name: ✅ Safe
|
|
247
|
-
- Avatar: ✅ Safe
|
|
248
|
-
- Anonymous status: ✅ Safe
|
|
249
|
-
- Email: ⚠️ Only to account owner
|
|
250
|
-
|
|
251
|
-
**NEVER DISPLAY**:
|
|
252
|
-
- User ID (UID)
|
|
253
|
-
- Authentication tokens
|
|
254
|
-
- Session identifiers
|
|
255
|
-
- Internal metadata
|
|
256
|
-
|
|
257
|
-
**PRIVACY CONSIDERATIONS**:
|
|
258
|
-
- User-controlled display name
|
|
259
|
-
- Optional profile photo
|
|
260
|
-
- Anonymous users have more privacy
|
|
261
|
-
- GDPR/CCPA compliant
|
|
262
|
-
|
|
263
|
-
---
|
|
264
|
-
|
|
265
|
-
## Platform Support
|
|
266
|
-
|
|
267
|
-
### Strategy
|
|
268
|
-
|
|
269
|
-
**Purpose**: Consistent behavior across all platforms.
|
|
270
|
-
|
|
271
|
-
**Constraints**:
|
|
272
|
-
- iOS: ✅ Fully supported
|
|
273
|
-
- Android: ✅ Fully supported
|
|
274
|
-
- Web: ✅ Fully supported
|
|
275
|
-
|
|
276
|
-
**PLATFORM-SPECIFIC**:
|
|
277
|
-
- Avatar display: Platform-appropriate components
|
|
278
|
-
- Name formatting: Consistent across platforms
|
|
279
|
-
- Navigation: Platform-specific patterns
|
|
280
|
-
- Fallbacks: Platform-appropriate defaults
|
|
281
|
-
|
|
282
|
-
---
|
|
283
|
-
|
|
284
|
-
## Related Hooks
|
|
285
|
-
|
|
286
|
-
- **`useAuth`** (`src/presentation/hooks/useAuth.ts`) - Core authentication state
|
|
287
|
-
- **`useProfileUpdate`** (`src/presentation/hooks/useProfileUpdate.md`) - Profile editing
|
|
288
|
-
- **`useAccountManagement`** (`src/presentation/hooks/useAccountManagement.md`) - Account operations
|
|
289
|
-
|
|
290
|
-
## Related Components
|
|
291
|
-
|
|
292
|
-
- **`ProfileSection`** (`src/presentation/components/ProfileComponents.md`) - Profile display component
|
|
293
|
-
- **`AccountActions`** (`src/presentation/components/ProfileComponents.md`) - Account management UI
|
|
294
|
-
|
|
295
|
-
## Related Utilities
|
|
296
|
-
|
|
297
|
-
- **`generateAnonymousName`** (`src/presentation/hooks/useUserProfile.ts`) - Anonymous name generation
|