@umituz/react-native-subscription 2.14.97 → 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.
- package/LICENSE +21 -0
- package/README.md +462 -0
- package/package.json +1 -3
- package/src/application/README.md +229 -0
- package/src/application/ports/README.md +103 -0
- package/src/domain/README.md +402 -0
- package/src/domain/constants/README.md +80 -0
- package/src/domain/entities/README.md +176 -0
- package/src/domain/errors/README.md +307 -0
- package/src/domain/value-objects/README.md +186 -0
- package/src/domains/config/README.md +390 -0
- package/src/domains/paywall/README.md +371 -0
- package/src/domains/paywall/components/PaywallHeader.tsx +8 -11
- package/src/domains/paywall/components/README.md +185 -0
- package/src/domains/paywall/entities/README.md +199 -0
- package/src/domains/paywall/hooks/README.md +129 -0
- package/src/domains/wallet/README.md +292 -0
- package/src/domains/wallet/domain/README.md +108 -0
- package/src/domains/wallet/domain/entities/README.md +122 -0
- package/src/domains/wallet/domain/errors/README.md +157 -0
- package/src/domains/wallet/infrastructure/README.md +96 -0
- package/src/domains/wallet/presentation/components/BalanceCard.tsx +6 -12
- package/src/domains/wallet/presentation/components/README.md +231 -0
- package/src/domains/wallet/presentation/hooks/README.md +255 -0
- package/src/infrastructure/README.md +514 -0
- package/src/infrastructure/mappers/README.md +34 -0
- package/src/infrastructure/models/README.md +26 -0
- package/src/infrastructure/repositories/README.md +385 -0
- package/src/infrastructure/services/README.md +374 -0
- package/src/presentation/README.md +410 -0
- package/src/presentation/components/README.md +183 -0
- package/src/presentation/components/details/CreditRow.md +337 -0
- package/src/presentation/components/details/DetailRow.md +283 -0
- package/src/presentation/components/details/PremiumDetailsCard.md +266 -0
- package/src/presentation/components/details/PremiumStatusBadge.md +266 -0
- package/src/presentation/components/details/README.md +449 -0
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +314 -0
- package/src/presentation/components/feedback/README.md +447 -0
- package/src/presentation/components/paywall/PaywallModal.md +444 -0
- package/src/presentation/components/paywall/README.md +190 -0
- package/src/presentation/components/sections/README.md +468 -0
- package/src/presentation/components/sections/SubscriptionSection.md +246 -0
- package/src/presentation/hooks/README.md +743 -0
- package/src/presentation/hooks/useAuthAwarePurchase.md +359 -0
- package/src/presentation/hooks/useAuthGate.md +403 -0
- package/src/presentation/hooks/useAuthSubscriptionSync.md +398 -0
- package/src/presentation/hooks/useCreditChecker.md +407 -0
- package/src/presentation/hooks/useCredits.md +342 -0
- package/src/presentation/hooks/useCreditsGate.md +346 -0
- package/src/presentation/hooks/useDeductCredit.md +160 -0
- package/src/presentation/hooks/useDevTestCallbacks.md +422 -0
- package/src/presentation/hooks/useFeatureGate.md +157 -0
- package/src/presentation/hooks/useInitializeCredits.md +458 -0
- package/src/presentation/hooks/usePaywall.md +334 -0
- package/src/presentation/hooks/usePaywallOperations.md +486 -0
- package/src/presentation/hooks/usePaywallVisibility.md +344 -0
- package/src/presentation/hooks/usePremium.md +230 -0
- package/src/presentation/hooks/usePremiumGate.md +423 -0
- package/src/presentation/hooks/usePremiumWithCredits.md +429 -0
- package/src/presentation/hooks/useSubscription.md +450 -0
- package/src/presentation/hooks/useSubscriptionDetails.md +438 -0
- package/src/presentation/hooks/useSubscriptionGate.md +168 -0
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +374 -0
- package/src/presentation/hooks/useSubscriptionStatus.md +424 -0
- package/src/presentation/hooks/useUserTier.md +356 -0
- package/src/presentation/hooks/useUserTierWithRepository.md +452 -0
- package/src/presentation/screens/README.md +194 -0
- package/src/presentation/types/README.md +38 -0
- package/src/presentation/utils/README.md +52 -0
- package/src/revenuecat/README.md +523 -0
- package/src/revenuecat/domain/README.md +147 -0
- package/src/revenuecat/domain/errors/README.md +197 -0
- package/src/revenuecat/infrastructure/config/README.md +40 -0
- package/src/revenuecat/infrastructure/managers/README.md +49 -0
- package/src/revenuecat/presentation/hooks/README.md +56 -0
- 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)
|