@umituz/react-native-auth 3.4.29 → 3.4.31
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 +331 -0
- package/src/presentation/components/PasswordIndicators.md +471 -0
- package/src/presentation/components/ProfileComponents.md +432 -0
- package/src/presentation/components/README.md +117 -0
- package/src/presentation/components/SocialLoginButtons.md +303 -0
- package/src/presentation/hooks/README.md +122 -0
- package/src/presentation/hooks/useAccountManagement.md +380 -0
- package/src/presentation/hooks/useAuth.md +255 -0
- package/src/presentation/hooks/useAuthBottomSheet.md +417 -0
- package/src/presentation/hooks/useAuthRequired.md +248 -0
- package/src/presentation/hooks/useProfileUpdate.md +327 -0
- package/src/presentation/hooks/useSocialLogin.md +411 -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,380 @@
|
|
|
1
|
+
# useAccountManagement
|
|
2
|
+
|
|
3
|
+
Hesap yönetimi işlemleri için hook. Çıkış yapma ve hesap silme işlevselliği sağlar.
|
|
4
|
+
|
|
5
|
+
## Özellikler
|
|
6
|
+
|
|
7
|
+
- Güvenli çıkış yapma
|
|
8
|
+
- Hesap silme (reauthentication gerektirir)
|
|
9
|
+
- Reauthentication callback desteği
|
|
10
|
+
- Loading state yönetimi
|
|
11
|
+
|
|
12
|
+
## Kullanım
|
|
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
|
+
// Google/Apple ile yeniden authentication
|
|
21
|
+
const result = await reauthenticateWithGoogle();
|
|
22
|
+
return result.success;
|
|
23
|
+
},
|
|
24
|
+
onPasswordRequired: async () => {
|
|
25
|
+
// Şifre prompt göster
|
|
26
|
+
const password = await showPasswordPrompt();
|
|
27
|
+
return password;
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const handleLogout = async () => {
|
|
32
|
+
Alert.alert(
|
|
33
|
+
'Çıkış Yap',
|
|
34
|
+
'Çıkış yapmak istediğinizden emin misiniz?',
|
|
35
|
+
[
|
|
36
|
+
{ text: 'İptal', style: 'cancel' },
|
|
37
|
+
{
|
|
38
|
+
text: 'Çıkış',
|
|
39
|
+
onPress: logout,
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const handleDeleteAccount = async () => {
|
|
46
|
+
Alert.alert(
|
|
47
|
+
'Hesabı Sil',
|
|
48
|
+
'Bu işlem geri alınamaz. Hesabınızı silmek istediğinizden emin misiniz?',
|
|
49
|
+
[
|
|
50
|
+
{ text: 'İptal', style: 'cancel' },
|
|
51
|
+
{
|
|
52
|
+
text: 'Sil',
|
|
53
|
+
style: 'destructive',
|
|
54
|
+
onPress: deleteAccount,
|
|
55
|
+
},
|
|
56
|
+
]
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<View>
|
|
62
|
+
<Button onPress={handleLogout} disabled={isLoading}>
|
|
63
|
+
Çıkış Yap
|
|
64
|
+
</Button>
|
|
65
|
+
<Button
|
|
66
|
+
onPress={handleDeleteAccount}
|
|
67
|
+
disabled={isDeletingAccount}
|
|
68
|
+
style={{ backgroundColor: 'red' }}
|
|
69
|
+
>
|
|
70
|
+
{isDeletingAccount ? 'Siliniyor...' : 'Hesabı Sil'}
|
|
71
|
+
</Button>
|
|
72
|
+
</View>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## API
|
|
78
|
+
|
|
79
|
+
### Parameters
|
|
80
|
+
|
|
81
|
+
| Param | Tip | Required | Açıklama |
|
|
82
|
+
|-------|------|----------|----------|
|
|
83
|
+
| `onReauthRequired` | `() => Promise<boolean>` | No | Google/Apple ile yeniden authentication callback'i |
|
|
84
|
+
| `onPasswordRequired` | `() => Promise<string \| null>` | No | Şifre ile yeniden authentication callback'i |
|
|
85
|
+
|
|
86
|
+
### Return Value
|
|
87
|
+
|
|
88
|
+
| Prop | Tip | Açıklama |
|
|
89
|
+
|------|-----|----------|
|
|
90
|
+
| `logout` | `() => Promise<void>` | Çıkış yapma fonksiyonu |
|
|
91
|
+
| `deleteAccount` | `() => Promise<void>` | Hesap silme fonksiyonu |
|
|
92
|
+
| `isLoading` | `boolean` | Genel loading durumu |
|
|
93
|
+
| `isDeletingAccount` | `boolean` | Hesap silme loading durumu |
|
|
94
|
+
|
|
95
|
+
## Reauthentication
|
|
96
|
+
|
|
97
|
+
Hesap silme işlemi hassas bir işlem olduğu için Firebase, kullanıcının son zamanlarda giriş yapmasını gerektirir. Bu hook size reauthentication için callback'ler sağlar.
|
|
98
|
+
|
|
99
|
+
### onReauthRequired
|
|
100
|
+
|
|
101
|
+
Google veya Apple ile giriş yapmış kullanıcılar için:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const { deleteAccount } = useAccountManagement({
|
|
105
|
+
onReauthRequired: async () => {
|
|
106
|
+
try {
|
|
107
|
+
// Google ile yeniden authentication
|
|
108
|
+
const result = await signInWithGooglePopup();
|
|
109
|
+
|
|
110
|
+
if (result.user) {
|
|
111
|
+
Alert.alert('Başarılı', 'Lütfen hesap silme işlemine devam edin');
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return false;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
Alert.alert('Hata', 'Reauthentication başarısız');
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### onPasswordRequired
|
|
125
|
+
|
|
126
|
+
Email/password ile giriş yapmış kullanıcılar için:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const { deleteAccount } = useAccountManagement({
|
|
130
|
+
onPasswordRequired: async () => {
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
// Şifre prompt göster
|
|
133
|
+
Alert.prompt(
|
|
134
|
+
'Şifre Girin',
|
|
135
|
+
'Hesabınızı silmek için şifrenizi girin',
|
|
136
|
+
[
|
|
137
|
+
{
|
|
138
|
+
text: 'İptal',
|
|
139
|
+
onPress: () => resolve(null),
|
|
140
|
+
style: 'cancel',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
text: 'Tamam',
|
|
144
|
+
onPress: (password) => resolve(password || null),
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
'secure-text'
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Örnekler
|
|
155
|
+
|
|
156
|
+
### Basit Hesap Ayarları Ekranı
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
function AccountSettingsScreen() {
|
|
160
|
+
const { logout, deleteAccount, isDeletingAccount } = useAccountManagement();
|
|
161
|
+
const navigation = useNavigation();
|
|
162
|
+
|
|
163
|
+
const handleLogout = async () => {
|
|
164
|
+
try {
|
|
165
|
+
await logout();
|
|
166
|
+
navigation.replace('Login');
|
|
167
|
+
} catch (error) {
|
|
168
|
+
Alert.alert('Hata', 'Çıkış yapılamadı');
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const handleDeleteAccount = async () => {
|
|
173
|
+
try {
|
|
174
|
+
await deleteAccount();
|
|
175
|
+
navigation.replace('Login');
|
|
176
|
+
Alert.alert('Başarılı', 'Hesabınız silindi');
|
|
177
|
+
} catch (error) {
|
|
178
|
+
Alert.alert('Hata', 'Hesap silinemedi');
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<ScrollView style={styles.container}>
|
|
184
|
+
<Section title="Oturum">
|
|
185
|
+
<MenuItem
|
|
186
|
+
title="Çıkış Yap"
|
|
187
|
+
icon="log-out"
|
|
188
|
+
onPress={handleLogout}
|
|
189
|
+
/>
|
|
190
|
+
</Section>
|
|
191
|
+
|
|
192
|
+
<Section title="Tehlikeli Bölge">
|
|
193
|
+
<MenuItem
|
|
194
|
+
title="Hesabı Sil"
|
|
195
|
+
icon="trash"
|
|
196
|
+
onPress={handleDeleteAccount}
|
|
197
|
+
destructive
|
|
198
|
+
disabled={isDeletingAccount}
|
|
199
|
+
/>
|
|
200
|
+
{isDeletingAccount && <ActivityIndicator />}
|
|
201
|
+
</Section>
|
|
202
|
+
</ScrollView>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Custom Reauthentication UI
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
function DeleteAccountScreen() {
|
|
211
|
+
const [showReauth, setShowReauth] = useState(false);
|
|
212
|
+
const [reauthMethod, setReauthMethod] = useState<'password' | 'google' | 'apple'>('password');
|
|
213
|
+
|
|
214
|
+
const { deleteAccount, isDeletingAccount } = useAccountManagement({
|
|
215
|
+
onReauthRequired: async () => {
|
|
216
|
+
setShowReauth(true);
|
|
217
|
+
return new Promise((resolve) => {
|
|
218
|
+
// Custom reauthentication UI
|
|
219
|
+
const handleResult = (success: boolean) => {
|
|
220
|
+
setShowReauth(false);
|
|
221
|
+
resolve(success);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// UI'ı göster ve sonucu bekle
|
|
225
|
+
showCustomReauthUI(reauthMethod, handleResult);
|
|
226
|
+
});
|
|
227
|
+
},
|
|
228
|
+
onPasswordRequired: async () => {
|
|
229
|
+
setShowReauth(true);
|
|
230
|
+
return new Promise((resolve) => {
|
|
231
|
+
// Custom password prompt
|
|
232
|
+
showPasswordPrompt((password) => {
|
|
233
|
+
setShowReauth(false);
|
|
234
|
+
resolve(password);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const handleDelete = async () => {
|
|
241
|
+
try {
|
|
242
|
+
await deleteAccount();
|
|
243
|
+
Alert.alert('Başarılı', 'Hesabınız silindi');
|
|
244
|
+
} catch (error) {
|
|
245
|
+
Alert.alert('Hata', error.message);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<View>
|
|
251
|
+
<Button onPress={handleDelete} disabled={isDeletingAccount}>
|
|
252
|
+
Hesabı Sil
|
|
253
|
+
</Button>
|
|
254
|
+
|
|
255
|
+
{showReauth && (
|
|
256
|
+
<ReauthenticationModal
|
|
257
|
+
method={reauthMethod}
|
|
258
|
+
onComplete={() => {
|
|
259
|
+
// Reauthentication başarılı, deleteAccount devam eder
|
|
260
|
+
}}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
</View>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Hesap Silme Onayı
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
function DeleteAccountConfirmation() {
|
|
272
|
+
const { deleteAccount, isDeletingAccount } = useAccountManagement();
|
|
273
|
+
const [agreed, setAgreed] = useState(false);
|
|
274
|
+
|
|
275
|
+
const handleDelete = async () => {
|
|
276
|
+
if (!agreed) {
|
|
277
|
+
Alert.alert('Uyarı', 'Lütfen koşulları kabul edin');
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
Alert.alert(
|
|
282
|
+
'Hesabı Sil',
|
|
283
|
+
'Bu işlem geri alınamaz. Devam etmek istediğinizden emin misiniz?',
|
|
284
|
+
[
|
|
285
|
+
{ text: 'İptal', style: 'cancel' },
|
|
286
|
+
{
|
|
287
|
+
text: 'Sil',
|
|
288
|
+
style: 'destructive',
|
|
289
|
+
onPress: deleteAccount,
|
|
290
|
+
},
|
|
291
|
+
]
|
|
292
|
+
);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
return (
|
|
296
|
+
<View>
|
|
297
|
+
<Text style={styles.warning}>
|
|
298
|
+
Hesabınızı silerseniz:
|
|
299
|
+
</Text>
|
|
300
|
+
<Text>• Tüm verileriniz kalıcı olarak silinir</Text>
|
|
301
|
+
<Text>• İşlemler geri alınamaz</Text>
|
|
302
|
+
<Text>• Aynı hesapla tekrar giriş yapamazsınız</Text>
|
|
303
|
+
|
|
304
|
+
<CheckBox
|
|
305
|
+
value={agreed}
|
|
306
|
+
onValueChange={setAgreed}
|
|
307
|
+
label="Hesap silme koşullarını kabul ediyorum"
|
|
308
|
+
/>
|
|
309
|
+
|
|
310
|
+
<Button
|
|
311
|
+
onPress={handleDelete}
|
|
312
|
+
disabled={!agreed || isDeletingAccount}
|
|
313
|
+
style={{ backgroundColor: 'red' }}
|
|
314
|
+
>
|
|
315
|
+
{isDeletingAccount ? 'Siliniyor...' : 'Hesabı Kalıcı Olarak Sil'}
|
|
316
|
+
</Button>
|
|
317
|
+
</View>
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Hata Yönetimi
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
function AccountSettings() {
|
|
326
|
+
const { logout, deleteAccount } = useAccountManagement();
|
|
327
|
+
|
|
328
|
+
const handleLogout = async () => {
|
|
329
|
+
try {
|
|
330
|
+
await logout();
|
|
331
|
+
} catch (error) {
|
|
332
|
+
if (error.code === 'auth/network-request-failed') {
|
|
333
|
+
Alert.alert('Bağlantı Hatası', 'İnternet bağlantınızı kontrol edin');
|
|
334
|
+
} else {
|
|
335
|
+
Alert.alert('Hata', 'Çıkış yapılamadı');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const handleDeleteAccount = async () => {
|
|
341
|
+
try {
|
|
342
|
+
await deleteAccount();
|
|
343
|
+
} catch (error) {
|
|
344
|
+
if (error.code === 'auth/requires-recent-login') {
|
|
345
|
+
Alert.alert(
|
|
346
|
+
'Giriş Gerekiyor',
|
|
347
|
+
'Hesabınızı silmek için lütfen tekrar giriş yapın'
|
|
348
|
+
);
|
|
349
|
+
} else if (error.code === 'auth/too-many-requests') {
|
|
350
|
+
Alert.alert(
|
|
351
|
+
'Çok Fazla Deneme',
|
|
352
|
+
'Çok fazla başarısız deneme. Lütfen daha sonra tekrar deneyin'
|
|
353
|
+
);
|
|
354
|
+
} else {
|
|
355
|
+
Alert.alert('Hata', 'Hesap silinemedi');
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
<View>
|
|
362
|
+
<Button onPress={handleLogout}>Çıkış Yap</Button>
|
|
363
|
+
<Button onPress={handleDeleteAccount}>Hesabı Sil</Button>
|
|
364
|
+
</View>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Önemli Notlar
|
|
370
|
+
|
|
371
|
+
1. **Reauthentication Gerekli**: Firebase, hesap silme işlemi için son zamanlarda giriş yapmayı gerektirir
|
|
372
|
+
2. **Anonymous Kullanıcılar**: Anonymous hesaplar silinemez
|
|
373
|
+
3. **Geri Alınamaz**: Hesap silme işlemi geri alınamaz
|
|
374
|
+
4. **Callback'ler**: `onReauthRequired` ve `onPasswordRequired` callback'lerini sağlamazsanız, hatalar fırlatılır
|
|
375
|
+
|
|
376
|
+
## İlgili Hooks
|
|
377
|
+
|
|
378
|
+
- [`useAuth`](./useAuth.md) - Ana auth state yönetimi
|
|
379
|
+
- [`useSignOut`](./useAuth.md) - Çıkış yapma fonksiyonu
|
|
380
|
+
- [`useUserProfile`](./useUserProfile.md) - Profil bilgileri
|
|
@@ -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
|