@umituz/react-native-auth 3.4.30 → 3.4.32
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 +426 -0
- package/package.json +1 -3
- package/src/application/README.md +513 -0
- package/src/domain/ConfigAndErrors.md +545 -0
- package/src/domain/README.md +293 -0
- package/src/domain/entities/AuthUser.md +377 -0
- package/src/domain/entities/UserProfile.md +443 -0
- package/src/infrastructure/README.md +576 -0
- package/src/infrastructure/services/README.md +417 -0
- package/src/presentation/README.md +770 -0
- package/src/presentation/components/AuthBackground.tsx +21 -0
- package/src/presentation/components/AuthContainer.tsx +3 -3
- package/src/presentation/components/LoginForm.md +222 -0
- package/src/presentation/components/PasswordIndicators.md +260 -0
- package/src/presentation/components/ProfileComponents.md +575 -0
- package/src/presentation/components/README.md +117 -0
- package/src/presentation/components/SocialLoginButtons.md +340 -0
- package/src/presentation/hooks/README.md +122 -0
- package/src/presentation/hooks/useAccountManagement.md +386 -0
- package/src/presentation/hooks/useAuth.md +255 -0
- package/src/presentation/hooks/useAuthBottomSheet.md +414 -0
- package/src/presentation/hooks/useAuthRequired.md +248 -0
- package/src/presentation/hooks/useProfileUpdate.md +327 -0
- package/src/presentation/hooks/useSocialLogin.md +356 -0
- package/src/presentation/hooks/useUserProfile.md +230 -0
- package/src/presentation/screens/README.md +198 -0
- package/src/presentation/components/AuthGradientBackground.tsx +0 -33
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# useAccountManagement
|
|
2
|
+
|
|
3
|
+
Hook for account management operations (logout, delete account).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Sign out functionality
|
|
8
|
+
- Account deletion with reauthentication
|
|
9
|
+
- Reauthentication callback support
|
|
10
|
+
- Loading state management
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { useAccountManagement } from '@umituz/react-native-auth';
|
|
16
|
+
|
|
17
|
+
function AccountSettings() {
|
|
18
|
+
const { logout, deleteAccount, isLoading, isDeletingAccount } = useAccountManagement({
|
|
19
|
+
onReauthRequired: async () => {
|
|
20
|
+
// Show Google/Apple sign-in UI
|
|
21
|
+
const result = await reauthenticateWithGoogle();
|
|
22
|
+
return result.success;
|
|
23
|
+
},
|
|
24
|
+
onPasswordRequired: async () => {
|
|
25
|
+
// Show password prompt
|
|
26
|
+
const password = await showPasswordPrompt();
|
|
27
|
+
return password;
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<View>
|
|
33
|
+
<Button onPress={logout}>Sign Out</Button>
|
|
34
|
+
<Button onPress={deleteAccount}>Delete Account</Button>
|
|
35
|
+
</View>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## API
|
|
41
|
+
|
|
42
|
+
### Parameters
|
|
43
|
+
|
|
44
|
+
| Param | Type | Required | Description |
|
|
45
|
+
|-------|------|----------|-------------|
|
|
46
|
+
| `onReauthRequired` | `() => Promise<boolean>` | No | Callback for Google/Apple reauthentication |
|
|
47
|
+
| `onPasswordRequired` | `() => Promise<string \| null>` | No | Callback for password reauthentication |
|
|
48
|
+
|
|
49
|
+
### Return Value
|
|
50
|
+
|
|
51
|
+
| Prop | Type | Description |
|
|
52
|
+
|------|------|-------------|
|
|
53
|
+
| `logout` | `() => Promise<void>` | Sign out function |
|
|
54
|
+
| `deleteAccount` | `() => Promise<void>` | Delete account function |
|
|
55
|
+
| `isLoading` | `boolean` | General loading state |
|
|
56
|
+
| `isDeletingAccount` | `boolean` | Account deletion loading state |
|
|
57
|
+
|
|
58
|
+
## Examples
|
|
59
|
+
|
|
60
|
+
### Simple Account Settings
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
function AccountSettingsScreen() {
|
|
64
|
+
const { logout, deleteAccount, isDeletingAccount } = useAccountManagement();
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<ScrollView style={styles.container}>
|
|
68
|
+
<Section title="Session">
|
|
69
|
+
<MenuItem
|
|
70
|
+
title="Sign Out"
|
|
71
|
+
icon="log-out"
|
|
72
|
+
onPress={logout}
|
|
73
|
+
/>
|
|
74
|
+
</Section>
|
|
75
|
+
|
|
76
|
+
<Section title="Danger Zone">
|
|
77
|
+
<MenuItem
|
|
78
|
+
title="Delete Account"
|
|
79
|
+
icon="trash"
|
|
80
|
+
onPress={deleteAccount}
|
|
81
|
+
destructive
|
|
82
|
+
disabled={isDeletingAccount}
|
|
83
|
+
/>
|
|
84
|
+
{isDeletingAccount && <ActivityIndicator />}
|
|
85
|
+
</Section>
|
|
86
|
+
</ScrollView>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### With Reauthentication
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
function AccountSettingsScreen() {
|
|
95
|
+
const { logout, deleteAccount } = useAccountManagement({
|
|
96
|
+
onReauthRequired: async () => {
|
|
97
|
+
try {
|
|
98
|
+
// Reauthenticate with Google
|
|
99
|
+
const result = await signInWithGooglePopup();
|
|
100
|
+
|
|
101
|
+
if (result.user) {
|
|
102
|
+
Alert.alert('Success', 'Please continue with account deletion');
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return false;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
Alert.alert('Error', 'Reauthentication failed');
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
onPasswordRequired: async () => {
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
// Show password prompt
|
|
115
|
+
Alert.prompt(
|
|
116
|
+
'Enter Password',
|
|
117
|
+
'Please enter your password to delete your account',
|
|
118
|
+
[
|
|
119
|
+
{
|
|
120
|
+
text: 'Cancel',
|
|
121
|
+
onPress: () => resolve(null),
|
|
122
|
+
style: 'cancel',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
text: 'OK',
|
|
126
|
+
onPress: (password) => resolve(password || null),
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
'secure-text'
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// ...
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Custom Reauthentication UI
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
function DeleteAccountScreen() {
|
|
143
|
+
const [showReauth, setShowReauth] = useState(false);
|
|
144
|
+
|
|
145
|
+
const { deleteAccount, isDeletingAccount } = useAccountManagement({
|
|
146
|
+
onReauthRequired: async () => {
|
|
147
|
+
setShowReauth(true);
|
|
148
|
+
return new Promise((resolve) => {
|
|
149
|
+
// Custom reauthentication UI
|
|
150
|
+
const handleResult = (success: boolean) => {
|
|
151
|
+
setShowReauth(false);
|
|
152
|
+
resolve(success);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
showCustomReauthUI(handleResult);
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
onPasswordRequired: async () => {
|
|
159
|
+
setShowReauth(true);
|
|
160
|
+
return new Promise((resolve) => {
|
|
161
|
+
// Custom password prompt
|
|
162
|
+
showPasswordPrompt((password) => {
|
|
163
|
+
setShowReauth(false);
|
|
164
|
+
resolve(password);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const handleDelete = async () => {
|
|
171
|
+
try {
|
|
172
|
+
await deleteAccount();
|
|
173
|
+
Alert.alert('Success', 'Account deleted');
|
|
174
|
+
} catch (error) {
|
|
175
|
+
Alert.alert('Error', error.message);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<View>
|
|
181
|
+
<Button onPress={handleDelete} disabled={isDeletingAccount}>
|
|
182
|
+
Delete Account
|
|
183
|
+
</Button>
|
|
184
|
+
|
|
185
|
+
{showReauth && (
|
|
186
|
+
<ReauthenticationModal
|
|
187
|
+
onComplete={() => {
|
|
188
|
+
// Reauthentication successful, deleteAccount continues
|
|
189
|
+
}}
|
|
190
|
+
/>
|
|
191
|
+
)}
|
|
192
|
+
</View>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Delete Account Confirmation
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
function DeleteAccountConfirmation() {
|
|
201
|
+
const { deleteAccount, isDeletingAccount } = useAccountManagement();
|
|
202
|
+
const [agreed, setAgreed] = useState(false);
|
|
203
|
+
|
|
204
|
+
const handleDelete = async () => {
|
|
205
|
+
if (!agreed) {
|
|
206
|
+
Alert.alert('Warning', 'Please accept the terms');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
Alert.alert(
|
|
211
|
+
'Delete Account',
|
|
212
|
+
'This action cannot be undone. Are you sure?',
|
|
213
|
+
[
|
|
214
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
215
|
+
{
|
|
216
|
+
text: 'Delete',
|
|
217
|
+
style: 'destructive',
|
|
218
|
+
onPress: deleteAccount,
|
|
219
|
+
},
|
|
220
|
+
]
|
|
221
|
+
);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<View>
|
|
226
|
+
<Text style={styles.warning}>
|
|
227
|
+
By deleting your account:
|
|
228
|
+
</Text>
|
|
229
|
+
<Text>• All your data will be permanently deleted</Text>
|
|
230
|
+
<Text>• This action cannot be undone</Text>
|
|
231
|
+
<Text>• You won't be able to sign in with the same account</Text>
|
|
232
|
+
|
|
233
|
+
<CheckBox
|
|
234
|
+
value={agreed}
|
|
235
|
+
onValueChange={setAgreed}
|
|
236
|
+
label="I accept the account deletion terms"
|
|
237
|
+
/>
|
|
238
|
+
|
|
239
|
+
<Button
|
|
240
|
+
onPress={handleDelete}
|
|
241
|
+
disabled={!agreed || isDeletingAccount}
|
|
242
|
+
style={{ backgroundColor: 'red' }}
|
|
243
|
+
>
|
|
244
|
+
{isDeletingAccount ? 'Deleting...' : 'Permanently Delete Account'}
|
|
245
|
+
</Button>
|
|
246
|
+
</View>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Anonymous User Handling
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
function AccountActionsAnonymous() {
|
|
255
|
+
const { isAnonymous } = useAuth();
|
|
256
|
+
|
|
257
|
+
if (isAnonymous) {
|
|
258
|
+
return (
|
|
259
|
+
<Button onPress={() => navigation.navigate('Register')}>
|
|
260
|
+
Create Account
|
|
261
|
+
</Button>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const config = {
|
|
266
|
+
logoutText: 'Sign Out',
|
|
267
|
+
deleteAccountText: 'Delete Account',
|
|
268
|
+
logoutConfirmTitle: 'Sign Out',
|
|
269
|
+
logoutConfirmMessage: 'Are you sure you want to sign out?',
|
|
270
|
+
deleteConfirmTitle: 'Delete Account',
|
|
271
|
+
deleteConfirmMessage: 'Are you sure you want to delete your account?',
|
|
272
|
+
onLogout: logout,
|
|
273
|
+
onDeleteAccount: deleteAccount,
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
return <AccountActions config={config} />;
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Error Handling
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
function AccountSettingsWithErrorHandling() {
|
|
284
|
+
const { logout, deleteAccount } = useAccountManagement();
|
|
285
|
+
const navigation = useNavigation();
|
|
286
|
+
|
|
287
|
+
const handleLogout = async () => {
|
|
288
|
+
try {
|
|
289
|
+
await logout();
|
|
290
|
+
navigation.replace('Login');
|
|
291
|
+
} catch (error) {
|
|
292
|
+
if (error.code === 'auth/network-request-failed') {
|
|
293
|
+
Alert.alert('Connection Error', 'Check your internet connection');
|
|
294
|
+
} else {
|
|
295
|
+
Alert.alert('Error', 'Failed to sign out');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const handleDeleteAccount = async () => {
|
|
301
|
+
try {
|
|
302
|
+
await deleteAccount();
|
|
303
|
+
Alert.alert('Success', 'Account deleted');
|
|
304
|
+
navigation.replace('Login');
|
|
305
|
+
} catch (error) {
|
|
306
|
+
if (error.code === 'auth/requires-recent-login') {
|
|
307
|
+
Alert.alert(
|
|
308
|
+
'Authentication Required',
|
|
309
|
+
'Please sign in again to delete your account'
|
|
310
|
+
);
|
|
311
|
+
} else if (error.code === 'auth/too-many-requests') {
|
|
312
|
+
Alert.alert(
|
|
313
|
+
'Too Many Attempts',
|
|
314
|
+
'Too many failed attempts. Please try again later'
|
|
315
|
+
);
|
|
316
|
+
} else {
|
|
317
|
+
Alert.alert('Error', 'Failed to delete account');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<View>
|
|
324
|
+
<Button onPress={handleLogout}>Sign Out</Button>
|
|
325
|
+
<Button onPress={handleDeleteAccount}>Delete Account</Button>
|
|
326
|
+
</View>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Important Notes
|
|
332
|
+
|
|
333
|
+
1. **Reauthentication Required**: Firebase requires recent sign-in for account deletion
|
|
334
|
+
2. **Anonymous Users**: Anonymous accounts cannot be deleted
|
|
335
|
+
3. **Irreversible**: Account deletion is permanent
|
|
336
|
+
4. **Callbacks**: If `onReauthRequired` and `onPasswordRequired` are not provided, errors will be thrown
|
|
337
|
+
|
|
338
|
+
## Reauthentication
|
|
339
|
+
|
|
340
|
+
Account deletion is a sensitive operation, so Firebase requires the user to have signed in recently. This hook provides reauthentication callbacks:
|
|
341
|
+
|
|
342
|
+
### onReauthRequired
|
|
343
|
+
|
|
344
|
+
For Google or Apple sign-in users:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
const { deleteAccount } = useAccountManagement({
|
|
348
|
+
onReauthRequired: async () => {
|
|
349
|
+
try {
|
|
350
|
+
// Reauthenticate with Google
|
|
351
|
+
const result = await signInWithGooglePopup();
|
|
352
|
+
return result.user ? true : false;
|
|
353
|
+
} catch (error) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### onPasswordRequired
|
|
361
|
+
|
|
362
|
+
For email/password users:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
const { deleteAccount } = useAccountManagement({
|
|
366
|
+
onPasswordRequired: async () => {
|
|
367
|
+
return new Promise((resolve) => {
|
|
368
|
+
Alert.prompt(
|
|
369
|
+
'Enter Password',
|
|
370
|
+
'Please enter your password',
|
|
371
|
+
[
|
|
372
|
+
{ text: 'Cancel', onPress: () => resolve(null), style: 'cancel' },
|
|
373
|
+
{ text: 'OK', onPress: (password) => resolve(password || null) },
|
|
374
|
+
],
|
|
375
|
+
'secure-text'
|
|
376
|
+
);
|
|
377
|
+
});
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Related Hooks
|
|
383
|
+
|
|
384
|
+
- [`useAuth`](./useAuth.md) - Main auth state management
|
|
385
|
+
- [`useSignOut`](./useAuth.md) - Sign out function
|
|
386
|
+
- [`useUserProfile`](./useUserProfile.md) - Profile information
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# useAuth
|
|
2
|
+
|
|
3
|
+
Primary authentication hook for managing auth state and operations.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Centralized Zustand store for auth state
|
|
8
|
+
- Email/password authentication
|
|
9
|
+
- Anonymous mode support
|
|
10
|
+
- Type-safe API
|
|
11
|
+
- Automatic error handling
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { useAuth } from '@umituz/react-native-auth';
|
|
17
|
+
|
|
18
|
+
function MyComponent() {
|
|
19
|
+
const {
|
|
20
|
+
user,
|
|
21
|
+
userId,
|
|
22
|
+
userType,
|
|
23
|
+
loading,
|
|
24
|
+
isAuthReady,
|
|
25
|
+
isAnonymous,
|
|
26
|
+
isAuthenticated,
|
|
27
|
+
error,
|
|
28
|
+
signIn,
|
|
29
|
+
signUp,
|
|
30
|
+
signOut,
|
|
31
|
+
continueAnonymously,
|
|
32
|
+
setError,
|
|
33
|
+
} = useAuth();
|
|
34
|
+
|
|
35
|
+
// Handle loading state
|
|
36
|
+
if (loading) return <LoadingSpinner />;
|
|
37
|
+
|
|
38
|
+
// Redirect to login if not authenticated
|
|
39
|
+
if (!isAuthenticated) {
|
|
40
|
+
return <LoginScreen />;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<View>
|
|
45
|
+
<Text>Welcome, {user?.email}</Text>
|
|
46
|
+
<Button onPress={signOut}>Sign Out</Button>
|
|
47
|
+
</View>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
### Return Value
|
|
55
|
+
|
|
56
|
+
| Prop | Type | Description |
|
|
57
|
+
|------|------|-------------|
|
|
58
|
+
| `user` | `AuthUser \| null` | Current authenticated user |
|
|
59
|
+
| `userId` | `string \| null` | Current user ID (uid) |
|
|
60
|
+
| `userType` | `UserType` | Current user type |
|
|
61
|
+
| `loading` | `boolean` | Whether auth state is loading |
|
|
62
|
+
| `isAuthReady` | `boolean` | Whether auth is ready (initialized and not loading) |
|
|
63
|
+
| `isAnonymous` | `boolean` | Whether user is anonymous |
|
|
64
|
+
| `isAuthenticated` | `boolean` | Whether user is authenticated (not anonymous) |
|
|
65
|
+
| `error` | `string \| null` | Current error message |
|
|
66
|
+
| `signIn` | `(email, password) => Promise<void>` | Sign in function |
|
|
67
|
+
| `signUp` | `(email, password, displayName?) => Promise<void>` | Sign up function |
|
|
68
|
+
| `signOut` | `() => Promise<void>` | Sign out function |
|
|
69
|
+
| `continueAnonymously` | `() => Promise<void>` | Continue anonymously function |
|
|
70
|
+
| `setError` | `(error: string \| null) => void` | Set error manually |
|
|
71
|
+
|
|
72
|
+
## Examples
|
|
73
|
+
|
|
74
|
+
### Login Form
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
function LoginForm() {
|
|
78
|
+
const { signIn, loading, error } = useAuth();
|
|
79
|
+
const [email, setEmail] = useState('');
|
|
80
|
+
const [password, setPassword] = useState('');
|
|
81
|
+
|
|
82
|
+
const handleLogin = async () => {
|
|
83
|
+
try {
|
|
84
|
+
await signIn(email, password);
|
|
85
|
+
// Navigate to home on success
|
|
86
|
+
} catch (err) {
|
|
87
|
+
// Error is automatically set in error state
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<View>
|
|
93
|
+
<TextInput
|
|
94
|
+
value={email}
|
|
95
|
+
onChangeText={setEmail}
|
|
96
|
+
placeholder="Email"
|
|
97
|
+
autoCapitalize="none"
|
|
98
|
+
keyboardType="email-address"
|
|
99
|
+
/>
|
|
100
|
+
<TextInput
|
|
101
|
+
value={password}
|
|
102
|
+
onChangeText={setPassword}
|
|
103
|
+
placeholder="Password"
|
|
104
|
+
secureTextEntry
|
|
105
|
+
/>
|
|
106
|
+
{error && <Text style={{ color: 'red' }}>{error}</Text>}
|
|
107
|
+
<Button onPress={handleLogin} disabled={loading}>
|
|
108
|
+
{loading ? 'Signing in...' : 'Sign In'}
|
|
109
|
+
</Button>
|
|
110
|
+
</View>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Registration Form
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
function RegisterForm() {
|
|
119
|
+
const { signUp, loading, error } = useAuth();
|
|
120
|
+
const [email, setEmail] = useState('');
|
|
121
|
+
const [password, setPassword] = useState('');
|
|
122
|
+
const [displayName, setDisplayName] = useState('');
|
|
123
|
+
|
|
124
|
+
const handleRegister = async () => {
|
|
125
|
+
try {
|
|
126
|
+
await signUp(email, password, displayName);
|
|
127
|
+
// Success - user is automatically signed in
|
|
128
|
+
} catch (err) {
|
|
129
|
+
// Error handling
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<View>
|
|
135
|
+
<TextInput
|
|
136
|
+
value={displayName}
|
|
137
|
+
onChangeText={setDisplayName}
|
|
138
|
+
placeholder="Full Name"
|
|
139
|
+
/>
|
|
140
|
+
<TextInput
|
|
141
|
+
value={email}
|
|
142
|
+
onChangeText={setEmail}
|
|
143
|
+
placeholder="Email"
|
|
144
|
+
keyboardType="email-address"
|
|
145
|
+
/>
|
|
146
|
+
<TextInput
|
|
147
|
+
value={password}
|
|
148
|
+
onChangeText={setPassword}
|
|
149
|
+
placeholder="Password"
|
|
150
|
+
secureTextEntry
|
|
151
|
+
/>
|
|
152
|
+
{error && <Text style={{ color: 'red' }}>{error}</Text>}
|
|
153
|
+
<Button onPress={handleRegister} disabled={loading}>
|
|
154
|
+
{loading ? 'Creating account...' : 'Sign Up'}
|
|
155
|
+
</Button>
|
|
156
|
+
</View>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Anonymous Mode
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
function AnonymousPrompt() {
|
|
165
|
+
const { continueAnonymously, loading } = useAuth();
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<View>
|
|
169
|
+
<Text>Continue without an account?</Text>
|
|
170
|
+
<Button onPress={continueAnonymously} disabled={loading}>
|
|
171
|
+
{loading ? 'Starting...' : 'Continue as Guest'}
|
|
172
|
+
</Button>
|
|
173
|
+
</View>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Auth State Checking
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
function ProtectedContent() {
|
|
182
|
+
const { isAuthenticated, isAuthReady, user } = useAuth();
|
|
183
|
+
|
|
184
|
+
// Show loading while auth initializes
|
|
185
|
+
if (!isAuthReady) {
|
|
186
|
+
return <LoadingScreen />;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Redirect if not authenticated
|
|
190
|
+
if (!isAuthenticated) {
|
|
191
|
+
return <LoginScreen />;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Show protected content
|
|
195
|
+
return (
|
|
196
|
+
<View>
|
|
197
|
+
<Text>Welcome, {user?.email}</Text>
|
|
198
|
+
<ProtectedContent />
|
|
199
|
+
</View>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Sign Out
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
function ProfileScreen() {
|
|
208
|
+
const { user, signOut } = useAuth();
|
|
209
|
+
const navigation = useNavigation();
|
|
210
|
+
|
|
211
|
+
const handleSignOut = async () => {
|
|
212
|
+
Alert.alert(
|
|
213
|
+
'Sign Out',
|
|
214
|
+
'Are you sure you want to sign out?',
|
|
215
|
+
[
|
|
216
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
217
|
+
{
|
|
218
|
+
text: 'Sign Out',
|
|
219
|
+
onPress: async () => {
|
|
220
|
+
try {
|
|
221
|
+
await signOut();
|
|
222
|
+
navigation.reset({
|
|
223
|
+
index: 0,
|
|
224
|
+
routes: [{ name: 'Login' }],
|
|
225
|
+
});
|
|
226
|
+
} catch (error) {
|
|
227
|
+
Alert.alert('Error', 'Failed to sign out');
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
]
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<View>
|
|
237
|
+
<Text>{user?.email}</Text>
|
|
238
|
+
<Button onPress={handleSignOut}>Sign Out</Button>
|
|
239
|
+
</View>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Important Notes
|
|
245
|
+
|
|
246
|
+
1. **initializeAuthListener()**: Must be called once in app root
|
|
247
|
+
2. **Centralized State**: All components share the same state via Zustand
|
|
248
|
+
3. **Error Handling**: Errors are automatically set in the `error` state
|
|
249
|
+
4. **Loading States**: `loading` is true during operations
|
|
250
|
+
|
|
251
|
+
## Related Hooks
|
|
252
|
+
|
|
253
|
+
- [`useAuthRequired`](./useAuthRequired.md) - For components requiring auth
|
|
254
|
+
- [`useRequireAuth`](./useAuthRequired.md#userequireauth) - Route protection
|
|
255
|
+
- [`useUserProfile`](./useUserProfile.md) - User profile data
|