@umituz/react-native-auth 3.4.32 → 3.4.34
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/README.md +347 -348
- package/package.json +2 -3
- package/src/application/README.md +323 -442
- package/src/domain/ConfigAndErrors.md +296 -431
- package/src/domain/README.md +361 -210
- package/src/domain/entities/AuthUser.md +231 -372
- package/src/domain/entities/UserProfile.md +271 -441
- package/src/index.ts +35 -0
- package/src/infrastructure/README.md +388 -444
- package/src/infrastructure/services/README.md +386 -312
- package/src/infrastructure/utils/validation/BaseValidators.ts +35 -0
- package/src/infrastructure/utils/validation/CollectionValidators.ts +56 -0
- package/src/infrastructure/utils/validation/DateValidators.ts +63 -0
- package/src/infrastructure/utils/validation/FormValidators.ts +22 -0
- package/src/infrastructure/utils/validation/NumberValidators.ts +55 -0
- package/src/infrastructure/utils/validation/StringValidators.ts +55 -0
- package/src/infrastructure/utils/validation/sanitization.ts +98 -0
- package/src/infrastructure/utils/validation/types.ts +15 -0
- package/src/presentation/README.md +631 -563
- package/src/presentation/components/ProfileComponents.md +307 -504
- package/src/presentation/components/README.md +254 -92
- package/src/presentation/hooks/README.md +247 -83
- package/src/presentation/hooks/useAccountManagement.md +295 -344
- package/src/presentation/hooks/useAuth.md +271 -227
- package/src/presentation/hooks/useAuthBottomSheet.md +417 -367
- package/src/presentation/hooks/useAuthRequired.md +308 -194
- package/src/presentation/hooks/useProfileUpdate.md +251 -279
- package/src/presentation/hooks/useSocialLogin.md +312 -287
- package/src/presentation/hooks/useUserProfile.md +259 -192
- package/src/presentation/screens/README.md +151 -153
|
@@ -1,575 +1,378 @@
|
|
|
1
1
|
# Profile Components
|
|
2
2
|
|
|
3
|
-
Components for user profile
|
|
4
|
-
|
|
5
|
-
## Components
|
|
6
|
-
|
|
7
|
-
- **[`ProfileSection`](#profilesection)** - Profile display section
|
|
8
|
-
- **[`AccountActions`](#accountactions)** - Account management actions
|
|
3
|
+
Components for displaying user profile information and managing account actions.
|
|
9
4
|
|
|
10
5
|
---
|
|
11
6
|
|
|
12
7
|
## ProfileSection
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
Displays user profile information including avatar, name, and authentication status.
|
|
10
|
+
|
|
11
|
+
### Strategy
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
**Purpose**: Show user profile information with different layouts for authenticated vs anonymous users.
|
|
17
14
|
|
|
15
|
+
**When to Use**:
|
|
16
|
+
- Settings screens showing user info
|
|
17
|
+
- Profile headers in navigation
|
|
18
|
+
- Account management sections
|
|
19
|
+
- User identification in UI
|
|
20
|
+
|
|
21
|
+
**Import Path**:
|
|
18
22
|
```typescript
|
|
19
23
|
import { ProfileSection } from '@umituz/react-native-auth';
|
|
20
|
-
|
|
21
|
-
function SettingsScreen() {
|
|
22
|
-
const profile = useUserProfile({
|
|
23
|
-
accountRoute: 'AccountSettings',
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const navigation = useNavigation();
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<View>
|
|
30
|
-
<ProfileSection
|
|
31
|
-
profile={{
|
|
32
|
-
displayName: profile?.displayName,
|
|
33
|
-
userId: profile?.userId,
|
|
34
|
-
isAnonymous: profile?.isAnonymous || false,
|
|
35
|
-
avatarUrl: profile?.avatarUrl,
|
|
36
|
-
accountSettingsRoute: profile?.accountSettingsRoute,
|
|
37
|
-
}}
|
|
38
|
-
onPress={() => navigation.navigate('EditProfile')}
|
|
39
|
-
onSignIn={() => navigation.navigate('Login')}
|
|
40
|
-
signInText="Sign In"
|
|
41
|
-
anonymousText="Guest"
|
|
42
|
-
/>
|
|
43
|
-
</View>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
24
|
```
|
|
47
25
|
|
|
48
|
-
|
|
26
|
+
**Component Location**: `src/presentation/components/ProfileSection.tsx`
|
|
49
27
|
|
|
50
|
-
|
|
51
|
-
|------|------|----------|-------------|
|
|
52
|
-
| `profile` | `ProfileSectionConfig` | Yes | Profile configuration |
|
|
53
|
-
| `onPress` | `() => void` | No | Press handler (for authenticated users) |
|
|
54
|
-
| `onSignIn` | `() => void` | No | Sign-in handler (for anonymous users) |
|
|
55
|
-
| `signInText` | `string` | No | "Sign In" button text |
|
|
56
|
-
| `anonymousText` | `string` | No | Anonymous user label |
|
|
28
|
+
**Data Hook**: `src/presentation/hooks/useUserProfile.ts`
|
|
57
29
|
|
|
58
|
-
|
|
30
|
+
### Rules
|
|
59
31
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
accountSettingsRoute?: string; // Account settings route
|
|
67
|
-
benefits?: string[]; // Benefits list
|
|
68
|
-
}
|
|
69
|
-
```
|
|
32
|
+
**MUST**:
|
|
33
|
+
- Pass profile object with required fields
|
|
34
|
+
- Provide different handlers for authenticated vs anonymous users
|
|
35
|
+
- Show avatar fallback if no photo available
|
|
36
|
+
- Display authentication status clearly
|
|
37
|
+
- Handle navigation to profile editing
|
|
70
38
|
|
|
71
|
-
|
|
39
|
+
**MUST NOT**:
|
|
40
|
+
- Show sensitive information (user ID, email publicly)
|
|
41
|
+
- Allow anonymous users to access profile editing
|
|
42
|
+
- Display generic avatar for authenticated users without photo
|
|
43
|
+
- Hardcode profile data (use useUserProfile hook)
|
|
72
44
|
|
|
73
|
-
|
|
45
|
+
### Constraints
|
|
74
46
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
displayName: user?.displayName || user?.email,
|
|
81
|
-
userId: user?.uid,
|
|
82
|
-
isAnonymous: false,
|
|
83
|
-
avatarUrl: user?.photoURL,
|
|
84
|
-
accountSettingsRoute: 'AccountSettings',
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const navigation = useNavigation();
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<ProfileSection
|
|
91
|
-
profile={profile}
|
|
92
|
-
onPress={() => navigation.navigate('EditProfile')}
|
|
93
|
-
/>
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
```
|
|
47
|
+
**AUTHENTICATED USER DISPLAY**:
|
|
48
|
+
- Show display name or email
|
|
49
|
+
- Show avatar if available
|
|
50
|
+
- Show "Edit Profile" or settings access
|
|
51
|
+
- Hide authentication prompts
|
|
97
52
|
|
|
98
|
-
|
|
53
|
+
**ANONYMOUS USER DISPLAY**:
|
|
54
|
+
- Show "Guest" or anonymous label
|
|
55
|
+
- Show generic placeholder avatar
|
|
56
|
+
- Show "Sign In" or "Create Account" prompt
|
|
57
|
+
- Hide profile editing options
|
|
99
58
|
|
|
59
|
+
**REQUIRED PROPERTIES**:
|
|
100
60
|
```typescript
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
userId: undefined,
|
|
107
|
-
isAnonymous: true,
|
|
108
|
-
avatarUrl: undefined,
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const navigation = useNavigation();
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<ProfileSection
|
|
115
|
-
profile={profile}
|
|
116
|
-
onSignIn={() => navigation.navigate('Login')}
|
|
117
|
-
signInText="Sign In"
|
|
118
|
-
/>
|
|
119
|
-
);
|
|
61
|
+
{
|
|
62
|
+
displayName?: string;
|
|
63
|
+
isAnonymous: boolean;
|
|
64
|
+
avatarUrl?: string;
|
|
65
|
+
accountSettingsRoute?: string;
|
|
120
66
|
}
|
|
121
67
|
```
|
|
122
68
|
|
|
123
|
-
|
|
69
|
+
**PLATFORM SUPPORT**:
|
|
70
|
+
- iOS: ✅ Fully supported
|
|
71
|
+
- Android: ✅ Fully supported
|
|
72
|
+
- Web: ✅ Fully supported
|
|
124
73
|
|
|
125
|
-
|
|
126
|
-
function PremiumProfileSection() {
|
|
127
|
-
const { user } = useAuth();
|
|
128
|
-
|
|
129
|
-
const profile = {
|
|
130
|
-
displayName: user?.displayName,
|
|
131
|
-
userId: user?.uid,
|
|
132
|
-
isAnonymous: false,
|
|
133
|
-
avatarUrl: user?.photoURL,
|
|
134
|
-
benefits: [
|
|
135
|
-
'Access to premium content',
|
|
136
|
-
'Ad-free experience',
|
|
137
|
-
'Exclusive discounts',
|
|
138
|
-
],
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
return <ProfileSection profile={profile} />;
|
|
142
|
-
}
|
|
143
|
-
```
|
|
74
|
+
---
|
|
144
75
|
|
|
145
|
-
|
|
76
|
+
## AccountActions
|
|
146
77
|
|
|
147
|
-
|
|
148
|
-
function DynamicProfileSection() {
|
|
149
|
-
const { user } = useAuth();
|
|
150
|
-
const navigation = useNavigation();
|
|
151
|
-
const profile = useUserProfile();
|
|
152
|
-
|
|
153
|
-
const handlePress = () => {
|
|
154
|
-
if (profile?.isAnonymous) {
|
|
155
|
-
navigation.navigate('Login');
|
|
156
|
-
} else {
|
|
157
|
-
navigation.navigate('EditProfile');
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
return (
|
|
162
|
-
<ProfileSection
|
|
163
|
-
profile={{
|
|
164
|
-
displayName: profile?.displayName,
|
|
165
|
-
userId: profile?.userId,
|
|
166
|
-
isAnonymous: profile?.isAnonymous || false,
|
|
167
|
-
avatarUrl: profile?.avatarUrl,
|
|
168
|
-
benefits: profile?.isAnonymous
|
|
169
|
-
? ['Create an account to access more features']
|
|
170
|
-
: ['Get premium membership', 'Access exclusive content'],
|
|
171
|
-
}}
|
|
172
|
-
onPress={handlePress}
|
|
173
|
-
/>
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
```
|
|
78
|
+
Provides buttons for account management operations like sign out and account deletion.
|
|
177
79
|
|
|
178
|
-
|
|
80
|
+
### Strategy
|
|
179
81
|
|
|
180
|
-
|
|
181
|
-
function ProfileSectionWithCustomAvatar() {
|
|
182
|
-
const { user } = useAuth();
|
|
183
|
-
const navigation = useNavigation();
|
|
184
|
-
|
|
185
|
-
const profile = {
|
|
186
|
-
displayName: user?.displayName || 'User',
|
|
187
|
-
userId: user?.uid,
|
|
188
|
-
isAnonymous: user?.isAnonymous || false,
|
|
189
|
-
avatarUrl: user?.photoURL || 'https://example.com/default-avatar.png',
|
|
190
|
-
accountSettingsRoute: 'AccountSettings',
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return (
|
|
194
|
-
<ProfileSection
|
|
195
|
-
profile={profile}
|
|
196
|
-
onPress={() => navigation.navigate('EditProfile')}
|
|
197
|
-
/>
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
```
|
|
82
|
+
**Purpose**: Safe account management with proper confirmations and error handling.
|
|
201
83
|
|
|
202
|
-
|
|
84
|
+
**When to Use**:
|
|
85
|
+
- Account settings screens
|
|
86
|
+
- Logout functionality
|
|
87
|
+
- Account deletion flows
|
|
88
|
+
- Password change access
|
|
203
89
|
|
|
90
|
+
**Import Path**:
|
|
204
91
|
```typescript
|
|
205
|
-
|
|
206
|
-
const profile = useUserProfile();
|
|
207
|
-
const navigation = useNavigation();
|
|
208
|
-
|
|
209
|
-
return (
|
|
210
|
-
<View>
|
|
211
|
-
<ProfileSection
|
|
212
|
-
profile={{
|
|
213
|
-
displayName: profile?.displayName,
|
|
214
|
-
userId: profile?.userId,
|
|
215
|
-
isAnonymous: profile?.isAnonymous || false,
|
|
216
|
-
avatarUrl: profile?.avatarUrl,
|
|
217
|
-
accountSettingsRoute: 'AccountSettings',
|
|
218
|
-
}}
|
|
219
|
-
onPress={() => navigation.navigate('EditProfile')}
|
|
220
|
-
/>
|
|
221
|
-
<TouchableOpacity
|
|
222
|
-
style={styles.editButton}
|
|
223
|
-
onPress={() => navigation.navigate('EditProfile')}
|
|
224
|
-
>
|
|
225
|
-
<Text>Edit Profile</Text>
|
|
226
|
-
</TouchableOpacity>
|
|
227
|
-
</View>
|
|
228
|
-
);
|
|
229
|
-
}
|
|
92
|
+
import { AccountActions } from '@umituz/react-native-auth';
|
|
230
93
|
```
|
|
231
94
|
|
|
95
|
+
**Component Location**: `src/presentation/components/AccountActions.tsx`
|
|
96
|
+
|
|
97
|
+
**Management Hook**: `src/presentation/hooks/useAccountManagement.ts`
|
|
98
|
+
|
|
99
|
+
### Rules
|
|
100
|
+
|
|
101
|
+
**MUST**:
|
|
102
|
+
- Require confirmation before sign out
|
|
103
|
+
- Require confirmation before account deletion
|
|
104
|
+
- Show clear warnings for account deletion
|
|
105
|
+
- Provide error messages for failures
|
|
106
|
+
- Handle loading states during operations
|
|
107
|
+
- Hide deletion option for anonymous users
|
|
108
|
+
|
|
109
|
+
**MUST NOT**:
|
|
110
|
+
- Allow account deletion without confirmation
|
|
111
|
+
- Delete account without recent authentication
|
|
112
|
+
- Show account actions to anonymous users
|
|
113
|
+
- Allow immediate destructive actions
|
|
114
|
+
- Expose internal error messages
|
|
115
|
+
|
|
116
|
+
### Constraints
|
|
117
|
+
|
|
118
|
+
**SIGN OUT REQUIREMENTS**:
|
|
119
|
+
- Confirmation dialog required
|
|
120
|
+
- Clear sign-out message
|
|
121
|
+
- Cancel option must be available
|
|
122
|
+
- Non-destructive (can sign back in)
|
|
123
|
+
|
|
124
|
+
**ACCOUNT DELETION REQUIREMENTS**:
|
|
125
|
+
- Double confirmation required (warning + confirm)
|
|
126
|
+
- Clear warning about irreversibility
|
|
127
|
+
- Recent authentication required (Firebase requirement)
|
|
128
|
+
- Error handling for re-authentication failures
|
|
129
|
+
- Cannot delete anonymous accounts
|
|
130
|
+
|
|
131
|
+
**PASSWORD CHANGE**:
|
|
132
|
+
- Optional feature (configurable)
|
|
133
|
+
- Only for email/password users
|
|
134
|
+
- Requires current password
|
|
135
|
+
- Not available for social auth users
|
|
136
|
+
|
|
137
|
+
**OPERATION SAFETY**:
|
|
138
|
+
- Disable buttons during operation
|
|
139
|
+
- Show loading indicators
|
|
140
|
+
- Prevent concurrent operations
|
|
141
|
+
- Allow retry on failure
|
|
142
|
+
- Log security events
|
|
143
|
+
|
|
232
144
|
---
|
|
233
145
|
|
|
234
|
-
##
|
|
146
|
+
## Anonymous User Handling
|
|
235
147
|
|
|
236
|
-
|
|
148
|
+
### Strategy
|
|
237
149
|
|
|
238
|
-
|
|
150
|
+
**Purpose**: Differentiate experience between authenticated and anonymous users appropriately.
|
|
239
151
|
|
|
240
|
-
|
|
241
|
-
import { AccountActions } from '@umituz/react-native-auth';
|
|
152
|
+
### Rules
|
|
242
153
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
logoutText: 'Sign Out',
|
|
249
|
-
deleteAccountText: 'Delete Account',
|
|
250
|
-
changePasswordText: 'Change Password',
|
|
251
|
-
logoutConfirmTitle: 'Sign Out',
|
|
252
|
-
logoutConfirmMessage: 'Are you sure you want to sign out?',
|
|
253
|
-
deleteConfirmTitle: 'Delete Account',
|
|
254
|
-
deleteConfirmMessage: 'This action cannot be undone. Continue?',
|
|
255
|
-
deleteErrorTitle: 'Error',
|
|
256
|
-
deleteErrorMessage: 'Account could not be deleted. Please try again.',
|
|
257
|
-
onLogout: async () => {
|
|
258
|
-
await logout();
|
|
259
|
-
navigation.replace('Login');
|
|
260
|
-
},
|
|
261
|
-
onDeleteAccount: async () => {
|
|
262
|
-
await deleteAccount();
|
|
263
|
-
navigation.replace('Login');
|
|
264
|
-
},
|
|
265
|
-
showChangePassword: true,
|
|
266
|
-
onChangePassword: () => {
|
|
267
|
-
navigation.navigate('ChangePassword');
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
return (
|
|
272
|
-
<View>
|
|
273
|
-
<AccountActions config={config} />
|
|
274
|
-
</View>
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
```
|
|
154
|
+
**MUST**:
|
|
155
|
+
- Hide account deletion for anonymous users
|
|
156
|
+
- Show "Create Account" prompt instead of sign out
|
|
157
|
+
- Indicate guest status clearly
|
|
158
|
+
- Guide anonymous users toward registration
|
|
278
159
|
|
|
279
|
-
|
|
160
|
+
**MUST NOT**:
|
|
161
|
+
- Show account deletion to anonymous users
|
|
162
|
+
- Allow sign out of anonymous session (unless upgrading)
|
|
163
|
+
- Treat anonymous users as authenticated
|
|
164
|
+
- Hide anonymous status from user
|
|
280
165
|
|
|
281
|
-
|
|
282
|
-
|------|------|----------|-------------|
|
|
283
|
-
| `config` | `AccountActionsConfig` | Yes | Account actions configuration |
|
|
166
|
+
### Constraints
|
|
284
167
|
|
|
285
|
-
|
|
168
|
+
**ANONYMOUS USER LIMITATIONS**:
|
|
169
|
+
- Cannot delete anonymous account
|
|
170
|
+
- Cannot change password (no password set)
|
|
171
|
+
- Cannot access profile editing
|
|
172
|
+
- Limited account settings access
|
|
286
173
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
logoutConfirmTitle: string; // Sign out confirmation title
|
|
293
|
-
logoutConfirmMessage: string; // Sign out confirmation message
|
|
294
|
-
deleteConfirmTitle: string; // Delete confirmation title
|
|
295
|
-
deleteConfirmMessage: string; // Delete confirmation message
|
|
296
|
-
deleteErrorTitle?: string; // Delete error title
|
|
297
|
-
deleteErrorMessage?: string; // Delete error message
|
|
298
|
-
onLogout: () => Promise<void>; // Sign out handler
|
|
299
|
-
onDeleteAccount: () => Promise<void>; // Delete handler
|
|
300
|
-
onChangePassword?: () => void; // Change password handler
|
|
301
|
-
showChangePassword?: boolean; // Show change password button
|
|
302
|
-
}
|
|
303
|
-
```
|
|
174
|
+
**UPGRADE PATH**:
|
|
175
|
+
- Anonymous → Registered: Link credentials
|
|
176
|
+
- Preserves anonymous account data
|
|
177
|
+
- Requires email/password or social auth
|
|
178
|
+
- Seamless transition for user
|
|
304
179
|
|
|
305
|
-
|
|
180
|
+
---
|
|
306
181
|
|
|
307
|
-
|
|
182
|
+
## Security & Privacy
|
|
308
183
|
|
|
309
|
-
|
|
310
|
-
function SimpleAccountActions() {
|
|
311
|
-
const { logout, deleteAccount } = useAccountManagement();
|
|
312
|
-
|
|
313
|
-
const config = {
|
|
314
|
-
logoutText: 'Sign Out',
|
|
315
|
-
deleteAccountText: 'Delete Account',
|
|
316
|
-
logoutConfirmTitle: 'Sign Out',
|
|
317
|
-
logoutConfirmMessage: 'Are you sure you want to sign out?',
|
|
318
|
-
deleteConfirmTitle: 'Delete Account',
|
|
319
|
-
deleteConfirmMessage: 'Are you sure you want to delete your account?',
|
|
320
|
-
onLogout: logout,
|
|
321
|
-
onDeleteAccount: deleteAccount,
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
return <AccountActions config={config} />;
|
|
325
|
-
}
|
|
326
|
-
```
|
|
184
|
+
### Strategy
|
|
327
185
|
|
|
328
|
-
|
|
186
|
+
**Purpose**: Protect user information and prevent unauthorized account access.
|
|
329
187
|
|
|
330
|
-
|
|
331
|
-
function AccountActionsWithPasswordChange() {
|
|
332
|
-
const { logout, deleteAccount } = useAccountManagement();
|
|
333
|
-
const navigation = useNavigation();
|
|
334
|
-
|
|
335
|
-
const config = {
|
|
336
|
-
logoutText: 'Sign Out',
|
|
337
|
-
deleteAccountText: 'Delete Account',
|
|
338
|
-
changePasswordText: 'Change Password',
|
|
339
|
-
logoutConfirmTitle: 'Sign Out',
|
|
340
|
-
logoutConfirmMessage: 'Are you sure you want to sign out?',
|
|
341
|
-
deleteConfirmTitle: 'Delete Account',
|
|
342
|
-
deleteConfirmMessage: 'Are you sure you want to delete your account?',
|
|
343
|
-
onLogout: async () => {
|
|
344
|
-
await logout();
|
|
345
|
-
navigation.replace('Login');
|
|
346
|
-
},
|
|
347
|
-
onDeleteAccount: async () => {
|
|
348
|
-
await deleteAccount();
|
|
349
|
-
navigation.replace('Login');
|
|
350
|
-
},
|
|
351
|
-
showChangePassword: true,
|
|
352
|
-
onChangePassword: () => {
|
|
353
|
-
navigation.navigate('ChangePassword');
|
|
354
|
-
},
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
return <AccountActions config={config} />;
|
|
358
|
-
}
|
|
359
|
-
```
|
|
188
|
+
### Rules
|
|
360
189
|
|
|
361
|
-
|
|
190
|
+
**MUST**:
|
|
191
|
+
- Never display full user ID in UI
|
|
192
|
+
- Never expose sensitive tokens
|
|
193
|
+
- Require recent auth for destructive actions
|
|
194
|
+
- Log account management events
|
|
195
|
+
- Validate permissions before actions
|
|
362
196
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
logoutText: 'Sign Out',
|
|
370
|
-
deleteAccountText: 'Delete Account',
|
|
371
|
-
logoutConfirmTitle: 'Sign Out',
|
|
372
|
-
logoutConfirmMessage: 'Are you sure you want to sign out?',
|
|
373
|
-
deleteConfirmTitle: 'Delete Account',
|
|
374
|
-
deleteConfirmMessage: 'This action cannot be undone. Continue?',
|
|
375
|
-
deleteErrorTitle: 'Account Deletion Failed',
|
|
376
|
-
deleteErrorMessage: 'An error occurred while deleting your account. Please try again later or contact support.',
|
|
377
|
-
onLogout: async () => {
|
|
378
|
-
try {
|
|
379
|
-
await logout();
|
|
380
|
-
navigation.replace('Login');
|
|
381
|
-
} catch (error) {
|
|
382
|
-
Alert.alert('Error', 'Failed to sign out');
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
onDeleteAccount: async () => {
|
|
386
|
-
try {
|
|
387
|
-
await deleteAccount();
|
|
388
|
-
Alert.alert('Success', 'Your account has been deleted');
|
|
389
|
-
navigation.replace('Login');
|
|
390
|
-
} catch (error) {
|
|
391
|
-
// Error is automatically shown (deleteErrorMessage)
|
|
392
|
-
throw error;
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
return <AccountActions config={config} />;
|
|
398
|
-
}
|
|
399
|
-
```
|
|
197
|
+
**MUST NOT**:
|
|
198
|
+
- Show user ID or internal identifiers
|
|
199
|
+
- Display raw email publicly
|
|
200
|
+
- Allow account deletion without re-auth
|
|
201
|
+
- Skip confirmation dialogs
|
|
202
|
+
- Log sensitive data
|
|
400
203
|
|
|
401
|
-
|
|
204
|
+
### Constraints
|
|
402
205
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
return (
|
|
409
|
-
<Button onPress={() => navigation.navigate('Register')}>
|
|
410
|
-
Create Account
|
|
411
|
-
</Button>
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const config = {
|
|
416
|
-
logoutText: 'Sign Out',
|
|
417
|
-
deleteAccountText: 'Delete Account',
|
|
418
|
-
logoutConfirmTitle: 'Sign Out',
|
|
419
|
-
logoutConfirmMessage: 'Are you sure you want to sign out?',
|
|
420
|
-
deleteConfirmTitle: 'Delete Account',
|
|
421
|
-
deleteConfirmMessage: 'Are you sure you want to delete your account?',
|
|
422
|
-
onLogout: logout,
|
|
423
|
-
onDeleteAccount: deleteAccount,
|
|
424
|
-
};
|
|
425
|
-
|
|
426
|
-
return <AccountActions config={config} />;
|
|
427
|
-
}
|
|
428
|
-
```
|
|
206
|
+
**INFORMATION DISPLAY**:
|
|
207
|
+
- Display name: OK
|
|
208
|
+
- Email: Only to account owner
|
|
209
|
+
- User ID: Never in UI
|
|
210
|
+
- Auth tokens: Never in logs
|
|
429
211
|
|
|
430
|
-
|
|
212
|
+
**RE-AUTHENTICATION**:
|
|
213
|
+
- Required for: Account deletion, password change
|
|
214
|
+
- Timeout: Usually 5 minutes in Firebase
|
|
215
|
+
- Methods: Re-sign in with existing credentials
|
|
216
|
+
- Failure: Block destructive action
|
|
431
217
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
const config = {
|
|
438
|
-
logoutText: isLoading ? 'Signing out...' : 'Sign Out',
|
|
439
|
-
deleteAccountText: isDeletingAccount ? 'Deleting...' : 'Delete Account',
|
|
440
|
-
logoutConfirmTitle: 'Sign Out',
|
|
441
|
-
logoutConfirmMessage: 'Are you sure?',
|
|
442
|
-
deleteConfirmTitle: 'Delete Account',
|
|
443
|
-
deleteConfirmMessage: 'This cannot be undone. Continue?',
|
|
444
|
-
onLogout: async () => {
|
|
445
|
-
await logout();
|
|
446
|
-
navigation.replace('Login');
|
|
447
|
-
},
|
|
448
|
-
onDeleteAccount: async () => {
|
|
449
|
-
await deleteAccount();
|
|
450
|
-
navigation.replace('Login');
|
|
451
|
-
},
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
return (
|
|
455
|
-
<AccountActions
|
|
456
|
-
config={config}
|
|
457
|
-
isLoading={isLoading}
|
|
458
|
-
isDeletingAccount={isDeletingAccount}
|
|
459
|
-
/>
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
```
|
|
218
|
+
**EVENT LOGGING**:
|
|
219
|
+
- Log: Account views, settings access
|
|
220
|
+
- Log: Sign out, deletion attempts
|
|
221
|
+
- Never log: Passwords, tokens, full emails
|
|
222
|
+
- Purpose: Security audit, debugging
|
|
463
223
|
|
|
464
|
-
|
|
224
|
+
---
|
|
465
225
|
|
|
466
|
-
|
|
467
|
-
function AccountActionsWithAnalytics() {
|
|
468
|
-
const { logout, deleteAccount } = useAccountManagement();
|
|
469
|
-
const analytics = useAnalytics();
|
|
470
|
-
|
|
471
|
-
const config = {
|
|
472
|
-
logoutText: 'Sign Out',
|
|
473
|
-
deleteAccountText: 'Delete Account',
|
|
474
|
-
logoutConfirmTitle: 'Sign Out',
|
|
475
|
-
logoutConfirmMessage: 'Are you sure?',
|
|
476
|
-
deleteConfirmTitle: 'Delete Account',
|
|
477
|
-
deleteConfirmMessage: 'This cannot be undone.',
|
|
478
|
-
onLogout: async () => {
|
|
479
|
-
analytics.trackEvent('account_logout');
|
|
480
|
-
await logout();
|
|
481
|
-
},
|
|
482
|
-
onDeleteAccount: async () => {
|
|
483
|
-
analytics.trackEvent('account_delete_initiated');
|
|
484
|
-
await deleteAccount();
|
|
485
|
-
analytics.trackEvent('account_delete_completed');
|
|
486
|
-
},
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
return <AccountActions config={config} />;
|
|
490
|
-
}
|
|
491
|
-
```
|
|
226
|
+
## Navigation Integration
|
|
492
227
|
|
|
493
|
-
|
|
228
|
+
### Strategy
|
|
494
229
|
|
|
495
|
-
|
|
496
|
-
function AccountSettingsScreen() {
|
|
497
|
-
const profile = useUserProfile();
|
|
498
|
-
const { logout, deleteAccount } = useAccountManagement();
|
|
499
|
-
const navigation = useNavigation();
|
|
500
|
-
|
|
501
|
-
return (
|
|
502
|
-
<ScrollView>
|
|
503
|
-
{/* Profile section */}
|
|
504
|
-
<ProfileSection
|
|
505
|
-
profile={{
|
|
506
|
-
displayName: profile?.displayName,
|
|
507
|
-
userId: profile?.userId,
|
|
508
|
-
isAnonymous: profile?.isAnonymous || false,
|
|
509
|
-
avatarUrl: profile?.avatarUrl,
|
|
510
|
-
}}
|
|
511
|
-
onPress={() => navigation.navigate('EditProfile')}
|
|
512
|
-
/>
|
|
513
|
-
|
|
514
|
-
{/* Account actions */}
|
|
515
|
-
{!profile?.isAnonymous && (
|
|
516
|
-
<AccountActions
|
|
517
|
-
config={{
|
|
518
|
-
logoutText: 'Sign Out',
|
|
519
|
-
deleteAccountText: 'Delete Account',
|
|
520
|
-
logoutConfirmTitle: 'Sign Out',
|
|
521
|
-
logoutConfirmMessage: 'Are you sure you want to sign out?',
|
|
522
|
-
deleteConfirmTitle: 'Delete Account',
|
|
523
|
-
deleteConfirmMessage: 'This action cannot be undone. Continue?',
|
|
524
|
-
onLogout: async () => {
|
|
525
|
-
await logout();
|
|
526
|
-
navigation.replace('Login');
|
|
527
|
-
},
|
|
528
|
-
onDeleteAccount: async () => {
|
|
529
|
-
await deleteAccount();
|
|
530
|
-
navigation.replace('Login');
|
|
531
|
-
},
|
|
532
|
-
}}
|
|
533
|
-
/>
|
|
534
|
-
)}
|
|
535
|
-
</ScrollView>
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
```
|
|
230
|
+
**Purpose**: Proper navigation flow for profile-related screens.
|
|
539
231
|
|
|
540
|
-
|
|
232
|
+
### Rules
|
|
541
233
|
|
|
542
|
-
|
|
234
|
+
**MUST**:
|
|
235
|
+
- Provide navigation callbacks for all actions
|
|
236
|
+
- Handle back navigation properly
|
|
237
|
+
- Pass profile data to edit screens
|
|
238
|
+
- Return to proper screen after actions
|
|
543
239
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
240
|
+
**MUST NOT**:
|
|
241
|
+
- Hardcode navigation paths
|
|
242
|
+
- Break back navigation stack
|
|
243
|
+
- Lose unsaved changes
|
|
244
|
+
- Leave modals open after actions
|
|
245
|
+
|
|
246
|
+
### Constraints
|
|
247
|
+
|
|
248
|
+
**NAVIGATION FLOWS**:
|
|
249
|
+
- Profile → Edit Profile → Back to Profile
|
|
250
|
+
- Profile → Account Settings → Back to Profile
|
|
251
|
+
- Sign Out → Login Screen (replace stack)
|
|
252
|
+
- Delete Account → Welcome/Login (replace stack)
|
|
253
|
+
|
|
254
|
+
**STACK MANAGEMENT**:
|
|
255
|
+
- Sign out: Replace entire stack
|
|
256
|
+
- Delete account: Replace entire stack
|
|
257
|
+
- Edit profile: Push onto stack
|
|
258
|
+
- Settings: Push onto stack
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Error Handling
|
|
263
|
+
|
|
264
|
+
### Strategy
|
|
265
|
+
|
|
266
|
+
**Purpose**: Clear user feedback for account action failures.
|
|
267
|
+
|
|
268
|
+
### Rules
|
|
269
|
+
|
|
270
|
+
**MUST**:
|
|
271
|
+
- Show user-friendly error messages
|
|
272
|
+
- Provide retry options after failures
|
|
273
|
+
- Distinguish error types clearly
|
|
274
|
+
- Guide users to resolution
|
|
275
|
+
- Log errors for debugging
|
|
555
276
|
|
|
556
|
-
|
|
277
|
+
**MUST NOT**:
|
|
278
|
+
- Show raw error codes to users
|
|
279
|
+
- Expose technical details
|
|
280
|
+
- Block retry indefinitely
|
|
281
|
+
- Crash on errors
|
|
557
282
|
|
|
558
|
-
|
|
283
|
+
### Constraints
|
|
559
284
|
|
|
560
|
-
|
|
561
|
-
-
|
|
562
|
-
-
|
|
563
|
-
-
|
|
564
|
-
-
|
|
285
|
+
**ERROR CATEGORIES**:
|
|
286
|
+
- Network errors: "Check your connection"
|
|
287
|
+
- Re-auth required: "Please sign in again"
|
|
288
|
+
- Permission denied: "You don't have permission"
|
|
289
|
+
- Rate limited: "Please try again later"
|
|
565
290
|
|
|
566
|
-
|
|
291
|
+
**RECOVERY OPTIONS**:
|
|
292
|
+
- Retry button for temporary failures
|
|
293
|
+
- Sign-in prompt for re-auth
|
|
294
|
+
- Support contact for persistent issues
|
|
295
|
+
- Graceful degradation
|
|
567
296
|
|
|
568
|
-
|
|
569
|
-
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Design System Integration
|
|
300
|
+
|
|
301
|
+
### Strategy
|
|
302
|
+
|
|
303
|
+
**Purpose**: Consistent styling with application design system.
|
|
304
|
+
|
|
305
|
+
### Rules
|
|
306
|
+
|
|
307
|
+
**MUST**:
|
|
308
|
+
- Use design system color tokens
|
|
309
|
+
- Follow design system spacing
|
|
310
|
+
- Use design system typography
|
|
311
|
+
- Match design system component styles
|
|
312
|
+
- Respect design system dark mode
|
|
313
|
+
|
|
314
|
+
**MUST NOT**:
|
|
315
|
+
- Hardcode colors or sizes
|
|
316
|
+
- Use custom styles outside system
|
|
317
|
+
- Break responsive layouts
|
|
318
|
+
- Ignore accessibility tokens
|
|
319
|
+
|
|
320
|
+
### Constraints
|
|
321
|
+
|
|
322
|
+
**STYLE TOKENS**:
|
|
323
|
+
- Colors: Primary, danger, background, text
|
|
324
|
+
- Spacing: Consistent gaps and padding
|
|
325
|
+
- Typography: System fonts and sizes
|
|
326
|
+
- Icons: Design system icon set
|
|
327
|
+
- Avatar sizes: Defined in system
|
|
328
|
+
|
|
329
|
+
**DARK MODE**:
|
|
330
|
+
- Automatically support dark mode
|
|
331
|
+
- Use system color tokens
|
|
332
|
+
- Test contrast in both modes
|
|
333
|
+
- No hardcoded colors
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Accessibility Requirements
|
|
338
|
+
|
|
339
|
+
### Strategy
|
|
340
|
+
|
|
341
|
+
**Purpose**: Ensure profile components are accessible to all users.
|
|
342
|
+
|
|
343
|
+
### Rules
|
|
344
|
+
|
|
345
|
+
**MUST**:
|
|
346
|
+
- Provide accessibility labels for buttons
|
|
347
|
+
- Announce state changes to screen readers
|
|
348
|
+
- Support keyboard navigation (web)
|
|
349
|
+
- Maintain proper focus order
|
|
350
|
+
- Use semantic HTML elements (web)
|
|
351
|
+
|
|
352
|
+
**MUST NOT**:
|
|
353
|
+
- Rely on color alone for meaning
|
|
354
|
+
- Hide important information from screen readers
|
|
355
|
+
- Break keyboard navigation
|
|
356
|
+
- Use low contrast colors
|
|
357
|
+
|
|
358
|
+
### Constraints
|
|
359
|
+
|
|
360
|
+
**SCREEN READER**:
|
|
361
|
+
- Announce user name and status
|
|
362
|
+
- Announce button actions clearly
|
|
363
|
+
- Provide hints for destructive actions
|
|
364
|
+
- Announce loading states
|
|
365
|
+
|
|
366
|
+
**VISUAL ACCESSIBILITY**:
|
|
367
|
+
- Minimum contrast: 4.5:1 for text
|
|
368
|
+
- Touch targets: 44x44px minimum
|
|
369
|
+
- Clear focus indicators
|
|
370
|
+
- Not color-dependent
|
|
371
|
+
|
|
372
|
+
---
|
|
570
373
|
|
|
571
374
|
## Related Hooks
|
|
572
375
|
|
|
573
|
-
-
|
|
574
|
-
-
|
|
575
|
-
-
|
|
376
|
+
- **`useUserProfile`** (`src/presentation/hooks/useUserProfile.ts`) - Profile data management
|
|
377
|
+
- **`useAccountManagement`** (`src/presentation/hooks/useAccountManagement.ts`) - Account operations
|
|
378
|
+
- **`useAuth`** (`src/presentation/hooks/useAuth.ts`) - Authentication state
|