@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.
@@ -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