@umituz/react-native-auth 3.4.30 → 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,417 @@
|
|
|
1
|
+
# useAuthBottomSheet
|
|
2
|
+
|
|
3
|
+
Authentication bottom sheet yönetimi için hook. Login/Register modal'ını göstermek, yönetmek ve social authentication işlemlerini gerçekleştirmek için kullanılır.
|
|
4
|
+
|
|
5
|
+
## Özellikler
|
|
6
|
+
|
|
7
|
+
- Bottom sheet modal yönetimi
|
|
8
|
+
- Login/Register mod değiştirme
|
|
9
|
+
- Social authentication entegrasyonu
|
|
10
|
+
- Otomatik kapanma (başarılı authentication sonrası)
|
|
11
|
+
- Pending callback yönetimi
|
|
12
|
+
|
|
13
|
+
## Kullanım
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { useAuthBottomSheet } from '@umituz/react-native-auth';
|
|
17
|
+
import { BottomSheetModal } from '@umituz/react-native-design-system';
|
|
18
|
+
|
|
19
|
+
function AuthBottomSheet() {
|
|
20
|
+
const {
|
|
21
|
+
modalRef,
|
|
22
|
+
mode,
|
|
23
|
+
providers,
|
|
24
|
+
googleLoading,
|
|
25
|
+
appleLoading,
|
|
26
|
+
handleDismiss,
|
|
27
|
+
handleClose,
|
|
28
|
+
handleNavigateToRegister,
|
|
29
|
+
handleNavigateToLogin,
|
|
30
|
+
handleGoogleSignIn,
|
|
31
|
+
handleAppleSignIn,
|
|
32
|
+
} = useAuthBottomSheet({
|
|
33
|
+
socialConfig: {
|
|
34
|
+
google: {
|
|
35
|
+
iosClientId: 'your-ios-client-id',
|
|
36
|
+
webClientId: 'your-web-client-id',
|
|
37
|
+
},
|
|
38
|
+
apple: { enabled: true },
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<BottomSheetModal ref={modalRef} onDismiss={handleDismiss}>
|
|
44
|
+
{mode === 'login' ? (
|
|
45
|
+
<LoginForm
|
|
46
|
+
onRegisterPress={handleNavigateToRegister}
|
|
47
|
+
onGoogleSignIn={handleGoogleSignIn}
|
|
48
|
+
onAppleSignIn={handleAppleSignIn}
|
|
49
|
+
googleLoading={googleLoading}
|
|
50
|
+
appleLoading={appleLoading}
|
|
51
|
+
/>
|
|
52
|
+
) : (
|
|
53
|
+
<RegisterForm
|
|
54
|
+
onLoginPress={handleNavigateToLogin}
|
|
55
|
+
onGoogleSignIn={handleGoogleSignIn}
|
|
56
|
+
onAppleSignIn={handleAppleSignIn}
|
|
57
|
+
googleLoading={googleLoading}
|
|
58
|
+
appleLoading={appleLoading}
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
61
|
+
</BottomSheetModal>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API
|
|
67
|
+
|
|
68
|
+
### Parameters
|
|
69
|
+
|
|
70
|
+
| Param | Tip | Required | Açıklama |
|
|
71
|
+
|-------|------|----------|----------|
|
|
72
|
+
| `socialConfig` | `SocialAuthConfiguration` | No | Social auth konfigürasyonu |
|
|
73
|
+
| `onGoogleSignIn` | `() => Promise<void>` | No | Custom Google sign-in handler |
|
|
74
|
+
| `onAppleSignIn` | `() => Promise<void>` | No | Custom Apple sign-in handler |
|
|
75
|
+
|
|
76
|
+
#### SocialAuthConfiguration
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
interface SocialAuthConfiguration {
|
|
80
|
+
google?: GoogleAuthConfig;
|
|
81
|
+
apple?: { enabled: boolean };
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Return Value
|
|
86
|
+
|
|
87
|
+
| Prop | Tip | Açıklama |
|
|
88
|
+
|------|-----|----------|
|
|
89
|
+
| `modalRef` | `RefObject<BottomSheetModalRef>` | Bottom sheet modal referansı |
|
|
90
|
+
| `mode` | `'login' \| 'register'` | Mevcut mod |
|
|
91
|
+
| `providers` | `SocialAuthProvider[]` | Mevcut social provider'lar |
|
|
92
|
+
| `googleLoading` | `boolean` | Google loading durumu |
|
|
93
|
+
| `appleLoading` | `boolean` | Apple loading durumu |
|
|
94
|
+
| `handleDismiss` | `() => void` | Modal'ı kapatma (dismiss) |
|
|
95
|
+
| `handleClose` | `() => void` | Modal'ı kapatma ve temizleme |
|
|
96
|
+
| `handleNavigateToRegister` | `() => void` | Register moduna geçiş |
|
|
97
|
+
| `handleNavigateToLogin` | `() => void` | Login moduna geçiş |
|
|
98
|
+
| `handleGoogleSignIn` | `() => Promise<void>` | Google ile giriş |
|
|
99
|
+
| `handleAppleSignIn` | `() => Promise<void>` | Apple ile giriş |
|
|
100
|
+
|
|
101
|
+
## Örnekler
|
|
102
|
+
|
|
103
|
+
### Basit Auth Bottom Sheet
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
function AuthModal() {
|
|
107
|
+
const {
|
|
108
|
+
modalRef,
|
|
109
|
+
mode,
|
|
110
|
+
handleDismiss,
|
|
111
|
+
handleNavigateToRegister,
|
|
112
|
+
handleNavigateToLogin,
|
|
113
|
+
} = useAuthBottomSheet();
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<BottomSheetModal ref={modalRef} snapPoints={['80%']} onDismiss={handleDismiss}>
|
|
117
|
+
<View>
|
|
118
|
+
{mode === 'login' ? (
|
|
119
|
+
<>
|
|
120
|
+
<Text>Giriş Yap</Text>
|
|
121
|
+
<LoginForm />
|
|
122
|
+
<Button onPress={handleNavigateToRegister}>
|
|
123
|
+
Hesabınız yok mu? Kayıt Olun
|
|
124
|
+
</Button>
|
|
125
|
+
</>
|
|
126
|
+
) : (
|
|
127
|
+
<>
|
|
128
|
+
<Text>Kayıt Ol</Text>
|
|
129
|
+
<RegisterForm />
|
|
130
|
+
<Button onPress={handleNavigateToLogin}>
|
|
131
|
+
Zaten hesabınız var mı? Giriş Yapın
|
|
132
|
+
</Button>
|
|
133
|
+
</>
|
|
134
|
+
)}
|
|
135
|
+
</View>
|
|
136
|
+
</BottomSheetModal>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Social Login ile
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
function AuthBottomSheetWithSocial() {
|
|
145
|
+
const {
|
|
146
|
+
modalRef,
|
|
147
|
+
mode,
|
|
148
|
+
providers,
|
|
149
|
+
googleLoading,
|
|
150
|
+
appleLoading,
|
|
151
|
+
handleDismiss,
|
|
152
|
+
handleNavigateToRegister,
|
|
153
|
+
handleNavigateToLogin,
|
|
154
|
+
handleGoogleSignIn,
|
|
155
|
+
handleAppleSignIn,
|
|
156
|
+
} = useAuthBottomSheet({
|
|
157
|
+
socialConfig: {
|
|
158
|
+
google: {
|
|
159
|
+
webClientId: Config.GOOGLE_WEB_CLIENT_ID,
|
|
160
|
+
iosClientId: Config.GOOGLE_IOS_CLIENT_ID,
|
|
161
|
+
},
|
|
162
|
+
apple: { enabled: Platform.OS === 'ios' },
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<BottomSheetModal ref={modalRef} snapPoints={['90%']} onDismiss={handleDismiss}>
|
|
168
|
+
<View>
|
|
169
|
+
{mode === 'login' ? (
|
|
170
|
+
<>
|
|
171
|
+
<LoginForm />
|
|
172
|
+
|
|
173
|
+
{/* Social login buttons */}
|
|
174
|
+
{providers.includes('google') && (
|
|
175
|
+
<SocialButton
|
|
176
|
+
provider="google"
|
|
177
|
+
onPress={handleGoogleSignIn}
|
|
178
|
+
loading={googleLoading}
|
|
179
|
+
/>
|
|
180
|
+
)}
|
|
181
|
+
|
|
182
|
+
{providers.includes('apple') && (
|
|
183
|
+
<SocialButton
|
|
184
|
+
provider="apple"
|
|
185
|
+
onPress={handleAppleSignIn}
|
|
186
|
+
loading={appleLoading}
|
|
187
|
+
/>
|
|
188
|
+
)}
|
|
189
|
+
|
|
190
|
+
<Button onPress={handleNavigateToRegister}>
|
|
191
|
+
Kayıt Ol
|
|
192
|
+
</Button>
|
|
193
|
+
</>
|
|
194
|
+
) : (
|
|
195
|
+
<>
|
|
196
|
+
<RegisterForm />
|
|
197
|
+
|
|
198
|
+
{/* Social login buttons */}
|
|
199
|
+
{providers.includes('google') && (
|
|
200
|
+
<SocialButton
|
|
201
|
+
provider="google"
|
|
202
|
+
onPress={handleGoogleSignIn}
|
|
203
|
+
loading={googleLoading}
|
|
204
|
+
/>
|
|
205
|
+
)}
|
|
206
|
+
|
|
207
|
+
{providers.includes('apple') && (
|
|
208
|
+
<SocialButton
|
|
209
|
+
provider="apple"
|
|
210
|
+
onPress={handleAppleSignIn}
|
|
211
|
+
loading={appleLoading}
|
|
212
|
+
/>
|
|
213
|
+
)}
|
|
214
|
+
|
|
215
|
+
<Button onPress={handleNavigateToLogin}>
|
|
216
|
+
Giriş Yap
|
|
217
|
+
</Button>
|
|
218
|
+
</>
|
|
219
|
+
)}
|
|
220
|
+
</View>
|
|
221
|
+
</BottomSheetModal>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Custom Social Login Handlers
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
function AuthBottomSheetWithCustomHandlers() {
|
|
230
|
+
const { showAuthModal } = useAuthModalStore();
|
|
231
|
+
|
|
232
|
+
const handleCustomGoogleSignIn = async () => {
|
|
233
|
+
try {
|
|
234
|
+
// Custom Google sign-in logic
|
|
235
|
+
const result = await customGoogleSignInFlow();
|
|
236
|
+
|
|
237
|
+
if (result.success) {
|
|
238
|
+
// Başarılı authentication sonrası modal otomatik kapanır
|
|
239
|
+
console.log('Google ile giriş başarılı');
|
|
240
|
+
}
|
|
241
|
+
} catch (error) {
|
|
242
|
+
Alert.alert('Hata', 'Google ile giriş başarısız');
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const handleCustomAppleSignIn = async () => {
|
|
247
|
+
try {
|
|
248
|
+
// Custom Apple sign-in logic
|
|
249
|
+
const result = await customAppleSignInFlow();
|
|
250
|
+
|
|
251
|
+
if (result.success) {
|
|
252
|
+
console.log('Apple ile giriş başarılı');
|
|
253
|
+
}
|
|
254
|
+
} catch (error) {
|
|
255
|
+
Alert.alert('Hata', 'Apple ile giriş başarısız');
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const {
|
|
260
|
+
modalRef,
|
|
261
|
+
mode,
|
|
262
|
+
googleLoading,
|
|
263
|
+
appleLoading,
|
|
264
|
+
handleDismiss,
|
|
265
|
+
handleNavigateToRegister,
|
|
266
|
+
handleNavigateToLogin,
|
|
267
|
+
handleGoogleSignIn,
|
|
268
|
+
handleAppleSignIn,
|
|
269
|
+
} = useAuthBottomSheet({
|
|
270
|
+
onGoogleSignIn: handleCustomGoogleSignIn,
|
|
271
|
+
onAppleSignIn: handleCustomAppleSignIn,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<BottomSheetModal ref={modalRef} onDismiss={handleDismiss}>
|
|
276
|
+
{/* Auth form content */}
|
|
277
|
+
</BottomSheetModal>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Auth Modal Tetikleme
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
function RequireAuthButton() {
|
|
286
|
+
const { showAuthModal } = useAuthModalStore();
|
|
287
|
+
const { isAuthenticated } = useAuth();
|
|
288
|
+
|
|
289
|
+
const handlePress = () => {
|
|
290
|
+
if (!isAuthenticated) {
|
|
291
|
+
// Login modal'ını göster
|
|
292
|
+
showAuthModal(undefined, 'login');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Auth gerektiren işlemi yap
|
|
297
|
+
console.log('İşlem başarılı');
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<Button onPress={handlePress}>
|
|
302
|
+
Auth Gerektiren İşlem
|
|
303
|
+
</Button>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Pending Callback ile
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
function AddToFavoritesButton() {
|
|
312
|
+
const { showAuthModal } = useAuthModalStore();
|
|
313
|
+
const { isAuthenticated } = useAuth();
|
|
314
|
+
|
|
315
|
+
const handleAddToFavorites = async () => {
|
|
316
|
+
if (!isAuthenticated) {
|
|
317
|
+
// Authentication sonrası çalışacak callback'i kaydet
|
|
318
|
+
showAuthModal(async () => {
|
|
319
|
+
await addToFavorites(postId);
|
|
320
|
+
Alert.alert('Başarılı', 'Favorilere eklendi');
|
|
321
|
+
}, 'login');
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Kullanıcı authenticated, doğrudan işlemi yap
|
|
326
|
+
await addToFavorites(postId);
|
|
327
|
+
Alert.alert('Başarılı', 'Favorilere eklendi');
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
return (
|
|
331
|
+
<Button onPress={handleAddToFavorites}>
|
|
332
|
+
Favorilere Ekle
|
|
333
|
+
</Button>
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Otomatik Kapanma
|
|
339
|
+
|
|
340
|
+
Hook, başarılı authentication sonrası otomatik olarak modal'ı kapatır:
|
|
341
|
+
|
|
342
|
+
- **Authenticated olmadan authenticated olmaya**: Kullanıcı giriş yaptığında
|
|
343
|
+
- **Anonymous'den authenticated olmaya**: Anonymous kullanıcı kayıt olduğunda
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// Kullanıcı giriş yapar
|
|
347
|
+
// → useAuth store güncellenir
|
|
348
|
+
// → useAuthBottomSheet bunu tespit eder
|
|
349
|
+
// → Modal otomatik kapanır
|
|
350
|
+
// → Pending callback çalıştırılır
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Provider Kontrolü
|
|
354
|
+
|
|
355
|
+
Hook hangi provider'ların mevcut olduğunu otomatik belirler:
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
const { providers } = useAuthBottomSheet({
|
|
359
|
+
socialConfig: {
|
|
360
|
+
google: { webClientId: '...' },
|
|
361
|
+
apple: { enabled: true },
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// iOS: ['apple', 'google'] veya ['google']
|
|
366
|
+
// Android: ['google']
|
|
367
|
+
// Web: ['google']
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Hata Yönetimi
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
function AuthBottomSheetWithErrorHandling() {
|
|
374
|
+
const {
|
|
375
|
+
modalRef,
|
|
376
|
+
mode,
|
|
377
|
+
handleGoogleSignIn,
|
|
378
|
+
handleAppleSignIn,
|
|
379
|
+
} = useAuthBottomSheet({
|
|
380
|
+
socialConfig: {
|
|
381
|
+
google: { webClientId: Config.GOOGLE_WEB_CLIENT_ID },
|
|
382
|
+
apple: { enabled: true },
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const handleGoogleSignInWithErrorHandling = async () => {
|
|
387
|
+
try {
|
|
388
|
+
await handleGoogleSignIn();
|
|
389
|
+
// Hook hata yönetimini içeride yapar
|
|
390
|
+
} catch (error) {
|
|
391
|
+
// Additional error handling if needed
|
|
392
|
+
Alert.alert('Hata', 'Bir sorun oluştu');
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
<BottomSheetModal ref={modalRef}>
|
|
398
|
+
<Button onPress={handleGoogleSignInWithErrorHandling}>
|
|
399
|
+
Google ile Giriş
|
|
400
|
+
</Button>
|
|
401
|
+
</BottomSheetModal>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## İlgili Component'ler
|
|
407
|
+
|
|
408
|
+
- [`AuthBottomSheet`](../components/AuthBottomSheet.md) - Bottom sheet component'i
|
|
409
|
+
- [`useAuthModalStore`](../stores/authModalStore.md) - Auth modal state store'u
|
|
410
|
+
- [`LoginForm`](../components/LoginForm.md) - Login form component'i
|
|
411
|
+
- [`RegisterForm`](../components/RegisterForm.md) - Register form component'i
|
|
412
|
+
|
|
413
|
+
## İlgili Hook'lar
|
|
414
|
+
|
|
415
|
+
- [`useGoogleAuth`](./useSocialLogin.md#usegoogleauth) - Google auth hook'u
|
|
416
|
+
- [`useAppleAuth`](./useSocialLogin.md#useappleauth) - Apple auth hook'u
|
|
417
|
+
- [`useAuth`](./useAuth.md) - Ana auth state yönetimi
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# useAuthRequired & useRequireAuth
|
|
2
|
+
|
|
3
|
+
Two hooks for authentication requirements in components.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## useAuthRequired
|
|
8
|
+
|
|
9
|
+
Check auth requirements and show modal if needed.
|
|
10
|
+
|
|
11
|
+
### Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { useAuthRequired } from '@umituz/react-native-auth';
|
|
15
|
+
|
|
16
|
+
function LikeButton() {
|
|
17
|
+
const { isAllowed, isLoading, requireAuth, checkAndRequireAuth } = useAuthRequired();
|
|
18
|
+
|
|
19
|
+
const handleLike = () => {
|
|
20
|
+
if (checkAndRequireAuth()) {
|
|
21
|
+
// User is authenticated, proceed
|
|
22
|
+
likePost();
|
|
23
|
+
}
|
|
24
|
+
// Otherwise, auth modal is shown automatically
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Button onPress={handleLike} disabled={isLoading}>
|
|
29
|
+
{isAllowed ? 'Like' : 'Sign in to like'}
|
|
30
|
+
</Button>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### API
|
|
36
|
+
|
|
37
|
+
| Prop | Type | Description |
|
|
38
|
+
|------|------|-------------|
|
|
39
|
+
| `isAllowed` | `boolean` | Whether user is authenticated (not anonymous) |
|
|
40
|
+
| `isLoading` | `boolean` | Whether auth is still loading |
|
|
41
|
+
| `userId` | `string \| null` | Current user ID (null if not authenticated) |
|
|
42
|
+
| `requireAuth` | `() => void` | Show auth modal |
|
|
43
|
+
| `checkAndRequireAuth` | `() => boolean` | Check and show modal if needed, returns true/false |
|
|
44
|
+
|
|
45
|
+
### Examples
|
|
46
|
+
|
|
47
|
+
#### Add to Favorites
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
function AddToFavoritesButton({ postId }) {
|
|
51
|
+
const { isAllowed, checkAndRequireAuth } = useAuthRequired();
|
|
52
|
+
|
|
53
|
+
const handleAddToFavorites = () => {
|
|
54
|
+
if (!checkAndRequireAuth()) {
|
|
55
|
+
return; // Auth modal opened, stop here
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// User authenticated, proceed
|
|
59
|
+
addToFavorites(postId);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<TouchableOpacity onPress={handleAddToFavorites}>
|
|
64
|
+
<HeartIcon filled={isAllowed} />
|
|
65
|
+
</TouchableOpacity>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Post Comment
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
function CommentForm({ postId }) {
|
|
74
|
+
const { isAllowed, requireAuth, userId } = useAuthRequired();
|
|
75
|
+
const [comment, setComment] = useState('');
|
|
76
|
+
|
|
77
|
+
const handleSubmit = () => {
|
|
78
|
+
if (!isAllowed) {
|
|
79
|
+
requireAuth(); // Open auth modal
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Submit comment
|
|
84
|
+
submitComment(postId, userId, comment);
|
|
85
|
+
setComment('');
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<View>
|
|
90
|
+
<TextInput
|
|
91
|
+
value={comment}
|
|
92
|
+
onChangeText={setComment}
|
|
93
|
+
placeholder={isAllowed ? "Write your comment..." : "Sign in to comment"}
|
|
94
|
+
editable={isAllowed}
|
|
95
|
+
/>
|
|
96
|
+
<Button onPress={handleSubmit} disabled={!isAllowed}>
|
|
97
|
+
{isAllowed ? 'Submit' : 'Sign In'}
|
|
98
|
+
</Button>
|
|
99
|
+
</View>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## useRequireAuth
|
|
107
|
+
|
|
108
|
+
For components that **must** have an authenticated user. Throws if not authenticated.
|
|
109
|
+
|
|
110
|
+
### Usage
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { useRequireAuth } from '@umituz/react-native-auth';
|
|
114
|
+
|
|
115
|
+
function UserProfile() {
|
|
116
|
+
const userId = useRequireAuth(); // string, not null
|
|
117
|
+
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
// userId is guaranteed to be string
|
|
120
|
+
fetchUserData(userId);
|
|
121
|
+
}, [userId]);
|
|
122
|
+
|
|
123
|
+
return <ProfileContent userId={userId} />;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### useUserId (Safe Alternative)
|
|
128
|
+
|
|
129
|
+
Returns null if user is not authenticated:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { useUserId } from '@umituz/react-native-auth';
|
|
133
|
+
|
|
134
|
+
function MaybeUserProfile() {
|
|
135
|
+
const userId = useUserId(); // string | null
|
|
136
|
+
|
|
137
|
+
if (!userId) {
|
|
138
|
+
return <LoginPrompt />;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return <ProfileContent userId={userId} />;
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Examples
|
|
146
|
+
|
|
147
|
+
#### Order History
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
function OrderHistory() {
|
|
151
|
+
const userId = useRequireAuth(); // User must be authenticated
|
|
152
|
+
|
|
153
|
+
const { data: orders, isLoading } = useQuery({
|
|
154
|
+
queryKey: ['orders', userId],
|
|
155
|
+
queryFn: () => fetchOrders(userId),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (isLoading) return <LoadingSpinner />;
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<FlatList
|
|
162
|
+
data={orders}
|
|
163
|
+
renderItem={({ item }) => <OrderCard order={item} />}
|
|
164
|
+
/>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### User Settings
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
function UserSettings() {
|
|
173
|
+
const userId = useRequireAuth();
|
|
174
|
+
|
|
175
|
+
const updateSettings = async (settings: UserSettings) => {
|
|
176
|
+
await updateUserSettings(userId, settings);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
return <SettingsForm onSave={updateSettings} />;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Error Handling
|
|
184
|
+
|
|
185
|
+
Wrap components using `useRequireAuth` with Error Boundary:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
function App() {
|
|
189
|
+
return (
|
|
190
|
+
<ErrorBoundary fallback={<LoginScreen />}>
|
|
191
|
+
<Routes>
|
|
192
|
+
<Route path="/settings" element={<UserSettings />} />
|
|
193
|
+
<Route path="/orders" element={<OrderHistory />} />
|
|
194
|
+
</Routes>
|
|
195
|
+
</ErrorBoundary>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Comparison Table
|
|
201
|
+
|
|
202
|
+
| Situation | Hook | Why |
|
|
203
|
+
|-----------|------|-----|
|
|
204
|
+
| Check auth before action | `useAuthRequired` | Can show modal, graceful degradation |
|
|
205
|
+
| Show auth modal | `useAuthRequired` | Has `requireAuth()` method |
|
|
206
|
+
| Component requires auth | `useRequireAuth` | Type-safe, non-null userId |
|
|
207
|
+
| Optional auth | `useUserId` | Safe, can return null |
|
|
208
|
+
|
|
209
|
+
## Code Comparison
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// ❌ Wrong - useRequireAuth with modal attempt
|
|
213
|
+
function BadComponent() {
|
|
214
|
+
const userId = useRequireAuth(); // Will throw!
|
|
215
|
+
|
|
216
|
+
const handleClick = () => {
|
|
217
|
+
// Never reaches here
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ✅ Good - useAuthRequired with modal
|
|
222
|
+
function GoodComponent() {
|
|
223
|
+
const { isAllowed, requireAuth } = useAuthRequired();
|
|
224
|
+
|
|
225
|
+
const handleClick = () => {
|
|
226
|
+
if (!isAllowed) {
|
|
227
|
+
requireAuth(); // Show modal
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// Perform action
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ✅ Good - useRequireAuth for required auth
|
|
235
|
+
function ProtectedComponent() {
|
|
236
|
+
const userId = useRequireAuth(); // Type-safe: string
|
|
237
|
+
|
|
238
|
+
// Use userId
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
fetchUserData(userId);
|
|
241
|
+
}, [userId]);
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Related Hooks
|
|
246
|
+
|
|
247
|
+
- [`useAuth`](./useAuth.md) - Main auth state management
|
|
248
|
+
- [`useAuthModalStore`](../stores/authModalStore.ts) - Auth modal state
|