@umituz/react-native-subscription 2.15.3 → 2.15.5

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.
@@ -1,231 +0,0 @@
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`
@@ -1,284 +0,0 @@
1
- # useFeatureGate Hook
2
-
3
- Unified feature gate combining authentication, subscription, and credits checks.
4
-
5
- ## Location
6
-
7
- **Import Path**: `@umituz/react-native-subscription`
8
-
9
- **File**: `src/presentation/hooks/useFeatureGate.ts`
10
-
11
- **Type**: Hook
12
-
13
- ## Strategy
14
-
15
- ### Progressive Access Control
16
-
17
- 1. **Authentication Check**
18
- - Verify user is authenticated
19
- - Show auth modal if not authenticated
20
- - Queue pending action for post-auth execution
21
-
22
- 2. **Subscription Check**
23
- - Verify user has required subscription tier
24
- - Show paywall if subscription insufficient
25
- - Bypass credit check for premium users
26
-
27
- 3. **Credit Check**
28
- - Verify sufficient credit balance
29
- - Show purchase prompt if insufficient
30
- - Deduct credits only after all checks pass
31
-
32
- 4. **Action Execution**
33
- - Execute gated action only if all checks pass
34
- - Handle failures gracefully
35
- - Update UI state appropriately
36
-
37
- ### Integration Points
38
-
39
- - **useAuth**: For authentication state
40
- - **usePremium**: For subscription status
41
- - **useCredits**: For credit balance
42
- - **useDeductCredit**: For credit deduction
43
- - **Auth Modals**: Custom authentication UI
44
- - **Paywall Components**: Upgrade prompts
45
-
46
- ## Restrictions
47
-
48
- ### REQUIRED
49
-
50
- - **Authentication Check**: MUST implement `onShowAuthModal` callback
51
- - **Paywall Handler**: MUST implement `onShowPaywall` callback
52
- - **Credit Validation**: MUST check `hasCredits` before allowing actions
53
- - **State Management**: MUST respect `canAccess` boolean
54
-
55
- ### PROHIBITED
56
-
57
- - **NEVER** bypass authentication check for sensitive features
58
- - **NEVER** show feature gate if user has access (confusing UX)
59
- - **NEVER** execute action without checking all gates first
60
- - **DO NOT** hardcode gate logic (use hook parameters)
61
- - **NEVER** deduct credits without checking subscription status first
62
-
63
- ### CRITICAL SAFETY
64
-
65
- - **ALWAYS** check gates in order: Auth → Subscription → Credits
66
- - **ALWAYS** provide clear messaging about why access is denied
67
- - **ALWAYS** preserve user intent through auth/purchase flows
68
- - **NEVER** trust client-side gate for security (server-side validation required)
69
- - **MUST** implement proper error boundaries around gated actions
70
-
71
- ## Rules
72
-
73
- ### Basic Feature Gating
74
-
75
- ```typescript
76
- // CORRECT - All gates with clear messaging
77
- function PremiumFeature() {
78
- const { requireFeature, canAccess, isAuthenticated, hasSubscription, hasCredits } =
79
- useFeatureGate({
80
- isAuthenticated: !!user,
81
- onShowAuthModal: (pendingAction) => {
82
- navigation.navigate('Auth', { pendingAction });
83
- },
84
- hasSubscription: isPremium,
85
- hasCredits: credits >= 5,
86
- creditBalance: credits,
87
- requiredCredits: 5,
88
- onShowPaywall: (required) => {
89
- if (!isAuthenticated) return;
90
- if (!hasSubscription) showPaywall();
91
- else showCreditPurchase(required);
92
- },
93
- });
94
-
95
- const handleAction = () => {
96
- requireFeature(async () => {
97
- await executeFeature();
98
- });
99
- };
100
-
101
- return (
102
- <Button
103
- onPress={handleAction}
104
- title={!canAccess ? 'Unlock Feature' : 'Execute'}
105
- />
106
- );
107
- }
108
-
109
- // INCORRECT - Missing auth callback
110
- const { requireFeature } = useFeatureGate({
111
- isAuthenticated: !!user,
112
- // Missing onShowAuthModal
113
- hasSubscription: isPremium,
114
- hasCredits: credits >= 5,
115
- creditBalance: credits,
116
- onShowPaywall: showPaywall,
117
- });
118
-
119
- // INCORRECT - No access check
120
- const handleAction = () => {
121
- requireFeature(() => executeFeature());
122
- };
123
- // Always shows button, even if user can't access
124
- ```
125
-
126
- ### Progressive Access Control
127
-
128
- ```typescript
129
- // CORRECT - Show appropriate message for each gate
130
- function ProgressiveGate() {
131
- const { canAccess, isAuthenticated, hasSubscription, hasCredits } = useFeatureGate({
132
- isAuthenticated: !!user,
133
- onShowAuthModal: (pending) => showAuthModal({ pendingAction: pending }),
134
- hasSubscription: isPremium,
135
- hasCredits: credits >= 3,
136
- creditBalance: credits,
137
- requiredCredits: 3,
138
- onShowPaywall: (required) => showUpgradeModal({ requiredCredits: required }),
139
- });
140
-
141
- const getAccessMessage = () => {
142
- if (!isAuthenticated) return 'Sign in to access';
143
- if (!hasSubscription && !hasCredits) return 'Upgrade or purchase credits';
144
- if (!hasCredits) return `Need ${requiredCredits} credits`;
145
- return 'Full access';
146
- };
147
-
148
- return (
149
- <View>
150
- <Text>{getAccessMessage()}</Text>
151
- <Button
152
- onPress={() => requireFeature(() => executeAction())}
153
- disabled={!canAccess}
154
- title={canAccess ? 'Execute' : 'Unlock'}
155
- />
156
- </View>
157
- );
158
- }
159
-
160
- // INCORRECT - Generic message for all gates
161
- if (!canAccess) {
162
- return <Text>You cannot access this feature</Text>;
163
- }
164
- // Doesn't tell user what they need to do
165
- ```
166
-
167
- ### Premium Bypass Logic
168
-
169
- ```typescript
170
- // CORRECT - Premium users bypass credit check
171
- function PremiumBypass() {
172
- const { requireFeature } = useFeatureGate({
173
- isAuthenticated: !!user,
174
- onShowAuthModal: (pending) => showAuthModal({ pendingAction: pending }),
175
- hasSubscription: isPremium,
176
- hasCredits: isPremium ? true : credits >= 5,
177
- creditBalance: credits,
178
- requiredCredits: isPremium ? 0 : 5,
179
- onShowPaywall: (required) => {
180
- if (isPremium) {
181
- showInsufficientCreditsModal(required);
182
- } else {
183
- showPaywall();
184
- }
185
- },
186
- });
187
-
188
- return <Button onPress={() => requireFeature(executeAction)} />;
189
- }
190
-
191
- // INCORRECT - Charges premium users for credits
192
- hasCredits: credits >= 5, // Even if isPremium is true
193
- requiredCredits: 5, // Charges premium users
194
- ```
195
-
196
- ### Callback Implementation
197
-
198
- ```typescript
199
- // CORRECT - Proper auth modal with pending action
200
- onShowAuthModal: (pendingAction) => {
201
- navigation.navigate('Auth', {
202
- onAuthSuccess: pendingAction,
203
- });
204
- }
205
-
206
- // CORRECT - Context-aware paywall
207
- onShowPaywall: (requiredCredits) => {
208
- if (isPremium) {
209
- // Already premium, need credits
210
- showCreditPurchaseModal({ requiredCredits });
211
- } else {
212
- // Not premium, show subscription
213
- showSubscriptionPaywall();
214
- }
215
- }
216
-
217
- // INCORRECT - No pending action preservation
218
- onShowAuthModal: () => {
219
- navigation.navigate('Auth');
220
- // User's intent is lost
221
- }
222
- ```
223
-
224
- ### Action Gating
225
-
226
- ```typescript
227
- // CORRECT - Gate action execution
228
- const handleGenerate = () => {
229
- requireFeature(async () => {
230
- // Only runs if all gates pass
231
- const result = await generateContent();
232
- showResult(result);
233
- });
234
- };
235
-
236
- // INCORRECT - Execute action before gating
237
- const handleGenerate = async () => {
238
- const result = await generateContent(); // Runs regardless!
239
- requireFeature(() => showResult(result));
240
- };
241
- ```
242
-
243
- ## AI Agent Guidelines
244
-
245
- ### When Implementing Feature Gates
246
-
247
- 1. **Always** check gates in order: Auth → Subscription → Credits
248
- 2. **Always** provide clear, specific messaging for each gate
249
- 3. **Always** preserve user intent through auth/purchase flows
250
- 4. **Always** bypass credit check for premium users (if applicable)
251
- 5. **Never** trust client-side gates for security (server validation required)
252
-
253
- ### Integration Checklist
254
-
255
- - [ ] Import from correct path: `@umituz/react-native-subscription`
256
- - [ ] Implement `onShowAuthModal` with pending action preservation
257
- - [ ] Implement `onShowPaywall` with context-aware messaging
258
- - [ ] Check `canAccess` before showing feature
259
- - [ ] Provide appropriate messaging for each gate state
260
- - [ ] Respect premium status (bypass credit check if needed)
261
- - [ ] Test all gate combinations (auth, subscription, credits)
262
- - [ ] Test pending action execution after auth/purchase
263
- - [ ] Implement error boundaries around gated actions
264
-
265
- ### Common Patterns to Implement
266
-
267
- 1. **Progressive Unlocking**: Show upgrade path for each gate
268
- 2. **Smart Paywalls**: Show subscription OR credits based on user state
269
- 3. **Intent Preservation**: Queue actions through auth/purchase flows
270
- 4. **Clear Messaging**: Tell users exactly what's required
271
- 5. **Premium Bypass**: Skip credit check for premium users
272
- 6. **Access Indicators**: Show lock/unlock status in UI
273
- 7. **Graceful Degradation**: Show limited version for free users
274
- 8. **Analytics Tracking**: Monitor gate triggers and conversions
275
-
276
- ## Related Documentation
277
-
278
- - **useAuthGate**: Authentication gating only
279
- - **useSubscriptionGate**: Subscription gating only
280
- - **useCreditsGate**: Credits gating only
281
- - **usePremiumGate**: Premium feature gating
282
- - **useDeductCredit**: Credit deduction after gate passes
283
- - **Feature Gating**: `../../../docs/FEATURE_GATING.md`
284
- - **Access Control**: `../../../docs/ACCESS_PATTERNS.md`