@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,414 @@
|
|
|
1
|
+
# useAuthBottomSheet
|
|
2
|
+
|
|
3
|
+
Hook for authentication bottom sheet management. Handles login/register modal display and social authentication.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Bottom sheet modal management
|
|
8
|
+
- Login/Register mode switching
|
|
9
|
+
- Social authentication integration
|
|
10
|
+
- Auto-close on successful auth
|
|
11
|
+
- Pending callback management
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
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 | Type | Required | Description |
|
|
71
|
+
|-------|------|----------|-------------|
|
|
72
|
+
| `socialConfig` | `SocialAuthConfiguration` | No | Social auth configuration |
|
|
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 | Type | Description |
|
|
88
|
+
|------|------|-------------|
|
|
89
|
+
| `modalRef` | `RefObject<BottomSheetModalRef>` | Bottom sheet modal reference |
|
|
90
|
+
| `mode` | `'login' \| 'register'` | Current mode |
|
|
91
|
+
| `providers` | `SocialAuthProvider[]` | Available social providers |
|
|
92
|
+
| `googleLoading` | `boolean` | Google loading state |
|
|
93
|
+
| `appleLoading` | `boolean` | Apple loading state |
|
|
94
|
+
| `handleDismiss` | `() => void` | Dismiss modal |
|
|
95
|
+
| `handleClose` | `() => void` | Close modal and cleanup |
|
|
96
|
+
| `handleNavigateToRegister` | `() => void` | Switch to register mode |
|
|
97
|
+
| `handleNavigateToLogin` | `() => void` | Switch to login mode |
|
|
98
|
+
| `handleGoogleSignIn` | `() => Promise<void>` | Google sign-in handler |
|
|
99
|
+
| `handleAppleSignIn` | `() => Promise<void>` | Apple sign-in handler |
|
|
100
|
+
|
|
101
|
+
## Examples
|
|
102
|
+
|
|
103
|
+
### Basic 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>Sign In</Text>
|
|
121
|
+
<LoginForm />
|
|
122
|
+
<Button onPress={handleNavigateToRegister}>
|
|
123
|
+
Don't have an account? Sign Up
|
|
124
|
+
</Button>
|
|
125
|
+
</>
|
|
126
|
+
) : (
|
|
127
|
+
<>
|
|
128
|
+
<Text>Sign Up</Text>
|
|
129
|
+
<RegisterForm />
|
|
130
|
+
<Button onPress={handleNavigateToLogin}>
|
|
131
|
+
Already have an account? Sign In
|
|
132
|
+
</Button>
|
|
133
|
+
</>
|
|
134
|
+
)}
|
|
135
|
+
</View>
|
|
136
|
+
</BottomSheetModal>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### With Social Login
|
|
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
|
+
Sign Up
|
|
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
|
+
Sign In
|
|
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
|
+
const result = await customGoogleSignInFlow();
|
|
235
|
+
|
|
236
|
+
if (result.success) {
|
|
237
|
+
// Modal auto-closes on successful auth
|
|
238
|
+
console.log('Google sign-in successful');
|
|
239
|
+
}
|
|
240
|
+
} catch (error) {
|
|
241
|
+
Alert.alert('Error', 'Google sign-in failed');
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const handleCustomAppleSignIn = async () => {
|
|
246
|
+
try {
|
|
247
|
+
const result = await customAppleSignInFlow();
|
|
248
|
+
|
|
249
|
+
if (result.success) {
|
|
250
|
+
console.log('Apple sign-in successful');
|
|
251
|
+
}
|
|
252
|
+
} catch (error) {
|
|
253
|
+
Alert.alert('Error', 'Apple sign-in failed');
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const {
|
|
258
|
+
modalRef,
|
|
259
|
+
mode,
|
|
260
|
+
googleLoading,
|
|
261
|
+
appleLoading,
|
|
262
|
+
handleDismiss,
|
|
263
|
+
handleNavigateToRegister,
|
|
264
|
+
handleNavigateToLogin,
|
|
265
|
+
handleGoogleSignIn,
|
|
266
|
+
handleAppleSignIn,
|
|
267
|
+
} = useAuthBottomSheet({
|
|
268
|
+
onGoogleSignIn: handleCustomGoogleSignIn,
|
|
269
|
+
onAppleSignIn: handleCustomAppleSignIn,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<BottomSheetModal ref={modalRef} onDismiss={handleDismiss}>
|
|
274
|
+
{/* Auth form content */}
|
|
275
|
+
</BottomSheetModal>
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Triggering Auth Modal
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
function RequireAuthButton() {
|
|
284
|
+
const { showAuthModal } = useAuthModalStore();
|
|
285
|
+
const { isAuthenticated } = useAuth();
|
|
286
|
+
|
|
287
|
+
const handlePress = () => {
|
|
288
|
+
if (!isAuthenticated) {
|
|
289
|
+
// Show login modal
|
|
290
|
+
showAuthModal(undefined, 'login');
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Perform auth-required action
|
|
295
|
+
console.log('Action successful');
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<Button onPress={handlePress}>
|
|
300
|
+
Auth Required Action
|
|
301
|
+
</Button>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### With Pending Callback
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
function AddToFavoritesButton() {
|
|
310
|
+
const { showAuthModal } = useAuthModalStore();
|
|
311
|
+
const { isAuthenticated } = useAuth();
|
|
312
|
+
|
|
313
|
+
const handleAddToFavorites = async () => {
|
|
314
|
+
if (!isAuthenticated) {
|
|
315
|
+
// Save callback to run after successful auth
|
|
316
|
+
showAuthModal(async () => {
|
|
317
|
+
await addToFavorites(postId);
|
|
318
|
+
Alert.alert('Success', 'Added to favorites');
|
|
319
|
+
}, 'login');
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// User authenticated, perform action directly
|
|
324
|
+
await addToFavorites(postId);
|
|
325
|
+
Alert.alert('Success', 'Added to favorites');
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<Button onPress={handleAddToFavorites}>
|
|
330
|
+
Add to Favorites
|
|
331
|
+
</Button>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Auto-Close Behavior
|
|
337
|
+
|
|
338
|
+
The hook automatically closes the modal after successful authentication:
|
|
339
|
+
|
|
340
|
+
- **Not authenticated → Authenticated**: User signs in/logs in
|
|
341
|
+
- **Anonymous → Authenticated**: Anonymous user registers
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
// User signs in
|
|
345
|
+
// → useAuth store updates
|
|
346
|
+
// → useAuthBottomSheet detects this
|
|
347
|
+
// → Modal auto-closes
|
|
348
|
+
// → Pending callback executes
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Provider Detection
|
|
352
|
+
|
|
353
|
+
The hook automatically determines which providers are available:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
const { providers } = useAuthBottomSheet({
|
|
357
|
+
socialConfig: {
|
|
358
|
+
google: { webClientId: '...' },
|
|
359
|
+
apple: { enabled: true },
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// iOS: ['apple', 'google'] or ['google']
|
|
364
|
+
// Android: ['google']
|
|
365
|
+
// Web: ['google']
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Error Handling
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
function AuthBottomSheetWithErrorHandling() {
|
|
372
|
+
const {
|
|
373
|
+
modalRef,
|
|
374
|
+
mode,
|
|
375
|
+
handleGoogleSignIn,
|
|
376
|
+
handleAppleSignIn,
|
|
377
|
+
} = useAuthBottomSheet({
|
|
378
|
+
socialConfig: {
|
|
379
|
+
google: { webClientId: Config.GOOGLE_WEB_CLIENT_ID },
|
|
380
|
+
apple: { enabled: true },
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const handleGoogleSignInWithErrorHandling = async () => {
|
|
385
|
+
try {
|
|
386
|
+
await handleGoogleSignIn();
|
|
387
|
+
} catch (error) {
|
|
388
|
+
// Additional error handling if needed
|
|
389
|
+
Alert.alert('Error', 'Something went wrong');
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
return (
|
|
394
|
+
<BottomSheetModal ref={modalRef}>
|
|
395
|
+
<Button onPress={handleGoogleSignInWithErrorHandling}>
|
|
396
|
+
Sign in with Google
|
|
397
|
+
</Button>
|
|
398
|
+
</BottomSheetModal>
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Related Components
|
|
404
|
+
|
|
405
|
+
- [`AuthBottomSheet`](../components/AuthBottomSheet.md) - Bottom sheet component
|
|
406
|
+
- [`useAuthModalStore`](../stores/authModalStore.md) - Auth modal state store
|
|
407
|
+
- [`LoginForm`](../components/LoginForm.md) - Login form component
|
|
408
|
+
- [`RegisterForm`](../components/RegisterForm.md) - Register form component
|
|
409
|
+
|
|
410
|
+
## Related Hooks
|
|
411
|
+
|
|
412
|
+
- [`useGoogleAuth`](./useSocialLogin.md#usegoogleauth) - Google auth hook
|
|
413
|
+
- [`useAppleAuth`](./useSocialLogin.md#useappleauth) - Apple auth hook
|
|
414
|
+
- [`useAuth`](./useAuth.md) - Main auth state management
|
|
@@ -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
|