@umituz/react-native-subscription 2.14.96 → 2.14.98

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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +462 -0
  3. package/package.json +3 -3
  4. package/src/application/README.md +229 -0
  5. package/src/application/ports/README.md +103 -0
  6. package/src/domain/README.md +402 -0
  7. package/src/domain/constants/README.md +80 -0
  8. package/src/domain/entities/README.md +176 -0
  9. package/src/domain/errors/README.md +307 -0
  10. package/src/domain/value-objects/README.md +186 -0
  11. package/src/domains/config/README.md +390 -0
  12. package/src/domains/paywall/README.md +371 -0
  13. package/src/domains/paywall/components/PaywallHeader.tsx +8 -11
  14. package/src/domains/paywall/components/README.md +185 -0
  15. package/src/domains/paywall/entities/README.md +199 -0
  16. package/src/domains/paywall/hooks/README.md +129 -0
  17. package/src/domains/wallet/README.md +292 -0
  18. package/src/domains/wallet/domain/README.md +108 -0
  19. package/src/domains/wallet/domain/entities/README.md +122 -0
  20. package/src/domains/wallet/domain/errors/README.md +157 -0
  21. package/src/domains/wallet/infrastructure/README.md +96 -0
  22. package/src/domains/wallet/presentation/components/BalanceCard.tsx +6 -12
  23. package/src/domains/wallet/presentation/components/README.md +231 -0
  24. package/src/domains/wallet/presentation/hooks/README.md +255 -0
  25. package/src/infrastructure/README.md +514 -0
  26. package/src/infrastructure/mappers/README.md +34 -0
  27. package/src/infrastructure/models/README.md +26 -0
  28. package/src/infrastructure/repositories/README.md +385 -0
  29. package/src/infrastructure/services/README.md +374 -0
  30. package/src/presentation/README.md +410 -0
  31. package/src/presentation/components/README.md +183 -0
  32. package/src/presentation/components/details/CreditRow.md +337 -0
  33. package/src/presentation/components/details/DetailRow.md +283 -0
  34. package/src/presentation/components/details/PremiumDetailsCard.md +266 -0
  35. package/src/presentation/components/details/PremiumStatusBadge.md +266 -0
  36. package/src/presentation/components/details/README.md +449 -0
  37. package/src/presentation/components/feedback/PaywallFeedbackModal.md +314 -0
  38. package/src/presentation/components/feedback/README.md +447 -0
  39. package/src/presentation/components/paywall/PaywallModal.md +444 -0
  40. package/src/presentation/components/paywall/README.md +190 -0
  41. package/src/presentation/components/sections/README.md +468 -0
  42. package/src/presentation/components/sections/SubscriptionSection.md +246 -0
  43. package/src/presentation/hooks/README.md +743 -0
  44. package/src/presentation/hooks/useAuthAwarePurchase.md +359 -0
  45. package/src/presentation/hooks/useAuthGate.md +403 -0
  46. package/src/presentation/hooks/useAuthSubscriptionSync.md +398 -0
  47. package/src/presentation/hooks/useCreditChecker.md +407 -0
  48. package/src/presentation/hooks/useCredits.md +342 -0
  49. package/src/presentation/hooks/useCreditsGate.md +346 -0
  50. package/src/presentation/hooks/useDeductCredit.md +160 -0
  51. package/src/presentation/hooks/useDevTestCallbacks.md +422 -0
  52. package/src/presentation/hooks/useFeatureGate.md +157 -0
  53. package/src/presentation/hooks/useInitializeCredits.md +458 -0
  54. package/src/presentation/hooks/usePaywall.md +334 -0
  55. package/src/presentation/hooks/usePaywallOperations.md +486 -0
  56. package/src/presentation/hooks/usePaywallVisibility.md +344 -0
  57. package/src/presentation/hooks/usePremium.md +230 -0
  58. package/src/presentation/hooks/usePremiumGate.md +423 -0
  59. package/src/presentation/hooks/usePremiumWithCredits.md +429 -0
  60. package/src/presentation/hooks/useSubscription.md +450 -0
  61. package/src/presentation/hooks/useSubscriptionDetails.md +438 -0
  62. package/src/presentation/hooks/useSubscriptionGate.md +168 -0
  63. package/src/presentation/hooks/useSubscriptionSettingsConfig.md +374 -0
  64. package/src/presentation/hooks/useSubscriptionStatus.md +424 -0
  65. package/src/presentation/hooks/useUserTier.md +356 -0
  66. package/src/presentation/hooks/useUserTierWithRepository.md +452 -0
  67. package/src/presentation/screens/README.md +194 -0
  68. package/src/presentation/types/README.md +38 -0
  69. package/src/presentation/utils/README.md +52 -0
  70. package/src/revenuecat/README.md +523 -0
  71. package/src/revenuecat/domain/README.md +147 -0
  72. package/src/revenuecat/domain/errors/README.md +197 -0
  73. package/src/revenuecat/infrastructure/config/README.md +40 -0
  74. package/src/revenuecat/infrastructure/managers/README.md +49 -0
  75. package/src/revenuecat/presentation/hooks/README.md +56 -0
  76. package/src/revenuecat/presentation/hooks/usePurchasePackage.ts +1 -1
  77. package/src/utils/README.md +529 -0
@@ -0,0 +1,450 @@
1
+ # useSubscription Hook
2
+
3
+ Core hook for subscription status management and operations.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { useSubscription } from '@umituz/react-native-subscription';
9
+ ```
10
+
11
+ ## Signature
12
+
13
+ ```typescript
14
+ function useSubscription(): {
15
+ status: SubscriptionStatus | null;
16
+ loading: boolean;
17
+ error: string | null;
18
+ isPremium: boolean;
19
+ loadStatus: (userId: string) => Promise<void>;
20
+ refreshStatus: (userId: string) => Promise<void>;
21
+ activateSubscription: (
22
+ userId: string,
23
+ productId: string,
24
+ expiresAt: string | null,
25
+ ) => Promise<void>;
26
+ deactivateSubscription: (userId: string) => Promise<void>;
27
+ }
28
+ ```
29
+
30
+ ## Returns
31
+
32
+ | Property | Type | Description |
33
+ |----------|------|-------------|
34
+ | `status` | `SubscriptionStatus \| null` | Current subscription status object |
35
+ | `loading` | `boolean` | Loading state |
36
+ | `error` | `string \| null` | Error message if any |
37
+ | `isPremium` | `boolean` | Whether user has active subscription |
38
+ | `loadStatus` | `(userId) => Promise<void>` | Load subscription status |
39
+ | `refreshStatus` | `(userId) => Promise<void>` | Refresh subscription status |
40
+ | `activateSubscription` | `(userId, productId, expiresAt) => Promise<void>` | Activate subscription |
41
+ | `deactivateSubscription` | `(userId) => Promise<void>` | Deactivate subscription |
42
+
43
+ ## Basic Usage
44
+
45
+ ```typescript
46
+ function SubscriptionStatus() {
47
+ const { user } = useAuth();
48
+ const { status, isPremium, loading, loadStatus } = useSubscription();
49
+
50
+ useEffect(() => {
51
+ if (user?.uid) {
52
+ loadStatus(user.uid);
53
+ }
54
+ }, [user]);
55
+
56
+ if (loading) return <ActivityIndicator />;
57
+
58
+ return (
59
+ <View>
60
+ <Text>Status: {isPremium ? 'Premium' : 'Free'}</Text>
61
+ {status && (
62
+ <Text>Product: {status.productId}</Text>
63
+ )}
64
+ </View>
65
+ );
66
+ }
67
+ ```
68
+
69
+ ## Advanced Usage
70
+
71
+ ### With Auto-Refresh
72
+
73
+ ```typescript
74
+ function SubscriptionWithRefresh() {
75
+ const { user } = useAuth();
76
+ const { status, isPremium, loading, refreshStatus } = useSubscription();
77
+
78
+ useEffect(() => {
79
+ if (user?.uid) {
80
+ refreshStatus(user.uid);
81
+ }
82
+ }, [user]);
83
+
84
+ // Refresh when app comes to foreground
85
+ useFocusEffect(
86
+ useCallback(() => {
87
+ if (user?.uid) {
88
+ refreshStatus(user.uid);
89
+ }
90
+ }, [user])
91
+ );
92
+
93
+ // Refresh every 5 minutes
94
+ useEffect(() => {
95
+ const interval = setInterval(() => {
96
+ if (user?.uid) {
97
+ refreshStatus(user.uid);
98
+ }
99
+ }, 5 * 60 * 1000);
100
+
101
+ return () => clearInterval(interval);
102
+ }, [user]);
103
+
104
+ if (loading) return <LoadingScreen />;
105
+
106
+ return (
107
+ <View>
108
+ <Text>{isPremium ? 'Premium' : 'Free'} User</Text>
109
+ </View>
110
+ );
111
+ }
112
+ ```
113
+
114
+ ### With Manual Activation
115
+
116
+ ```typescript
117
+ function SubscriptionManager() {
118
+ const { user } = useAuth();
119
+ const {
120
+ status,
121
+ activateSubscription,
122
+ deactivateSubscription,
123
+ loading,
124
+ } = useSubscription();
125
+
126
+ const handleActivate = async () => {
127
+ if (!user?.uid) return;
128
+
129
+ try {
130
+ await activateSubscription(
131
+ user.uid,
132
+ 'premium_monthly',
133
+ new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()
134
+ );
135
+ Alert.alert('Success', 'Subscription activated');
136
+ } catch (error) {
137
+ Alert.alert('Error', 'Failed to activate subscription');
138
+ }
139
+ };
140
+
141
+ const handleDeactivate = async () => {
142
+ if (!user?.uid) return;
143
+
144
+ try {
145
+ await deactivateSubscription(user.uid);
146
+ Alert.alert('Success', 'Subscription deactivated');
147
+ } catch (error) {
148
+ Alert.alert('Error', 'Failed to deactivate subscription');
149
+ }
150
+ };
151
+
152
+ return (
153
+ <View>
154
+ <Text>Status: {status?.type || 'None'}</Text>
155
+
156
+ <Button
157
+ onPress={handleActivate}
158
+ disabled={loading}
159
+ title="Activate Subscription"
160
+ />
161
+
162
+ <Button
163
+ onPress={handleDeactivate}
164
+ disabled={loading || !status}
165
+ title="Deactivate Subscription"
166
+ />
167
+ </View>
168
+ );
169
+ }
170
+ ```
171
+
172
+ ### With Error Handling
173
+
174
+ ```typescript
175
+ function RobustSubscription() {
176
+ const { user } = useAuth();
177
+ const { status, error, loading, loadStatus } = useSubscription();
178
+
179
+ useEffect(() => {
180
+ if (user?.uid) {
181
+ loadStatus(user.uid).catch((err) => {
182
+ console.error('Failed to load subscription:', err);
183
+ });
184
+ }
185
+ }, [user]);
186
+
187
+ useEffect(() => {
188
+ if (error) {
189
+ Alert.alert(
190
+ 'Subscription Error',
191
+ error,
192
+ [{ text: 'Retry', onPress: () => user && loadStatus(user.uid) }]
193
+ );
194
+ }
195
+ }, [error]);
196
+
197
+ if (loading) return <LoadingScreen />;
198
+
199
+ return (
200
+ <View>
201
+ {error ? (
202
+ <ErrorBanner message={error} />
203
+ ) : (
204
+ <SubscriptionDisplay status={status} />
205
+ )}
206
+ </View>
207
+ );
208
+ }
209
+ ```
210
+
211
+ ### With Status Polling
212
+
213
+ ```typescript
214
+ function PollingSubscription() {
215
+ const { user } = useAuth();
216
+ const { status, refreshStatus, loading } = useSubscription();
217
+
218
+ const [isPolling, setIsPolling] = useState(false);
219
+
220
+ useEffect(() => {
221
+ if (!user?.uid) return;
222
+
223
+ // Initial load
224
+ refreshStatus(user.uid);
225
+
226
+ // Poll for status updates
227
+ const interval = setInterval(async () => {
228
+ if (!isPolling) {
229
+ setIsPolling(true);
230
+ try {
231
+ await refreshStatus(user.uid);
232
+ } finally {
233
+ setIsPolling(false);
234
+ }
235
+ }
236
+ }, 30000); // Every 30 seconds
237
+
238
+ return () => clearInterval(interval);
239
+ }, [user]);
240
+
241
+ return (
242
+ <View>
243
+ <Text>Status: {status?.type || 'Loading...'}</Text>
244
+ {loading && <ActivityIndicator size="small" />}
245
+ </View>
246
+ );
247
+ }
248
+ ```
249
+
250
+ ## Examples
251
+
252
+ ### Complete Subscription Dashboard
253
+
254
+ ```typescript
255
+ function SubscriptionDashboard() {
256
+ const { user } = useAuth();
257
+ const {
258
+ status,
259
+ isPremium,
260
+ loading,
261
+ error,
262
+ refreshStatus,
263
+ } = useSubscription();
264
+
265
+ useEffect(() => {
266
+ if (user?.uid) {
267
+ refreshStatus(user.uid);
268
+ }
269
+ }, [user]);
270
+
271
+ useFocusEffect(
272
+ useCallback(() => {
273
+ if (user?.uid) {
274
+ refreshStatus(user.uid);
275
+ }
276
+ }, [user])
277
+ );
278
+
279
+ if (loading) {
280
+ return <LoadingScreen />;
281
+ }
282
+
283
+ return (
284
+ <ScrollView>
285
+ {error && <ErrorBanner message={error} />}
286
+
287
+ <SubscriptionCard
288
+ status={status}
289
+ isPremium={isPremium}
290
+ />
291
+
292
+ {status && (
293
+ <>
294
+ <DetailRow
295
+ label="Product ID"
296
+ value={status.productId}
297
+ />
298
+ <DetailRow
299
+ label="Status"
300
+ value={status.isActive ? 'Active' : 'Inactive'}
301
+ />
302
+ {status.expirationDate && (
303
+ <DetailRow
304
+ label="Expires"
305
+ value={new Date(status.expirationDate).toLocaleDateString()}
306
+ />
307
+ )}
308
+ </>
309
+ )}
310
+
311
+ <Button
312
+ onPress={() => user && refreshStatus(user.uid)}
313
+ title="Refresh Status"
314
+ />
315
+ </ScrollView>
316
+ );
317
+ }
318
+ ```
319
+
320
+ ### Subscription State Machine
321
+
322
+ ```typescript
323
+ function SubscriptionStateMachine() {
324
+ const { user } = useAuth();
325
+ const { status, loading, isPremium } = useSubscription();
326
+
327
+ useEffect(() => {
328
+ if (user?.uid) {
329
+ loadStatus(user.uid);
330
+ }
331
+ }, [user]);
332
+
333
+ const getState = () => {
334
+ if (loading) return 'loading';
335
+ if (!status) return 'none';
336
+ if (!status.isActive) return 'expired';
337
+ if (isPremium) return 'active';
338
+ return 'inactive';
339
+ };
340
+
341
+ const state = getState();
342
+
343
+ switch (state) {
344
+ case 'loading':
345
+ return <LoadingScreen />;
346
+ case 'none':
347
+ return <EmptyState message="No subscription found" />;
348
+ case 'expired':
349
+ return <ExpiredState onRenew={() => navigation.navigate('Paywall')} />;
350
+ case 'active':
351
+ return <PremiumContent />;
352
+ default:
353
+ return <FreeContent />;
354
+ }
355
+ }
356
+ ```
357
+
358
+ ### With Analytics Tracking
359
+
360
+ ```typescript
361
+ function TrackedSubscription() {
362
+ const { user } = useAuth();
363
+ const { status, isPremium, loading, loadStatus } = useSubscription();
364
+
365
+ const previousStatus = useRef(status);
366
+
367
+ useEffect(() => {
368
+ if (user?.uid) {
369
+ loadStatus(user.uid);
370
+ }
371
+ }, [user]);
372
+
373
+ useEffect(() => {
374
+ // Track subscription status changes
375
+ if (previousStatus.current?.isActive !== status?.isActive) {
376
+ analytics.track('subscription_status_changed', {
377
+ from: previousStatus.current?.isActive,
378
+ to: status?.isActive,
379
+ userId: user?.uid,
380
+ });
381
+ }
382
+
383
+ if (previousStatus.current?.type !== status?.type) {
384
+ analytics.track('subscription_type_changed', {
385
+ from: previousStatus.current?.type,
386
+ to: status?.type,
387
+ userId: user?.uid,
388
+ });
389
+ }
390
+
391
+ previousStatus.current = status;
392
+ }, [status]);
393
+
394
+ if (loading) return <LoadingScreen />;
395
+
396
+ return (
397
+ <View>
398
+ <Text>Status: {isPremium ? 'Premium' : 'Free'}</Text>
399
+ </View>
400
+ );
401
+ }
402
+ ```
403
+
404
+ ## Best Practices
405
+
406
+ 1. **Validate user ID** - Always check userId before operations
407
+ 2. **Handle loading** - Show loading states appropriately
408
+ 3. **Handle errors** - Display user-friendly error messages
409
+ 4. **Refresh on focus** - Update when screen regains focus
410
+ 5. **Auto-load** - Load status when user changes
411
+ 6. **Track changes** - Monitor status transitions
412
+ 7. **Cache wisely** - Don't over-refresh
413
+
414
+ ## Common Patterns
415
+
416
+ ### Basic Load
417
+
418
+ ```typescript
419
+ const { status, loadStatus } = useSubscription();
420
+ useEffect(() => {
421
+ if (user?.uid) loadStatus(user.uid);
422
+ }, [user]);
423
+ ```
424
+
425
+ ### With Refresh
426
+
427
+ ```typescript
428
+ const { status, refreshStatus } = useSubscription();
429
+ const handleRefresh = () => user && refreshStatus(user.uid);
430
+ ```
431
+
432
+ ### Manual Activation
433
+
434
+ ```typescript
435
+ const { activateSubscription } = useSubscription();
436
+ await activateSubscription(userId, productId, expiresAt);
437
+ ```
438
+
439
+ ## Related Hooks
440
+
441
+ - **usePremium** - Simplified premium checking
442
+ - **useSubscriptionStatus** - Detailed subscription status
443
+ - **useSubscriptionDetails** - Package and pricing info
444
+ - **useUserTier** - Tier information
445
+
446
+ ## See Also
447
+
448
+ - [Subscription Entity](../../domain/entities/SubscriptionStatus.md)
449
+ - [Subscription Service](../../infrastructure/services/SubscriptionService.md)
450
+ - [Status Utilities](../../utils/subscriptionUtils.md)