@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
|
@@ -2,451 +2,91 @@
|
|
|
2
2
|
|
|
3
3
|
Automatically fetches premium status and provides tier information with repository integration.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Location
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
import { useUserTierWithRepository } from '@umituz/react-native-subscription';
|
|
9
|
-
```
|
|
7
|
+
**Import Path**: `@umituz/react-native-subscription`
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
**File**: `src/presentation/hooks/useUserTierWithRepository.ts`
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
function useUserTierWithRepository(params: {
|
|
15
|
-
auth: AuthProvider;
|
|
16
|
-
repository: ISubscriptionRepository;
|
|
17
|
-
}): {
|
|
18
|
-
tier: 'guest' | 'freemium' | 'premium';
|
|
19
|
-
isPremium: boolean;
|
|
20
|
-
isGuest: boolean;
|
|
21
|
-
isAuthenticated: boolean;
|
|
22
|
-
userId: string | null;
|
|
23
|
-
isLoading: boolean;
|
|
24
|
-
error: string | null;
|
|
25
|
-
refresh: () => Promise<void>;
|
|
26
|
-
}
|
|
27
|
-
```
|
|
11
|
+
**Type**: Hook
|
|
28
12
|
|
|
29
|
-
##
|
|
13
|
+
## Strategy
|
|
30
14
|
|
|
31
|
-
|
|
32
|
-
|-----------|------|---------|-------------|
|
|
33
|
-
| `auth` | `AuthProvider` | **Required** | Auth provider with user state |
|
|
34
|
-
| `repository` | `ISubscriptionRepository` | **Required** | Subscription repository |
|
|
15
|
+
### Tier Determination with Repository
|
|
35
16
|
|
|
36
|
-
|
|
17
|
+
1. **Auth State Check**: Use provided auth provider to check authentication
|
|
18
|
+
2. **Premium Fetch**: Automatically fetch premium status from repository for authenticated users
|
|
19
|
+
3. **Tier Assignment**: Assign tier based on auth + subscription (guest/freemium/premium)
|
|
20
|
+
4. **Abort Control**: Use AbortController to prevent race conditions on rapid user changes
|
|
21
|
+
5. **Guest Optimization**: Skip repository fetch for guest users (no fetch needed)
|
|
22
|
+
6. **Real-time Updates**: React to auth state changes
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
interface AuthProvider {
|
|
40
|
-
user: { uid: string } | null;
|
|
41
|
-
isGuest: boolean;
|
|
42
|
-
isAuthenticated: boolean;
|
|
43
|
-
}
|
|
44
|
-
```
|
|
24
|
+
### Integration Points
|
|
45
25
|
|
|
46
|
-
|
|
26
|
+
- **Auth Provider**: Custom auth provider with user state
|
|
27
|
+
- **Subscription Repository**: `src/infrastructure/repositories/SubscriptionRepository.ts`
|
|
28
|
+
- **Domain Layer**: `src/domain/entities/README.md`
|
|
29
|
+
- **TanStack Query**: For caching and real-time updates
|
|
47
30
|
|
|
48
|
-
|
|
49
|
-
|----------|------|-------------|
|
|
50
|
-
| `tier` | `'guest' \| 'freemium' \| 'premium'` | User tier |
|
|
51
|
-
| `isPremium` | `boolean` | User has premium access |
|
|
52
|
-
| `isGuest` | `boolean` | User is not authenticated |
|
|
53
|
-
| `isAuthenticated` | `boolean` | User is authenticated |
|
|
54
|
-
| `userId` | `string \| null` | User ID |
|
|
55
|
-
| `isLoading` | `boolean` | Premium status is loading |
|
|
56
|
-
| `error` | `string \| null` | Error message |
|
|
57
|
-
| `refresh` | `() => Promise<void>` | Manually refresh premium status |
|
|
31
|
+
## Restrictions
|
|
58
32
|
|
|
59
|
-
|
|
33
|
+
### REQUIRED
|
|
60
34
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
35
|
+
- **Auth Provider**: MUST provide valid auth provider object
|
|
36
|
+
- **Repository**: MUST provide subscription repository
|
|
37
|
+
- **Loading State**: MUST handle loading state
|
|
38
|
+
- **Error Handling**: MUST handle error state
|
|
65
39
|
|
|
66
|
-
|
|
67
|
-
tier,
|
|
68
|
-
isPremium,
|
|
69
|
-
isLoading,
|
|
70
|
-
} = useUserTierWithRepository({
|
|
71
|
-
auth,
|
|
72
|
-
repository: subscriptionRepository,
|
|
73
|
-
});
|
|
40
|
+
### PROHIBITED
|
|
74
41
|
|
|
75
|
-
|
|
42
|
+
- **NEVER** call without valid auth provider
|
|
43
|
+
- **NEVER** call without valid repository
|
|
44
|
+
- **DO NOT** hardcode tier values (use hook return values)
|
|
45
|
+
- **DO NOT** assume instant data availability
|
|
76
46
|
|
|
77
|
-
|
|
78
|
-
<View>
|
|
79
|
-
<Text>Tier: {tier.toUpperCase()}</Text>
|
|
80
|
-
<Text>Status: {isPremium ? 'Premium' : 'Free'}</Text>
|
|
81
|
-
</View>
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
```
|
|
47
|
+
### CRITICAL SAFETY
|
|
85
48
|
|
|
86
|
-
|
|
49
|
+
- **ALWAYS** check loading state before using tier values
|
|
50
|
+
- **NEVER** trust client-side tier for security enforcement
|
|
51
|
+
- **MUST** provide valid auth provider structure
|
|
52
|
+
- **ALWAYS** handle tier transitions gracefully
|
|
87
53
|
|
|
88
|
-
|
|
54
|
+
## AI Agent Guidelines
|
|
89
55
|
|
|
90
|
-
|
|
91
|
-
function WithCustomAuth() {
|
|
92
|
-
const authManager = useCustomAuthManager();
|
|
56
|
+
### When Implementing Tier Determination
|
|
93
57
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
58
|
+
1. **Always** provide valid auth provider with user, isGuest, isAuthenticated
|
|
59
|
+
2. **Always** provide valid subscription repository
|
|
60
|
+
3. **Always** handle loading state
|
|
61
|
+
4. **Always** handle error state
|
|
62
|
+
5. **Never** use for security decisions without server validation
|
|
99
63
|
|
|
100
|
-
|
|
101
|
-
auth: authProvider,
|
|
102
|
-
repository: subscriptionRepository,
|
|
103
|
-
});
|
|
64
|
+
### Integration Checklist
|
|
104
65
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
66
|
+
- [ ] Import from correct path: `@umituz/react-native-subscription`
|
|
67
|
+
- [ ] Provide valid auth provider object
|
|
68
|
+
- [ ] Provide valid subscription repository
|
|
69
|
+
- [ ] Handle loading state
|
|
70
|
+
- [ ] Handle error state
|
|
71
|
+
- [ ] Use refresh() when needed
|
|
72
|
+
- [ ] Test with guest user
|
|
73
|
+
- [ ] Test with freemium user
|
|
74
|
+
- [ ] Test with premium user
|
|
75
|
+
- [ ] Test user switching scenarios
|
|
113
76
|
|
|
114
|
-
###
|
|
77
|
+
### Common Patterns
|
|
115
78
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
79
|
+
1. **Auth Provider Setup**: Use with Firebase, custom auth, or auth library
|
|
80
|
+
2. **Tier-Based UI**: Show different features based on tier
|
|
81
|
+
3. **Navigation Guards**: Redirect based on tier requirements
|
|
82
|
+
4. **Tier Monitoring**: Track tier changes for analytics
|
|
83
|
+
5. **Manual Refresh**: Call refresh() after subscription changes
|
|
119
84
|
|
|
120
|
-
|
|
121
|
-
user: user ? { uid: user.uid } : null,
|
|
122
|
-
isGuest: !user,
|
|
123
|
-
isAuthenticated: !!user,
|
|
124
|
-
}), [user]);
|
|
85
|
+
## Related Documentation
|
|
125
86
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return <TierBadge tier={tier} />;
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### With Error Handling
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
function TierWithErrorHandling() {
|
|
141
|
-
const auth = useAuth();
|
|
142
|
-
|
|
143
|
-
const {
|
|
144
|
-
tier,
|
|
145
|
-
error,
|
|
146
|
-
isLoading,
|
|
147
|
-
refresh,
|
|
148
|
-
} = useUserTierWithRepository({
|
|
149
|
-
auth,
|
|
150
|
-
repository: subscriptionRepository,
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
useEffect(() => {
|
|
154
|
-
if (error) {
|
|
155
|
-
console.error('Failed to fetch tier:', error);
|
|
156
|
-
// Optionally show error to user
|
|
157
|
-
}
|
|
158
|
-
}, [error]);
|
|
159
|
-
|
|
160
|
-
if (isLoading) return <LoadingScreen />;
|
|
161
|
-
|
|
162
|
-
if (error) {
|
|
163
|
-
return (
|
|
164
|
-
<View>
|
|
165
|
-
<Text>Failed to load subscription status</Text>
|
|
166
|
-
<Button onPress={refresh} title="Retry" />
|
|
167
|
-
</View>
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return <TierDisplay tier={tier} />;
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### With Manual Refresh
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
function TierWithRefresh() {
|
|
179
|
-
const auth = useAuth();
|
|
180
|
-
|
|
181
|
-
const {
|
|
182
|
-
tier,
|
|
183
|
-
isLoading,
|
|
184
|
-
refresh,
|
|
185
|
-
} = useUserTierWithRepository({
|
|
186
|
-
auth,
|
|
187
|
-
repository: subscriptionRepository,
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
const handleRefresh = async () => {
|
|
191
|
-
await refresh();
|
|
192
|
-
Alert.alert('Success', 'Subscription status refreshed');
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
<View>
|
|
197
|
-
<Text>Tier: {tier}</Text>
|
|
198
|
-
|
|
199
|
-
<Button
|
|
200
|
-
onPress={handleRefresh}
|
|
201
|
-
disabled={isLoading}
|
|
202
|
-
title="Refresh Status"
|
|
203
|
-
/>
|
|
204
|
-
</View>
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### With Loading State
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
function TierWithLoading() {
|
|
213
|
-
const auth = useAuth();
|
|
214
|
-
|
|
215
|
-
const {
|
|
216
|
-
tier,
|
|
217
|
-
isPremium,
|
|
218
|
-
isLoading,
|
|
219
|
-
} = useUserTierWithRepository({
|
|
220
|
-
auth,
|
|
221
|
-
repository: subscriptionRepository,
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
return (
|
|
225
|
-
<View>
|
|
226
|
-
<Text>Tier: {tier}</Text>
|
|
227
|
-
<Text>Premium: {isPremium ? 'Yes' : 'No'}</Text>
|
|
228
|
-
|
|
229
|
-
{isLoading && (
|
|
230
|
-
<View style={styles.loadingContainer}>
|
|
231
|
-
<ActivityIndicator size="small" />
|
|
232
|
-
<Text>Checking subscription...</Text>
|
|
233
|
-
</View>
|
|
234
|
-
)}
|
|
235
|
-
</View>
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
## Examples
|
|
241
|
-
|
|
242
|
-
### Complete User Profile
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
function UserProfile() {
|
|
246
|
-
const auth = useAuth();
|
|
247
|
-
|
|
248
|
-
const {
|
|
249
|
-
tier,
|
|
250
|
-
isPremium,
|
|
251
|
-
userId,
|
|
252
|
-
isLoading,
|
|
253
|
-
error,
|
|
254
|
-
} = useUserTierWithRepository({
|
|
255
|
-
auth,
|
|
256
|
-
repository: subscriptionRepository,
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
if (isLoading) return <LoadingScreen />;
|
|
260
|
-
|
|
261
|
-
return (
|
|
262
|
-
<ScrollView>
|
|
263
|
-
<UserHeader userId={userId} />
|
|
264
|
-
|
|
265
|
-
<View style={styles.section}>
|
|
266
|
-
<Text style={styles.label}>Account Status</Text>
|
|
267
|
-
<TierBadge tier={tier} />
|
|
268
|
-
</View>
|
|
269
|
-
|
|
270
|
-
{isPremium && (
|
|
271
|
-
<View style={styles.section}>
|
|
272
|
-
<Text style={styles.label}>Premium Benefits</Text>
|
|
273
|
-
<BenefitList />
|
|
274
|
-
</View>
|
|
275
|
-
)}
|
|
276
|
-
|
|
277
|
-
{!isPremium && tier !== 'guest' && (
|
|
278
|
-
<View style={styles.section}>
|
|
279
|
-
<Button
|
|
280
|
-
onPress={() => navigation.navigate('Paywall')}
|
|
281
|
-
title="Upgrade to Premium"
|
|
282
|
-
/>
|
|
283
|
-
</View>
|
|
284
|
-
)}
|
|
285
|
-
|
|
286
|
-
{error && (
|
|
287
|
-
<ErrorBanner
|
|
288
|
-
message="Failed to load subscription status"
|
|
289
|
-
onRetry={() => refresh()}
|
|
290
|
-
/>
|
|
291
|
-
)}
|
|
292
|
-
</ScrollView>
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
### Navigation Guard
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
function ProtectedScreen() {
|
|
301
|
-
const auth = useAuth();
|
|
302
|
-
const navigation = useNavigation();
|
|
303
|
-
|
|
304
|
-
const {
|
|
305
|
-
tier,
|
|
306
|
-
isLoading,
|
|
307
|
-
} = useUserTierWithRepository({
|
|
308
|
-
auth,
|
|
309
|
-
repository: subscriptionRepository,
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
useEffect(() => {
|
|
313
|
-
if (!isLoading && tier !== 'premium') {
|
|
314
|
-
navigation.replace('Paywall', {
|
|
315
|
-
returnTo: 'ProtectedScreen',
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
}, [tier, isLoading]);
|
|
319
|
-
|
|
320
|
-
if (isLoading) return <LoadingScreen />;
|
|
321
|
-
if (tier !== 'premium') return null;
|
|
322
|
-
|
|
323
|
-
return <ProtectedContent />;
|
|
324
|
-
}
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### Conditional Features
|
|
328
|
-
|
|
329
|
-
```typescript
|
|
330
|
-
function TierBasedFeatures() {
|
|
331
|
-
const auth = useAuth();
|
|
332
|
-
|
|
333
|
-
const { tier } = useUserTierWithRepository({
|
|
334
|
-
auth,
|
|
335
|
-
repository: subscriptionRepository,
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
return (
|
|
339
|
-
<View>
|
|
340
|
-
{/* Available to everyone */}
|
|
341
|
-
<BasicFeature />
|
|
342
|
-
|
|
343
|
-
{/* Available to freemium and premium */}
|
|
344
|
-
{(tier === 'freemium' || tier === 'premium') && (
|
|
345
|
-
<AuthenticatedFeature />
|
|
346
|
-
)}
|
|
347
|
-
|
|
348
|
-
{/* Premium only */}
|
|
349
|
-
{tier === 'premium' && (
|
|
350
|
-
<PremiumFeature />
|
|
351
|
-
)}
|
|
352
|
-
</View>
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### With Analytics
|
|
358
|
-
|
|
359
|
-
```typescript
|
|
360
|
-
function TierTracker() {
|
|
361
|
-
const auth = useAuth();
|
|
362
|
-
|
|
363
|
-
const { tier } = useUserTierWithRepository({
|
|
364
|
-
auth,
|
|
365
|
-
repository: subscriptionRepository,
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
const previousTier = useRef(tier);
|
|
369
|
-
|
|
370
|
-
useEffect(() => {
|
|
371
|
-
if (previousTier.current !== tier) {
|
|
372
|
-
analytics.track('tier_changed', {
|
|
373
|
-
from: previousTier.current,
|
|
374
|
-
to: tier,
|
|
375
|
-
timestamp: Date.now(),
|
|
376
|
-
});
|
|
377
|
-
previousTier.current = tier;
|
|
378
|
-
}
|
|
379
|
-
}, [tier]);
|
|
380
|
-
|
|
381
|
-
return <YourComponent />;
|
|
382
|
-
}
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
## How It Works
|
|
386
|
-
|
|
387
|
-
### Automatic Fetching
|
|
388
|
-
|
|
389
|
-
```typescript
|
|
390
|
-
// Mount with authenticated user
|
|
391
|
-
auth.user = { uid: 'user-123' }
|
|
392
|
-
auth.isAuthenticated = true
|
|
393
|
-
→ Fetch premium status from repository ✅
|
|
394
|
-
|
|
395
|
-
// Status returned
|
|
396
|
-
tier = 'premium'
|
|
397
|
-
isPremium = true
|
|
398
|
-
isLoading = false ✅
|
|
399
|
-
|
|
400
|
-
// User logs out
|
|
401
|
-
auth.user = null
|
|
402
|
-
auth.isAuthenticated = false
|
|
403
|
-
→ No fetch (guest users can't be premium) ✅
|
|
404
|
-
tier = 'guest'
|
|
405
|
-
isPremium = false ✅
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
### Guest Users
|
|
409
|
-
|
|
410
|
-
```typescript
|
|
411
|
-
auth.user = null
|
|
412
|
-
auth.isGuest = true
|
|
413
|
-
auth.isAuthenticated = false
|
|
414
|
-
|
|
415
|
-
// Hook doesn't fetch for guests
|
|
416
|
-
tier = 'guest'
|
|
417
|
-
isPremium = false
|
|
418
|
-
isLoading = false (no fetch needed) ✅
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
### Abort Handling
|
|
422
|
-
|
|
423
|
-
The hook uses AbortController for race condition prevention:
|
|
424
|
-
|
|
425
|
-
```typescript
|
|
426
|
-
// User changes quickly: A → B → A
|
|
427
|
-
// Only the last user's status is set
|
|
428
|
-
// Intermediate fetches are aborted ✅
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
## Best Practices
|
|
432
|
-
|
|
433
|
-
1. **Provide valid auth** - Ensure auth provider has correct state
|
|
434
|
-
2. **Handle loading** - Show loading during initial fetch
|
|
435
|
-
3. **Handle errors** - Display error messages and retry option
|
|
436
|
-
4. **Use refresh** - Call refresh when needed (e.g., after purchase)
|
|
437
|
-
5. **Check tier** - Use tier for feature gating
|
|
438
|
-
6. **Test transitions** - Verify guest → freemium → premium flows
|
|
439
|
-
7. **Cache appropriately** - Repository should handle caching
|
|
440
|
-
|
|
441
|
-
## Related Hooks
|
|
442
|
-
|
|
443
|
-
- **useUserTier** - Tier logic without repository
|
|
444
|
-
- **usePremium** - Premium status checking
|
|
445
|
-
- **useAuthGate** - Authentication gating
|
|
446
|
-
- **useSubscription** - Subscription details
|
|
447
|
-
|
|
448
|
-
## See Also
|
|
449
|
-
|
|
450
|
-
- [User Tier Guide](../../../docs/USER_TIER.md)
|
|
451
|
-
- [Repository Pattern](../../infrastructure/repositories/README.md)
|
|
452
|
-
- [Tier Utilities](../../utils/tierUtils.md)
|
|
87
|
+
- **useUserTier**: Tier logic without repository
|
|
88
|
+
- **usePremium**: Premium status checking
|
|
89
|
+
- **useAuthGate**: Authentication gating
|
|
90
|
+
- **useSubscription**: Subscription details
|
|
91
|
+
- **Repository Pattern**: `src/infrastructure/repositories/README.md`
|
|
92
|
+
- **User Tier Utils**: `src/utils/README.md`
|