@umituz/react-native-subscription 2.14.99 → 2.14.100
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 +211 -394
- package/package.json +1 -1
- package/src/application/README.md +46 -225
- package/src/application/ports/README.md +42 -97
- package/src/domain/README.md +36 -384
- package/src/domain/constants/README.md +0 -56
- package/src/domain/entities/README.md +43 -169
- package/src/domain/errors/README.md +33 -287
- package/src/domain/value-objects/README.md +43 -179
- package/src/domains/README.md +50 -238
- package/src/domains/README.md.bak +274 -0
- package/src/domains/config/README.md +93 -383
- package/src/domains/config/domain/README.md +23 -376
- package/src/domains/config/domain/entities/README.md +34 -343
- package/src/domains/paywall/README.md +99 -369
- package/src/domains/paywall/components/README.md +34 -178
- package/src/domains/paywall/entities/README.md +34 -193
- package/src/domains/paywall/hooks/README.md +34 -122
- package/src/domains/wallet/README.md +34 -275
- package/src/domains/wallet/README.md.bak +209 -0
- package/src/domains/wallet/domain/README.md +34 -101
- package/src/domains/wallet/domain/entities/README.md +34 -115
- package/src/domains/wallet/domain/errors/README.md +34 -151
- package/src/domains/wallet/infrastructure/README.md +34 -89
- package/src/domains/wallet/presentation/components/README.md +34 -224
- package/src/domains/wallet/presentation/hooks/README.md +34 -248
- package/src/infrastructure/README.md +37 -496
- package/src/infrastructure/mappers/README.md +0 -13
- package/src/infrastructure/repositories/README.md +74 -360
- package/src/infrastructure/services/README.md +95 -370
- package/src/presentation/README.md +123 -408
- package/src/presentation/README.md.bak +172 -0
- package/src/presentation/components/README.md +151 -179
- package/src/presentation/components/README.md.bak +217 -0
- package/src/presentation/components/details/CreditRow.md +65 -310
- package/src/presentation/components/details/DetailRow.md +63 -255
- package/src/presentation/components/details/PremiumDetailsCard.md +65 -238
- package/src/presentation/components/details/PremiumStatusBadge.md +64 -239
- package/src/presentation/components/details/README.md +97 -447
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +63 -287
- package/src/presentation/components/feedback/README.md +97 -445
- package/src/presentation/components/paywall/PaywallModal.md +66 -416
- package/src/presentation/components/paywall/README.md +50 -186
- package/src/presentation/components/sections/README.md +97 -466
- package/src/presentation/components/sections/SubscriptionSection.md +92 -244
- package/src/presentation/hooks/README.md +154 -741
- package/src/presentation/hooks/useAuthAwarePurchase.md +58 -325
- package/src/presentation/hooks/useAuthGate.md +61 -375
- package/src/presentation/hooks/useAuthSubscriptionSync.md +66 -370
- package/src/presentation/hooks/useCreditChecker.md +73 -378
- package/src/presentation/hooks/useCredits.md +74 -313
- package/src/presentation/hooks/useCredits.md.bak +231 -0
- package/src/presentation/hooks/useCreditsGate.md +66 -318
- package/src/presentation/hooks/useDeductCredit.md +0 -76
- package/src/presentation/hooks/useDevTestCallbacks.md +63 -394
- package/src/presentation/hooks/useFeatureGate.md +105 -150
- package/src/presentation/hooks/useFeatureGate.md.bak +284 -0
- package/src/presentation/hooks/useInitializeCredits.md +64 -430
- package/src/presentation/hooks/usePaywall.md +61 -306
- package/src/presentation/hooks/usePaywallOperations.md +64 -458
- package/src/presentation/hooks/usePaywallVisibility.md +67 -316
- package/src/presentation/hooks/usePremium.md +84 -226
- package/src/presentation/hooks/usePremiumGate.md +60 -395
- package/src/presentation/hooks/usePremiumWithCredits.md +64 -401
- package/src/presentation/hooks/useSubscription.md +66 -422
- package/src/presentation/hooks/useSubscriptionDetails.md +65 -410
- package/src/presentation/hooks/useSubscriptionGate.md +80 -164
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +66 -346
- package/src/presentation/hooks/useSubscriptionStatus.md +66 -396
- package/src/presentation/hooks/useUserTier.md +63 -328
- package/src/presentation/hooks/useUserTierWithRepository.md +64 -424
- package/src/presentation/screens/README.md +48 -190
- package/src/presentation/types/README.md +0 -16
- package/src/presentation/utils/README.md +0 -21
- package/src/revenuecat/README.md +99 -518
- package/src/revenuecat/application/README.md +35 -150
- package/src/revenuecat/application/ports/README.md +34 -162
- package/src/revenuecat/domain/README.md +42 -141
- package/src/revenuecat/domain/constants/README.md +34 -176
- package/src/revenuecat/domain/entities/README.md +34 -374
- package/src/revenuecat/domain/errors/README.md +47 -191
- package/src/revenuecat/domain/types/README.md +34 -366
- package/src/revenuecat/domain/value-objects/README.md +34 -434
- package/src/revenuecat/infrastructure/README.md +34 -43
- package/src/revenuecat/infrastructure/config/README.md +32 -23
- package/src/revenuecat/infrastructure/handlers/README.md +34 -211
- package/src/revenuecat/infrastructure/managers/README.md +34 -42
- package/src/revenuecat/infrastructure/services/README.md +35 -318
- package/src/revenuecat/infrastructure/utils/README.md +34 -375
- package/src/revenuecat/presentation/README.md +34 -176
- package/src/revenuecat/presentation/hooks/README.md +29 -35
- package/src/utils/README.md +38 -525
|
@@ -1,407 +1,102 @@
|
|
|
1
1
|
# useCreditChecker Hook
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Simple hook for checking if user has sufficient credits before operations.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Location
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
import { useCreditChecker } from '@umituz/react-native-subscription';
|
|
9
|
-
```
|
|
7
|
+
**Import Path**: `@umituz/react-native-subscription`
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
**File**: `src/presentation/hooks/useCreditChecker.ts`
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
function useCreditChecker(params?: {
|
|
15
|
-
onCreditDeducted?: (userId: string, cost: number) => void;
|
|
16
|
-
}): {
|
|
17
|
-
checkCreditsAvailable: (
|
|
18
|
-
userId: string | undefined,
|
|
19
|
-
cost?: number
|
|
20
|
-
) => Promise<CreditCheckResult>;
|
|
21
|
-
deductCreditsAfterSuccess: (
|
|
22
|
-
userId: string | undefined,
|
|
23
|
-
cost?: number
|
|
24
|
-
) => Promise<void>;
|
|
25
|
-
}
|
|
26
|
-
```
|
|
11
|
+
**Type**: Hook
|
|
27
12
|
|
|
28
|
-
##
|
|
13
|
+
## Strategy
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
|-----------|------|---------|-------------|
|
|
32
|
-
| `onCreditDeducted` | `(userId, cost) => void` | `undefined` | Callback after successful deduction |
|
|
15
|
+
### Credit Validation Flow
|
|
33
16
|
|
|
34
|
-
|
|
17
|
+
1. **Initial Check**
|
|
18
|
+
- Compare current balance against required amount
|
|
19
|
+
- Return boolean result immediately
|
|
20
|
+
- No network calls required (uses cached balance)
|
|
35
21
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
2. **Real-time Updates**
|
|
23
|
+
- Automatically re-checks when credits change
|
|
24
|
+
- Updates result via `useCredits` hook
|
|
25
|
+
- Triggers re-renders on balance changes
|
|
40
26
|
|
|
41
|
-
|
|
27
|
+
3. **Manual Refresh**
|
|
28
|
+
- Optionally trigger manual credit check
|
|
29
|
+
- Useful before expensive operations
|
|
30
|
+
- Validates current state before action
|
|
42
31
|
|
|
43
|
-
|
|
44
|
-
interface CreditCheckResult {
|
|
45
|
-
hasCredits: boolean;
|
|
46
|
-
currentBalance: number;
|
|
47
|
-
required: number;
|
|
48
|
-
canProceed: boolean;
|
|
49
|
-
}
|
|
50
|
-
```
|
|
32
|
+
### Integration Points
|
|
51
33
|
|
|
52
|
-
|
|
34
|
+
- **useCredits**: For fetching current credit balance
|
|
35
|
+
- **useDeductCredit**: For deducting credits after check
|
|
36
|
+
- **Credit Checking UI**: Pre-action validation displays
|
|
37
|
+
- **Purchase Flows**: Redirect to credit packages
|
|
53
38
|
|
|
54
|
-
|
|
55
|
-
function CreditFeature() {
|
|
56
|
-
const { user } = useAuth();
|
|
57
|
-
const { checkCreditsAvailable } = useCreditChecker();
|
|
39
|
+
## Restrictions
|
|
58
40
|
|
|
59
|
-
|
|
60
|
-
const result = await checkCreditsAvailable(user?.uid, 5);
|
|
41
|
+
### REQUIRED
|
|
61
42
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
`You need ${result.required} credits but only have ${result.currentBalance}`
|
|
66
|
-
);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
43
|
+
- **Required Amount**: MUST specify positive credit cost
|
|
44
|
+
- **Balance Check**: MUST verify `hasEnoughCredits` before action
|
|
45
|
+
- **User Feedback**: MUST show credit cost to user
|
|
69
46
|
|
|
70
|
-
|
|
71
|
-
await executeFeature();
|
|
72
|
-
};
|
|
47
|
+
### PROHIBITED
|
|
73
48
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
49
|
+
- **NEVER** assume credits are sufficient without checking
|
|
50
|
+
- **NEVER** use for security decisions (server-side validation required)
|
|
51
|
+
- **DO NOT** deduct credits with this hook (use `useDeductCredit`)
|
|
52
|
+
- **NEVER** hardcode credit costs (should be configurable)
|
|
77
53
|
|
|
78
|
-
|
|
54
|
+
### CRITICAL SAFETY
|
|
79
55
|
|
|
80
|
-
|
|
56
|
+
- **ALWAYS** check return value before proceeding with action
|
|
57
|
+
- **ALWAYS** show credit cost to user before execution
|
|
58
|
+
- **NEVER** trust client-side check for security (server must validate)
|
|
59
|
+
- **MUST** handle case where credits become insufficient between check and action
|
|
81
60
|
|
|
82
|
-
|
|
83
|
-
function CreditWithCallback() {
|
|
84
|
-
const { user } = useAuth();
|
|
61
|
+
## AI Agent Guidelines
|
|
85
62
|
|
|
86
|
-
|
|
87
|
-
onCreditDeducted: (userId, cost) => {
|
|
88
|
-
console.log(`Deducted ${cost} credits from ${userId}`);
|
|
89
|
-
analytics.track('credits_deducted', { userId, cost });
|
|
90
|
-
},
|
|
91
|
-
});
|
|
63
|
+
### When Implementing Credit Checks
|
|
92
64
|
|
|
93
|
-
|
|
94
|
-
|
|
65
|
+
1. **Always** specify positive credit cost
|
|
66
|
+
2. **Always** check `hasEnoughCredits` before action
|
|
67
|
+
3. **Always** show credit cost to user in UI
|
|
68
|
+
4. **Always** provide upgrade path when insufficient
|
|
69
|
+
5. **Never** use for security decisions (server validation required)
|
|
95
70
|
|
|
96
|
-
|
|
97
|
-
showPaywall();
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
71
|
+
### Integration Checklist
|
|
100
72
|
|
|
101
|
-
|
|
102
|
-
|
|
73
|
+
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
74
|
+
- [ ] Specify positive credit cost
|
|
75
|
+
- [ ] Check `hasEnoughCredits` before action
|
|
76
|
+
- [ ] Display credit cost to user
|
|
77
|
+
- [ ] Show upgrade/purchase option when insufficient
|
|
78
|
+
- [ ] Validate credit cost is positive number
|
|
79
|
+
- [ ] Test with sufficient credits
|
|
80
|
+
- [ ] Test with insufficient credits
|
|
81
|
+
- [ ] Test with zero credits
|
|
82
|
+
- [ ] Test credit changes between check and action
|
|
103
83
|
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
```
|
|
84
|
+
### Common Patterns to Implement
|
|
107
85
|
|
|
108
|
-
|
|
86
|
+
1. **Pre-action Validation**: Check before showing button
|
|
87
|
+
2. **Cost Display**: Always show credit cost to user
|
|
88
|
+
3. **Upgrade Prompts**: Link to credit packages when low
|
|
89
|
+
4. **Manual Refresh**: Check again before expensive operations
|
|
90
|
+
5. **Credit Deduction**: Use with `useDeductCredit` for complete flow
|
|
91
|
+
6. **Visual Feedback**: Disable buttons when insufficient
|
|
92
|
+
7. **Warning Messages**: Alert user before high-cost operations
|
|
93
|
+
8. **Balance Display**: Show current balance alongside cost
|
|
109
94
|
|
|
110
|
-
|
|
111
|
-
function TwoStepCreditProcess() {
|
|
112
|
-
const { user } = useAuth();
|
|
95
|
+
## Related Documentation
|
|
113
96
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// Step 1: Check credits
|
|
121
|
-
const check = await checkCreditsAvailable(user?.uid, 10);
|
|
122
|
-
|
|
123
|
-
if (!check.canProceed) {
|
|
124
|
-
Alert.alert('Insufficient Credits', 'You need 10 credits for this operation');
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
// Step 2: Perform operation
|
|
130
|
-
const result = await expensiveOperation();
|
|
131
|
-
|
|
132
|
-
// Step 3: Deduct credits only after success
|
|
133
|
-
await deductCreditsAfterSuccess(user?.uid, 10);
|
|
134
|
-
|
|
135
|
-
Alert.alert('Success', 'Operation completed!');
|
|
136
|
-
} catch (error) {
|
|
137
|
-
Alert.alert('Error', 'Operation failed, credits not deducted');
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
return <Button onPress={handleProcess} title="Process (10 credits)" />;
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### With Pre-Check UI
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
function CreditPreCheck() {
|
|
149
|
-
const { user } = useAuth();
|
|
150
|
-
const { checkCreditsAvailable } = useCreditChecker();
|
|
151
|
-
|
|
152
|
-
const [creditCheck, setCreditCheck] = useState<CreditCheckResult | null>(null);
|
|
153
|
-
|
|
154
|
-
useEffect(() => {
|
|
155
|
-
// Pre-check on mount
|
|
156
|
-
checkCreditsAvailable(user?.uid, 5).then(setCreditCheck);
|
|
157
|
-
}, [user]);
|
|
158
|
-
|
|
159
|
-
if (!creditCheck) return <ActivityIndicator />;
|
|
160
|
-
|
|
161
|
-
return (
|
|
162
|
-
<View>
|
|
163
|
-
<Text>Required: {creditCheck.required} credits</Text>
|
|
164
|
-
<Text>Balance: {creditCheck.currentBalance} credits</Text>
|
|
165
|
-
|
|
166
|
-
{creditCheck.canProceed ? (
|
|
167
|
-
<Button
|
|
168
|
-
onPress={() => executeAction()}
|
|
169
|
-
title="Proceed"
|
|
170
|
-
/>
|
|
171
|
-
) : (
|
|
172
|
-
<Button
|
|
173
|
-
onPress={() => showPaywall()}
|
|
174
|
-
title="Get More Credits"
|
|
175
|
-
/>
|
|
176
|
-
)}
|
|
177
|
-
</View>
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### With Cost Estimation
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
function CostEstimator() {
|
|
186
|
-
const { user } = useAuth();
|
|
187
|
-
const { checkCreditsAvailable } = useCreditChecker();
|
|
188
|
-
|
|
189
|
-
const estimateCost = (itemCount: number) => {
|
|
190
|
-
return Math.ceil(itemCount / 10); // 1 credit per 10 items
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
const handleExport = async (items: any[]) => {
|
|
194
|
-
const cost = estimateCost(items.length);
|
|
195
|
-
const result = await checkCreditsAvailable(user?.uid, cost);
|
|
196
|
-
|
|
197
|
-
if (!result.canProceed) {
|
|
198
|
-
Alert.alert(
|
|
199
|
-
'Insufficient Credits',
|
|
200
|
-
`Exporting ${items.length} items costs ${cost} credits. You have ${result.currentBalance}.`
|
|
201
|
-
);
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
await exportItems(items);
|
|
206
|
-
await deductCreditsAfterSuccess(user?.uid, cost);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
return (
|
|
210
|
-
<FlatList
|
|
211
|
-
data={items}
|
|
212
|
-
renderItem={({ item }) => <ItemRow item={item} />}
|
|
213
|
-
ListHeaderComponent={
|
|
214
|
-
<Button
|
|
215
|
-
onPress={() => handleExport(selectedItems)}
|
|
216
|
-
title={`Export (${estimateCost(selectedItems.length)} credits)`}
|
|
217
|
-
/>
|
|
218
|
-
}
|
|
219
|
-
/>
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## Examples
|
|
225
|
-
|
|
226
|
-
### AI Generation with Credits
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
function AIGeneration() {
|
|
230
|
-
const { user } = useAuth();
|
|
231
|
-
const { checkCreditsAvailable, deductCreditsAfterSuccess } = useCreditChecker({
|
|
232
|
-
onCreditDeducted: (userId, cost) => {
|
|
233
|
-
analytics.track('ai_generation_completed', { userId, cost });
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
const creditsNeeded = 5;
|
|
238
|
-
|
|
239
|
-
const handleGenerate = async () => {
|
|
240
|
-
// Check if user has enough credits
|
|
241
|
-
const check = await checkCreditsAvailable(user?.uid, creditsNeeded);
|
|
242
|
-
|
|
243
|
-
if (!check.canProceed) {
|
|
244
|
-
Alert.alert(
|
|
245
|
-
'Insufficient Credits',
|
|
246
|
-
`You need ${creditsNeeded} credits to generate. Current balance: ${check.currentBalance}`,
|
|
247
|
-
[
|
|
248
|
-
{ text: 'Cancel' },
|
|
249
|
-
{ text: 'Get Credits', onPress: () => navigation.navigate('CreditPackages') },
|
|
250
|
-
]
|
|
251
|
-
);
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
// Show loading
|
|
257
|
-
setGenerating(true);
|
|
258
|
-
|
|
259
|
-
// Generate AI content
|
|
260
|
-
const result = await callAIGenerationAPI(prompt);
|
|
261
|
-
|
|
262
|
-
// Only deduct credits after successful generation
|
|
263
|
-
await deductCreditsAfterSuccess(user?.uid, creditsNeeded);
|
|
264
|
-
|
|
265
|
-
setContent(result);
|
|
266
|
-
} catch (error) {
|
|
267
|
-
Alert.alert('Error', 'Failed to generate content');
|
|
268
|
-
} finally {
|
|
269
|
-
setGenerating(false);
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
return (
|
|
274
|
-
<View>
|
|
275
|
-
<Text>Credits needed: {creditsNeeded}</Text>
|
|
276
|
-
<Button
|
|
277
|
-
onPress={handleGenerate}
|
|
278
|
-
disabled={generating}
|
|
279
|
-
title={generating ? 'Generating...' : `Generate (${creditsNeeded} credits)`}
|
|
280
|
-
/>
|
|
281
|
-
</View>
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Batch Operations
|
|
287
|
-
|
|
288
|
-
```typescript
|
|
289
|
-
function BatchOperations() {
|
|
290
|
-
const { user } = useAuth();
|
|
291
|
-
const { checkCreditsAvailable, deductCreditsAfterSuccess } = useCreditChecker();
|
|
292
|
-
|
|
293
|
-
const handleBatchProcess = async (items: Item[]) => {
|
|
294
|
-
const cost = items.length; // 1 credit per item
|
|
295
|
-
|
|
296
|
-
const check = await checkCreditsAvailable(user?.uid, cost);
|
|
297
|
-
|
|
298
|
-
if (!check.canProceed) {
|
|
299
|
-
Alert.alert(
|
|
300
|
-
'Insufficient Credits',
|
|
301
|
-
`Processing ${items.length} items requires ${cost} credits. You have ${check.currentBalance}.`,
|
|
302
|
-
[
|
|
303
|
-
{ text: 'Cancel' },
|
|
304
|
-
{
|
|
305
|
-
text: 'Get Credits',
|
|
306
|
-
onPress: () => navigation.navigate('CreditPackages', { required: cost }),
|
|
307
|
-
},
|
|
308
|
-
]
|
|
309
|
-
);
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Confirm before proceeding
|
|
314
|
-
Alert.alert(
|
|
315
|
-
'Confirm Batch Process',
|
|
316
|
-
`Process ${items.length} items for ${cost} credits?`,
|
|
317
|
-
[
|
|
318
|
-
{ text: 'Cancel', style: 'cancel' },
|
|
319
|
-
{
|
|
320
|
-
text: 'Process',
|
|
321
|
-
onPress: async () => {
|
|
322
|
-
try {
|
|
323
|
-
// Process all items
|
|
324
|
-
for (const item of items) {
|
|
325
|
-
await processItem(item);
|
|
326
|
-
updateProgress();
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Deduct only after all succeed
|
|
330
|
-
await deductCreditsAfterSuccess(user?.uid, cost);
|
|
331
|
-
|
|
332
|
-
Alert.alert('Success', `Processed ${items.length} items`);
|
|
333
|
-
} catch (error) {
|
|
334
|
-
Alert.alert('Error', 'Batch processing failed');
|
|
335
|
-
}
|
|
336
|
-
},
|
|
337
|
-
},
|
|
338
|
-
]
|
|
339
|
-
);
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
return (
|
|
343
|
-
<Button
|
|
344
|
-
onPress={() => handleBatchProcess(selectedItems)}
|
|
345
|
-
title={`Process ${selectedItems.length} items`}
|
|
346
|
-
/>
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
### Progressive Credit Check
|
|
352
|
-
|
|
353
|
-
```typescript
|
|
354
|
-
function ProgressiveFeature() {
|
|
355
|
-
const { user } = useAuth();
|
|
356
|
-
const { checkCreditsAvailable } = useCreditChecker();
|
|
357
|
-
|
|
358
|
-
const handleProgressiveAction = async () => {
|
|
359
|
-
// Try from most expensive to least expensive
|
|
360
|
-
const tiers = [10, 5, 3, 1];
|
|
361
|
-
|
|
362
|
-
for (const cost of tiers) {
|
|
363
|
-
const check = await checkCreditsAvailable(user?.uid, cost);
|
|
364
|
-
|
|
365
|
-
if (check.canProceed) {
|
|
366
|
-
// Use the highest tier user can afford
|
|
367
|
-
await executeAction(cost);
|
|
368
|
-
Alert.alert('Success', `Used ${cost} credits`);
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// User can't afford any tier
|
|
374
|
-
Alert.alert('Insufficient Credits', 'You need at least 1 credit to proceed');
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
return (
|
|
378
|
-
<Button
|
|
379
|
-
onPress={handleProgressiveAction}
|
|
380
|
-
title="Smart Action (1-10 credits)"
|
|
381
|
-
/>
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
## Best Practices
|
|
387
|
-
|
|
388
|
-
1. **Check before deduct** - Always verify credits before operations
|
|
389
|
-
2. **Deduct after success** - Only deduct when operation succeeds
|
|
390
|
-
3. **Show clear messaging** - Tell users cost and balance
|
|
391
|
-
4. **Handle failures** - Don't deduct on error
|
|
392
|
-
5. **Track deductions** - Use callback for analytics
|
|
393
|
-
6. **Provide alternatives** - Link to credit purchase when low
|
|
394
|
-
7. **Estimate costs** - Calculate and show cost upfront
|
|
395
|
-
|
|
396
|
-
## Related Hooks
|
|
397
|
-
|
|
398
|
-
- **useCredits** - For accessing credits balance
|
|
399
|
-
- **useDeductCredit** - For direct credit deduction
|
|
400
|
-
- **useCreditsGate** - For gating features with credits
|
|
401
|
-
- **useCreditChecker** (utility) - For reusable credit checker
|
|
402
|
-
|
|
403
|
-
## See Also
|
|
404
|
-
|
|
405
|
-
- [Credit Checker Utility](../../../utils/creditChecker.md)
|
|
406
|
-
- [Credits README](../../../domains/wallet/README.md)
|
|
407
|
-
- [Credits Entity](../../../domains/wallet/domain/entities/Credits.md)
|
|
97
|
+
- **useCredits**: Access current credit balance
|
|
98
|
+
- **useDeductCredit**: Deduct credits after check passes
|
|
99
|
+
- **useCreditsGate**: Complete credit gating with deduction
|
|
100
|
+
- **useFeatureGate**: Unified feature gating with credits
|
|
101
|
+
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/README.md`
|
|
102
|
+
- **Wallet Domain**: `src/domains/wallet/README.md`
|