@umituz/react-native-subscription 2.14.99 → 2.14.101
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 +3 -3
- 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/entities/SubscriptionStatus.ts +1 -1
- 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/components/TransactionItem.tsx +1 -1
- 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/ActivationHandler.ts +1 -1
- package/src/infrastructure/services/README.md +95 -370
- package/src/infrastructure/services/SubscriptionService.ts +1 -1
- 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/useDeductCredit.ts +1 -1
- 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/presentation/utils/subscriptionDateUtils.ts +1 -1
- 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,342 +1,103 @@
|
|
|
1
1
|
# useCredits Hook
|
|
2
2
|
|
|
3
|
-
Hook for accessing and managing credits balance.
|
|
3
|
+
Hook for accessing and managing credits balance with real-time updates.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Location
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
import { useCredits } from '@umituz/react-native-subscription';
|
|
9
|
-
```
|
|
7
|
+
**Import Path**: `@umituz/react-native-subscription`
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
**File**: `src/presentation/hooks/useCredits.ts`
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
function useCredits(): {
|
|
15
|
-
credits: number;
|
|
16
|
-
balance: number;
|
|
17
|
-
transactions: Transaction[];
|
|
18
|
-
isLoading: boolean;
|
|
19
|
-
error: Error | null;
|
|
20
|
-
refetch: () => Promise<void>;
|
|
21
|
-
}
|
|
22
|
-
```
|
|
11
|
+
**Type**: Hook
|
|
23
12
|
|
|
24
|
-
##
|
|
13
|
+
## Strategy
|
|
25
14
|
|
|
26
|
-
|
|
27
|
-
|----------|------|-------------|
|
|
28
|
-
| `credits` | `number` | Current credit balance |
|
|
29
|
-
| `balance` | `number` | Balance in currency (USD, etc.) |
|
|
30
|
-
| `transactions` | `Transaction[]` | Transaction history |
|
|
31
|
-
| `isLoading` | `boolean` | Loading state |
|
|
32
|
-
| `error` | `Error \| null` | Error if any |
|
|
33
|
-
| `refetch` | `() => Promise<void>` | Manually refetch data |
|
|
15
|
+
### Data Fetching Flow
|
|
34
16
|
|
|
35
|
-
|
|
17
|
+
1. **Initial Load**
|
|
18
|
+
- Fetch credits from repository on mount
|
|
19
|
+
- Cache results in TanStack Query
|
|
20
|
+
- Handle loading/error states
|
|
36
21
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
2. **Real-time Updates**
|
|
23
|
+
- Subscribe to credit changes via repository listener
|
|
24
|
+
- Auto-update on balance changes
|
|
25
|
+
- Invalidate query on external modifications
|
|
40
26
|
|
|
41
|
-
|
|
27
|
+
3. **Cache Management**
|
|
28
|
+
- Use TanStack Query for caching
|
|
29
|
+
- Background refetch on window focus
|
|
30
|
+
- Manual refetch capability
|
|
42
31
|
|
|
43
|
-
|
|
44
|
-
<View>
|
|
45
|
-
<Text>Your Credits: {credits}</Text>
|
|
46
|
-
<Text>Balance: ${balance.toFixed(2)}</Text>
|
|
47
|
-
</View>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
```
|
|
32
|
+
### Integration Points
|
|
51
33
|
|
|
52
|
-
|
|
34
|
+
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/CreditsRepository.ts`
|
|
35
|
+
- **Credits Entity**: `src/domains/wallet/domain/entities/UserCredits.ts`
|
|
36
|
+
- **TanStack Query**: For cache management and background updates
|
|
37
|
+
- **useFocusEffect**: For refresh on screen focus
|
|
53
38
|
|
|
54
|
-
|
|
39
|
+
## Restrictions
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
function CreditsWithRefresh() {
|
|
58
|
-
const { credits, refetch, isLoading } = useCredits();
|
|
41
|
+
### REQUIRED
|
|
59
42
|
|
|
60
|
-
|
|
43
|
+
- **User Authentication**: User MUST be authenticated to access credits
|
|
44
|
+
- **Error Handling**: MUST handle error state in UI
|
|
45
|
+
- **Loading State**: MUST show loading indicator while fetching
|
|
61
46
|
|
|
62
|
-
|
|
63
|
-
setRefreshing(true);
|
|
64
|
-
await refetch();
|
|
65
|
-
setRefreshing(false);
|
|
66
|
-
};
|
|
47
|
+
### PROHIBITED
|
|
67
48
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
49
|
+
- **NEVER** assume credits are available without checking loading state
|
|
50
|
+
- **NEVER** use this hook for unauthenticated users
|
|
51
|
+
- **DO NOT** mutate credits directly - use `useDeductCredit` instead
|
|
52
|
+
- **DO NOT** call refetch excessively (causes unnecessary network calls)
|
|
71
53
|
|
|
72
|
-
|
|
73
|
-
onPress={handleRefresh}
|
|
74
|
-
disabled={refreshing || isLoading}
|
|
75
|
-
title={refreshing ? 'Refreshing...' : 'Refresh'}
|
|
76
|
-
/>
|
|
77
|
-
</View>
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
```
|
|
54
|
+
### CRITICAL SAFETY
|
|
81
55
|
|
|
82
|
-
|
|
56
|
+
- **ALWAYS** check `isLoading` before displaying credits
|
|
57
|
+
- **ALWAYS** handle `error` state to prevent crashes
|
|
58
|
+
- **NEVER** use `credits` value for security decisions (validate on backend)
|
|
59
|
+
- **MUST** implement error boundaries when displaying balance
|
|
83
60
|
|
|
84
|
-
|
|
85
|
-
function AutoRefreshCredits() {
|
|
86
|
-
const { credits, refetch } = useCredits();
|
|
61
|
+
## AI Agent Guidelines
|
|
87
62
|
|
|
88
|
-
|
|
89
|
-
useEffect(() => {
|
|
90
|
-
const interval = setInterval(() => {
|
|
91
|
-
refetch();
|
|
92
|
-
}, 30000);
|
|
63
|
+
### When Implementing Credit Displays
|
|
93
64
|
|
|
94
|
-
|
|
95
|
-
|
|
65
|
+
1. **Always** handle loading state explicitly
|
|
66
|
+
2. **Always** handle error state gracefully
|
|
67
|
+
3. **Always** provide manual refresh option
|
|
68
|
+
4. **Always** format balance safely (handle undefined/null)
|
|
69
|
+
5. **Never** use credits for security decisions (server-side validation required)
|
|
96
70
|
|
|
97
|
-
|
|
98
|
-
useFocusEffect(
|
|
99
|
-
useCallback(() => {
|
|
100
|
-
refetch();
|
|
101
|
-
}, [refetch])
|
|
102
|
-
);
|
|
71
|
+
### Integration Checklist
|
|
103
72
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
73
|
+
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
74
|
+
- [ ] Handle loading state in UI
|
|
75
|
+
- [ ] Handle error state in UI
|
|
76
|
+
- [ ] Provide refresh mechanism
|
|
77
|
+
- [ ] Format balance safely with default values
|
|
78
|
+
- [ ] Implement error boundaries
|
|
79
|
+
- [ ] Test with no credits (zero balance)
|
|
80
|
+
- [ ] Test with loading states
|
|
81
|
+
- [ ] Test with error states
|
|
82
|
+
- [ ] Test refresh functionality
|
|
107
83
|
|
|
108
|
-
###
|
|
84
|
+
### Common Patterns to Implement
|
|
109
85
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
86
|
+
1. **Balance Card**: Display credits with currency conversion
|
|
87
|
+
2. **Transaction List**: Show recent credit activity
|
|
88
|
+
3. **Low Credits Warning**: Alert when balance is low
|
|
89
|
+
4. **Refresh Control**: Pull-to-refresh or button
|
|
90
|
+
5. **Real-time Updates**: Use focus effect for auto-refresh
|
|
91
|
+
6. **Purchase Prompt**: Link to credit packages when low
|
|
92
|
+
7. **Skeletal Loading**: Show skeleton during initial load
|
|
93
|
+
8. **Error Recovery**: Retry mechanism for failed fetches
|
|
113
94
|
|
|
114
|
-
|
|
115
|
-
<View>
|
|
116
|
-
<BalanceDisplay credits={credits} />
|
|
95
|
+
## Related Documentation
|
|
117
96
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
keyExtractor={(item) => item.id}
|
|
126
|
-
renderItem={({ item }) => (
|
|
127
|
-
<TransactionItem
|
|
128
|
-
amount={item.amount}
|
|
129
|
-
reason={item.reason}
|
|
130
|
-
timestamp={item.timestamp}
|
|
131
|
-
/>
|
|
132
|
-
)}
|
|
133
|
-
/>
|
|
134
|
-
)}
|
|
135
|
-
</View>
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### With Balance Conversion
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
function CreditsWithConversion() {
|
|
144
|
-
const { credits, balance } = useCredits();
|
|
145
|
-
|
|
146
|
-
const creditValue = 0.01; // 1 credit = $0.01
|
|
147
|
-
|
|
148
|
-
return (
|
|
149
|
-
<View>
|
|
150
|
-
<Text style={styles.credits}>{credits} Credits</Text>
|
|
151
|
-
<Text style={styles.balance}>
|
|
152
|
-
Worth approximately ${(balance || 0).toFixed(2)}
|
|
153
|
-
</Text>
|
|
154
|
-
<Text style={styles.info}>
|
|
155
|
-
(1 credit = ${(creditValue).toFixed(2)})
|
|
156
|
-
</Text>
|
|
157
|
-
</View>
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## Examples
|
|
163
|
-
|
|
164
|
-
### Credits Balance Card
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
function CreditsBalanceCard() {
|
|
168
|
-
const { credits, balance, isLoading } = useCredits();
|
|
169
|
-
|
|
170
|
-
if (isLoading) {
|
|
171
|
-
return <SkeletonLoader />;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return (
|
|
175
|
-
<Card style={styles.card}>
|
|
176
|
-
<Card.Header>
|
|
177
|
-
<Card.Title>Your Balance</Card.Title>
|
|
178
|
-
</Card.Header>
|
|
179
|
-
|
|
180
|
-
<Card.Body>
|
|
181
|
-
<View style={styles.balanceContainer}>
|
|
182
|
-
<Text style={styles.credits}>{credits}</Text>
|
|
183
|
-
<Text style={styles.label}>Credits</Text>
|
|
184
|
-
</View>
|
|
185
|
-
|
|
186
|
-
<View style={styles.balanceContainer}>
|
|
187
|
-
<Text style={styles.balance}>
|
|
188
|
-
${(balance || 0).toFixed(2)}
|
|
189
|
-
</Text>
|
|
190
|
-
<Text style={styles.label}>Value</Text>
|
|
191
|
-
</View>
|
|
192
|
-
</Card.Body>
|
|
193
|
-
|
|
194
|
-
<Card.Footer>
|
|
195
|
-
<Button
|
|
196
|
-
onPress={() => navigation.navigate('CreditPackages')}
|
|
197
|
-
title="Get More Credits"
|
|
198
|
-
size="sm"
|
|
199
|
-
/>
|
|
200
|
-
</Card.Footer>
|
|
201
|
-
</Card>
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Credits Indicator
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
function CreditsIndicator() {
|
|
210
|
-
const { credits } = useCredits();
|
|
211
|
-
|
|
212
|
-
return (
|
|
213
|
-
<TouchableOpacity
|
|
214
|
-
onPress={() => navigation.navigate('Wallet')}
|
|
215
|
-
style={styles.indicator}
|
|
216
|
-
>
|
|
217
|
-
<Icon name="coins" size={20} color="#FFD700" />
|
|
218
|
-
<Text style={styles.credits}>{credits}</Text>
|
|
219
|
-
</TouchableOpacity>
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### Low Credits Warning
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
function LowCreditsWarning() {
|
|
228
|
-
const { credits } = useCredits();
|
|
229
|
-
const lowCreditThreshold = 10;
|
|
230
|
-
|
|
231
|
-
useEffect(() => {
|
|
232
|
-
if (credits <= lowCreditThreshold && credits > 0) {
|
|
233
|
-
notifications.show({
|
|
234
|
-
title: 'Low Credits',
|
|
235
|
-
body: `You have ${credits} credits remaining. Purchase more to continue using features.`,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}, [credits]);
|
|
239
|
-
|
|
240
|
-
if (credits <= lowCreditThreshold) {
|
|
241
|
-
return (
|
|
242
|
-
<Alert
|
|
243
|
-
severity={credits === 0 ? 'error' : 'warning'}
|
|
244
|
-
message={
|
|
245
|
-
credits === 0
|
|
246
|
-
? 'No credits remaining. Purchase more to continue.'
|
|
247
|
-
: `Low credits: ${credits} remaining`
|
|
248
|
-
}
|
|
249
|
-
action={
|
|
250
|
-
<Button onPress={() => navigation.navigate('CreditPackages')}>
|
|
251
|
-
Get More
|
|
252
|
-
</Button>
|
|
253
|
-
}
|
|
254
|
-
/>
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### Credits Usage Chart
|
|
263
|
-
|
|
264
|
-
```typescript
|
|
265
|
-
function CreditsUsageChart() {
|
|
266
|
-
const { transactions } = useCredits();
|
|
267
|
-
|
|
268
|
-
// Group transactions by date
|
|
269
|
-
const usageByDate = useMemo(() => {
|
|
270
|
-
const grouped = transactions.reduce((acc, tx) => {
|
|
271
|
-
const date = new Date(tx.timestamp).toDateString();
|
|
272
|
-
acc[date] = (acc[date] || 0) + Math.abs(tx.amount);
|
|
273
|
-
return acc;
|
|
274
|
-
}, {});
|
|
275
|
-
|
|
276
|
-
return Object.entries(grouped)
|
|
277
|
-
.map(([date, amount]) => ({ date, amount }))
|
|
278
|
-
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime())
|
|
279
|
-
.slice(-7); // Last 7 days
|
|
280
|
-
}, [transactions]);
|
|
281
|
-
|
|
282
|
-
return (
|
|
283
|
-
<View>
|
|
284
|
-
<Text>Credits Usage (Last 7 Days)</Text>
|
|
285
|
-
|
|
286
|
-
<LineChart
|
|
287
|
-
data={usageByDate}
|
|
288
|
-
xKey="date"
|
|
289
|
-
yKey="amount"
|
|
290
|
-
height={200}
|
|
291
|
-
/>
|
|
292
|
-
</View>
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
### Real-Time Balance Updates
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
function RealtimeCreditsBalance() {
|
|
301
|
-
const { credits, refetch } = useCredits();
|
|
302
|
-
|
|
303
|
-
// Listen to credit updates
|
|
304
|
-
useEffect(() => {
|
|
305
|
-
const unsubscribe = creditsListener.on((newBalance) => {
|
|
306
|
-
console.log('Credits updated:', newBalance);
|
|
307
|
-
refetch();
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
return () => unsubscribe?.();
|
|
311
|
-
}, []);
|
|
312
|
-
|
|
313
|
-
return (
|
|
314
|
-
<View>
|
|
315
|
-
<Text>Credits: {credits}</Text>
|
|
316
|
-
</View>
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
## Best Practices
|
|
322
|
-
|
|
323
|
-
1. **Handle loading** - Show skeleton or spinner
|
|
324
|
-
2. **Display balance** - Show both credits and value
|
|
325
|
-
3. **Provide purchase option** - Link to credit packages
|
|
326
|
-
4. **Show warnings** - Alert when credits are low
|
|
327
|
-
5. **Track usage** - Display transaction history
|
|
328
|
-
6. **Auto-refresh** - Keep balance up to date
|
|
329
|
-
7. **Handle errors** - Show user-friendly messages
|
|
330
|
-
|
|
331
|
-
## Related Hooks
|
|
332
|
-
|
|
333
|
-
- **useCreditsGate** - For gating features with credits
|
|
334
|
-
- **useDeductCredit** - For deducting credits
|
|
335
|
-
- **useTransactionHistory** - For transaction list
|
|
336
|
-
- **useWallet** - Wallet domain hook
|
|
337
|
-
|
|
338
|
-
## See Also
|
|
339
|
-
|
|
340
|
-
- [CreditsGate](./useCreditsGate.md)
|
|
341
|
-
- [DeductCredit](./useDeductCredit.md)
|
|
342
|
-
- [Wallet Domain](../../../domains/wallet/README.md)
|
|
97
|
+
- **useDeductCredit**: Deduct credits from balance
|
|
98
|
+
- **useCreditChecker**: Check credit availability before operations
|
|
99
|
+
- **useInitializeCredits**: Initialize credits after purchase
|
|
100
|
+
- **useCreditsGate**: Gate features behind credit requirements
|
|
101
|
+
- **useFeatureGate**: Unified feature gating with credits
|
|
102
|
+
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/README.md`
|
|
103
|
+
- **Wallet Domain**: `src/domains/wallet/README.md`
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# useCredits Hook
|
|
2
|
+
|
|
3
|
+
Hook for accessing and managing credits balance with real-time updates.
|
|
4
|
+
|
|
5
|
+
## Location
|
|
6
|
+
|
|
7
|
+
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
+
|
|
9
|
+
**File**: `src/presentation/hooks/useCredits.ts`
|
|
10
|
+
|
|
11
|
+
**Type**: Hook
|
|
12
|
+
|
|
13
|
+
## Strategy
|
|
14
|
+
|
|
15
|
+
### Data Fetching Flow
|
|
16
|
+
|
|
17
|
+
1. **Initial Load**
|
|
18
|
+
- Fetch credits from repository on mount
|
|
19
|
+
- Cache results in TanStack Query
|
|
20
|
+
- Handle loading/error states
|
|
21
|
+
|
|
22
|
+
2. **Real-time Updates**
|
|
23
|
+
- Subscribe to credit changes via repository listener
|
|
24
|
+
- Auto-update on balance changes
|
|
25
|
+
- Invalidate query on external modifications
|
|
26
|
+
|
|
27
|
+
3. **Cache Management**
|
|
28
|
+
- Use TanStack Query for caching
|
|
29
|
+
- Background refetch on window focus
|
|
30
|
+
- Manual refetch capability
|
|
31
|
+
|
|
32
|
+
### Integration Points
|
|
33
|
+
|
|
34
|
+
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/CreditsRepository.ts`
|
|
35
|
+
- **Credits Entity**: `src/domains/wallet/domain/entities/UserCredits.ts`
|
|
36
|
+
- **TanStack Query**: For cache management and background updates
|
|
37
|
+
- **useFocusEffect**: For refresh on screen focus
|
|
38
|
+
|
|
39
|
+
## Restrictions
|
|
40
|
+
|
|
41
|
+
### REQUIRED
|
|
42
|
+
|
|
43
|
+
- **User Authentication**: User MUST be authenticated to access credits
|
|
44
|
+
- **Error Handling**: MUST handle error state in UI
|
|
45
|
+
- **Loading State**: MUST show loading indicator while fetching
|
|
46
|
+
|
|
47
|
+
### PROHIBITED
|
|
48
|
+
|
|
49
|
+
- **NEVER** assume credits are available without checking loading state
|
|
50
|
+
- **NEVER** use this hook for unauthenticated users
|
|
51
|
+
- **DO NOT** mutate credits directly - use `useDeductCredit` instead
|
|
52
|
+
- **DO NOT** call refetch excessively (causes unnecessary network calls)
|
|
53
|
+
|
|
54
|
+
### CRITICAL SAFETY
|
|
55
|
+
|
|
56
|
+
- **ALWAYS** check `isLoading` before displaying credits
|
|
57
|
+
- **ALWAYS** handle `error` state to prevent crashes
|
|
58
|
+
- **NEVER** use `credits` value for security decisions (validate on backend)
|
|
59
|
+
- **MUST** implement error boundaries when displaying balance
|
|
60
|
+
|
|
61
|
+
## Rules
|
|
62
|
+
|
|
63
|
+
### Basic Usage
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// CORRECT - Handle all states
|
|
67
|
+
function CreditsDisplay() {
|
|
68
|
+
const { credits, isLoading, error } = useCredits();
|
|
69
|
+
|
|
70
|
+
if (isLoading) return <ActivityIndicator />;
|
|
71
|
+
if (error) return <ErrorDisplay error={error} />;
|
|
72
|
+
|
|
73
|
+
return <Text>Credits: {credits}</Text>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// INCORRECT - No loading check
|
|
77
|
+
function CreditsDisplay() {
|
|
78
|
+
const { credits } = useCredits();
|
|
79
|
+
return <Text>Credits: {credits}</Text>; // May show undefined
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// INCORRECT - No error handling
|
|
83
|
+
function CreditsDisplay() {
|
|
84
|
+
const { credits, isLoading, error } = useCredits();
|
|
85
|
+
|
|
86
|
+
if (isLoading) return <ActivityIndicator />;
|
|
87
|
+
return <Text>Credits: {credits}</Text>; // Crashes if error
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Manual Refresh
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// CORRECT - Manual refetch with loading state
|
|
95
|
+
function CreditsWithRefresh() {
|
|
96
|
+
const { credits, refetch, isLoading } = useCredits();
|
|
97
|
+
|
|
98
|
+
const handleRefresh = async () => {
|
|
99
|
+
await refetch();
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<Button onPress={handleRefresh} disabled={isLoading}>
|
|
104
|
+
Refresh
|
|
105
|
+
</Button>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// INCORRECT - Ignore loading state during refresh
|
|
110
|
+
const handleRefresh = () => {
|
|
111
|
+
refetch(); // No loading indication
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Transaction History
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// CORRECT - Display transactions with safety checks
|
|
119
|
+
function CreditsWithHistory() {
|
|
120
|
+
const { credits, transactions, isLoading } = useCredits();
|
|
121
|
+
|
|
122
|
+
if (isLoading) return <ActivityIndicator />;
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<View>
|
|
126
|
+
<Text>Credits: {credits}</Text>
|
|
127
|
+
<FlatList
|
|
128
|
+
data={transactions || []}
|
|
129
|
+
keyExtractor={(item) => item.id}
|
|
130
|
+
renderItem={({ item }) => <TransactionItem {...item} />}
|
|
131
|
+
/>
|
|
132
|
+
</View>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// INCORRECT - Assume transactions exist
|
|
137
|
+
<FlatList
|
|
138
|
+
data={transactions} // Crashes if undefined
|
|
139
|
+
keyExtractor={(item) => item.id}
|
|
140
|
+
renderItem={({ item }) => <TransactionItem {...item} />}
|
|
141
|
+
/>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Auto-Refresh Patterns
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// CORRECT - Refresh on focus with effect cleanup
|
|
148
|
+
function AutoRefreshCredits() {
|
|
149
|
+
const { credits, refetch } = useCredits();
|
|
150
|
+
|
|
151
|
+
useFocusEffect(
|
|
152
|
+
useCallback(() => {
|
|
153
|
+
refetch();
|
|
154
|
+
}, [refetch])
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return <Text>Credits: {credits}</Text>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// INCORRECT - Missing dependency
|
|
161
|
+
useFocusEffect(
|
|
162
|
+
useCallback(() => {
|
|
163
|
+
refetch();
|
|
164
|
+
}, []) // Missing refetch dependency
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Balance Display
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// CORRECT - Safe balance display with formatting
|
|
172
|
+
function BalanceDisplay() {
|
|
173
|
+
const { credits, balance, isLoading } = useCredits();
|
|
174
|
+
|
|
175
|
+
if (isLoading) return <Skeleton />;
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<View>
|
|
179
|
+
<Text>{credits} Credits</Text>
|
|
180
|
+
<Text>Value: ${(balance || 0).toFixed(2)}</Text>
|
|
181
|
+
</View>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// INCORRECT - Unsafe balance access
|
|
186
|
+
<Text>Value: ${balance.toFixed(2)}</Text> // Crashes if undefined
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## AI Agent Guidelines
|
|
190
|
+
|
|
191
|
+
### When Implementing Credit Displays
|
|
192
|
+
|
|
193
|
+
1. **Always** handle loading state explicitly
|
|
194
|
+
2. **Always** handle error state gracefully
|
|
195
|
+
3. **Always** provide manual refresh option
|
|
196
|
+
4. **Always** format balance safely (handle undefined/null)
|
|
197
|
+
5. **Never** use credits for security decisions (server-side validation required)
|
|
198
|
+
|
|
199
|
+
### Integration Checklist
|
|
200
|
+
|
|
201
|
+
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
202
|
+
- [ ] Handle loading state in UI
|
|
203
|
+
- [ ] Handle error state in UI
|
|
204
|
+
- [ ] Provide refresh mechanism
|
|
205
|
+
- [ ] Format balance safely with default values
|
|
206
|
+
- [ ] Implement error boundaries
|
|
207
|
+
- [ ] Test with no credits (zero balance)
|
|
208
|
+
- [ ] Test with loading states
|
|
209
|
+
- [ ] Test with error states
|
|
210
|
+
- [ ] Test refresh functionality
|
|
211
|
+
|
|
212
|
+
### Common Patterns to Implement
|
|
213
|
+
|
|
214
|
+
1. **Balance Card**: Display credits with currency conversion
|
|
215
|
+
2. **Transaction List**: Show recent credit activity
|
|
216
|
+
3. **Low Credits Warning**: Alert when balance is low
|
|
217
|
+
4. **Refresh Control**: Pull-to-refresh or button
|
|
218
|
+
5. **Real-time Updates**: Use focus effect for auto-refresh
|
|
219
|
+
6. **Purchase Prompt**: Link to credit packages when low
|
|
220
|
+
7. **Skeletal Loading**: Show skeleton during initial load
|
|
221
|
+
8. **Error Recovery**: Retry mechanism for failed fetches
|
|
222
|
+
|
|
223
|
+
## Related Documentation
|
|
224
|
+
|
|
225
|
+
- **useDeductCredit**: Deduct credits from balance
|
|
226
|
+
- **useCreditChecker**: Check credit availability before operations
|
|
227
|
+
- **useInitializeCredits**: Initialize credits after purchase
|
|
228
|
+
- **useCreditsGate**: Gate features behind credit requirements
|
|
229
|
+
- **useFeatureGate**: Unified feature gating with credits
|
|
230
|
+
- **Credits Repository**: `src/domains/wallet/infrastructure/repositories/README.md`
|
|
231
|
+
- **Wallet Domain**: `src/domains/wallet/README.md`
|