@umituz/react-native-subscription 2.26.14 → 2.26.16
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/package.json +1 -1
- package/src/domains/paywall/components/PaywallModal.tsx +36 -13
- package/src/domains/paywall/components/PlanCard.tsx +16 -3
- package/src/domains/paywall/entities/types.ts +4 -0
- package/src/domains/paywall/hooks/usePaywallTranslations.ts +8 -0
- package/src/presentation/hooks/index.ts +0 -13
- package/src/presentation/hooks/useFeatureGate.ts +12 -33
- package/src/revenuecat/domain/types/RevenueCatTypes.ts +32 -0
- package/src/revenuecat/index.ts +1 -0
- package/src/revenuecat/presentation/hooks/useRevenueCatTrialEligibility.ts +179 -0
- package/src/presentation/hooks/useAuthAwarePurchase.md +0 -92
- package/src/presentation/hooks/useAuthGate.md +0 -89
- package/src/presentation/hooks/useAuthGate.ts +0 -65
- package/src/presentation/hooks/useCreditChecker.md +0 -102
- package/src/presentation/hooks/useCreditChecker.ts +0 -41
- package/src/presentation/hooks/useCreditsGate.md +0 -94
- package/src/presentation/hooks/useCreditsGate.ts +0 -67
- package/src/presentation/hooks/useDevTestCallbacks.md +0 -91
- package/src/presentation/hooks/useDevTestCallbacks.ts +0 -142
- package/src/presentation/hooks/useInitializeCredits.md +0 -92
- package/src/presentation/hooks/useInitializeCredits.ts +0 -57
- package/src/presentation/hooks/usePremiumGate.md +0 -88
- package/src/presentation/hooks/usePremiumGate.ts +0 -116
- package/src/presentation/hooks/usePremiumWithCredits.md +0 -92
- package/src/presentation/hooks/usePremiumWithCredits.ts +0 -48
- package/src/presentation/hooks/useSubscription.md +0 -94
- package/src/presentation/hooks/useSubscription.ts +0 -119
- package/src/presentation/hooks/useSubscriptionDetails.md +0 -93
- package/src/presentation/hooks/useSubscriptionDetails.ts +0 -85
- package/src/presentation/hooks/useSubscriptionGate.md +0 -84
- package/src/presentation/hooks/useSubscriptionGate.ts +0 -67
- package/src/presentation/hooks/useSubscriptionStatus.md +0 -94
- package/src/presentation/hooks/useTrialEligibility.ts +0 -66
- package/src/presentation/hooks/useUserTier.md +0 -91
- package/src/presentation/hooks/useUserTier.ts +0 -78
- package/src/presentation/hooks/useUserTierWithRepository.md +0 -92
- package/src/presentation/hooks/useUserTierWithRepository.ts +0 -151
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useUserTier Hook
|
|
3
|
-
*
|
|
4
|
-
* Centralized hook for determining user tier (Guest, Freemium, Premium)
|
|
5
|
-
* Single source of truth for all premium/freemium/guest checks
|
|
6
|
-
*
|
|
7
|
-
* This hook only handles LOGICAL tier determination.
|
|
8
|
-
* Database operations should be handled by the app via PremiumStatusFetcher.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```typescript
|
|
12
|
-
* const { tier, isPremium, isGuest } = useUserTier({
|
|
13
|
-
* isGuest: false,
|
|
14
|
-
* userId: 'user123',
|
|
15
|
-
* isPremium: true, // App should fetch this from database
|
|
16
|
-
* });
|
|
17
|
-
*
|
|
18
|
-
* // Simple, clean checks
|
|
19
|
-
* if (tier === "guest") {
|
|
20
|
-
* // Show guest upgrade card
|
|
21
|
-
* } else if (tier === "freemium") {
|
|
22
|
-
* // Show freemium limits
|
|
23
|
-
* } else {
|
|
24
|
-
* // Premium features
|
|
25
|
-
* }
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import { useMemo } from 'react';
|
|
30
|
-
import { getUserTierInfo } from '../../utils/tierUtils';
|
|
31
|
-
import type { UserTierInfo } from '../../utils/types';
|
|
32
|
-
|
|
33
|
-
export interface UseUserTierParams {
|
|
34
|
-
/** Whether user is a guest */
|
|
35
|
-
isGuest: boolean;
|
|
36
|
-
/** User ID (null for guests) */
|
|
37
|
-
userId: string | null;
|
|
38
|
-
/** Whether user has active premium subscription (app should fetch from database) */
|
|
39
|
-
isPremium: boolean;
|
|
40
|
-
/** Optional: Loading state from app */
|
|
41
|
-
isLoading?: boolean;
|
|
42
|
-
/** Optional: Error state from app */
|
|
43
|
-
error?: string | null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface UseUserTierResult extends UserTierInfo {
|
|
47
|
-
/** Whether premium status is currently loading */
|
|
48
|
-
isLoading: boolean;
|
|
49
|
-
/** Premium status error (if any) */
|
|
50
|
-
error: string | null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Hook to get user tier information
|
|
55
|
-
* Combines auth state and premium status into single source of truth
|
|
56
|
-
*
|
|
57
|
-
* All premium/freemium/guest checks are centralized here:
|
|
58
|
-
* - Guest: isGuest || !userId → always freemium, never premium
|
|
59
|
-
* - Freemium: authenticated but !isPremium
|
|
60
|
-
* - Premium: authenticated && isPremium
|
|
61
|
-
*
|
|
62
|
-
* Note: This hook only handles LOGICAL tier determination.
|
|
63
|
-
* Database operations (fetching premium status) should be handled by the app.
|
|
64
|
-
*/
|
|
65
|
-
export function useUserTier(params: UseUserTierParams): UseUserTierResult {
|
|
66
|
-
const { isGuest, userId, isPremium, isLoading = false, error = null } = params;
|
|
67
|
-
|
|
68
|
-
// Calculate tier info using centralized logic
|
|
69
|
-
const tierInfo = useMemo(() => {
|
|
70
|
-
return getUserTierInfo(isGuest, userId, isPremium);
|
|
71
|
-
}, [isGuest, userId, isPremium]);
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
...tierInfo,
|
|
75
|
-
isLoading,
|
|
76
|
-
error,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# useUserTierWithRepository Hook
|
|
2
|
-
|
|
3
|
-
Automatically fetches premium status and provides tier information with repository integration.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
**Import Path**: `@umituz/react-native-subscription`
|
|
8
|
-
|
|
9
|
-
**File**: `src/presentation/hooks/useUserTierWithRepository.ts`
|
|
10
|
-
|
|
11
|
-
**Type**: Hook
|
|
12
|
-
|
|
13
|
-
## Strategy
|
|
14
|
-
|
|
15
|
-
### Tier Determination with Repository
|
|
16
|
-
|
|
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
|
|
23
|
-
|
|
24
|
-
### Integration Points
|
|
25
|
-
|
|
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
|
|
30
|
-
|
|
31
|
-
## Restrictions
|
|
32
|
-
|
|
33
|
-
### REQUIRED
|
|
34
|
-
|
|
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
|
|
39
|
-
|
|
40
|
-
### PROHIBITED
|
|
41
|
-
|
|
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
|
|
46
|
-
|
|
47
|
-
### CRITICAL SAFETY
|
|
48
|
-
|
|
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
|
|
53
|
-
|
|
54
|
-
## AI Agent Guidelines
|
|
55
|
-
|
|
56
|
-
### When Implementing Tier Determination
|
|
57
|
-
|
|
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
|
|
63
|
-
|
|
64
|
-
### Integration Checklist
|
|
65
|
-
|
|
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
|
|
76
|
-
|
|
77
|
-
### Common Patterns
|
|
78
|
-
|
|
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
|
|
84
|
-
|
|
85
|
-
## Related Documentation
|
|
86
|
-
|
|
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`
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useUserTierWithRepository Hook
|
|
3
|
-
* Automatically fetches premium status and provides user tier information.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useEffect, useState, useCallback } from 'react';
|
|
7
|
-
import { useUserTier, type UseUserTierParams } from './useUserTier';
|
|
8
|
-
import type { ISubscriptionRepository } from '../../application/ports/ISubscriptionRepository';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Auth provider interface
|
|
12
|
-
* Apps should provide an object that matches this interface
|
|
13
|
-
*/
|
|
14
|
-
export interface AuthProvider {
|
|
15
|
-
/** Current user object (null for guests) */
|
|
16
|
-
user: { uid: string } | null;
|
|
17
|
-
/** Whether user is a guest */
|
|
18
|
-
isGuest: boolean;
|
|
19
|
-
/** Whether user is authenticated */
|
|
20
|
-
isAuthenticated: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface UseUserTierWithRepositoryParams {
|
|
24
|
-
/** Auth provider (e.g., result of useAuth hook) */
|
|
25
|
-
auth: AuthProvider;
|
|
26
|
-
/** Subscription repository for fetching premium status */
|
|
27
|
-
repository: ISubscriptionRepository;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface UseUserTierWithRepositoryResult {
|
|
31
|
-
/** User tier: 'guest' | 'freemium' | 'premium' */
|
|
32
|
-
tier: 'guest' | 'freemium' | 'premium';
|
|
33
|
-
/** Whether user has premium access */
|
|
34
|
-
isPremium: boolean;
|
|
35
|
-
/** Whether user is a guest */
|
|
36
|
-
isGuest: boolean;
|
|
37
|
-
/** Whether user is authenticated */
|
|
38
|
-
isAuthenticated: boolean;
|
|
39
|
-
/** User ID (null for guests) */
|
|
40
|
-
userId: string | null;
|
|
41
|
-
/** Whether premium status is currently loading */
|
|
42
|
-
isLoading: boolean;
|
|
43
|
-
/** Premium status error (if any) */
|
|
44
|
-
error: string | null;
|
|
45
|
-
/** Refresh premium status from repository */
|
|
46
|
-
refresh: () => Promise<void>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Hook that automatically fetches premium status and provides user tier information
|
|
51
|
-
*
|
|
52
|
-
* This hook eliminates the need for app-specific useUserTier wrappers by:
|
|
53
|
-
* 1. Automatically fetching premium status from repository
|
|
54
|
-
* 2. Handling loading and error states
|
|
55
|
-
* 3. Providing refresh functionality
|
|
56
|
-
* 4. Using centralized tier logic from useUserTier
|
|
57
|
-
*/
|
|
58
|
-
export function useUserTierWithRepository(
|
|
59
|
-
params: UseUserTierWithRepositoryParams,
|
|
60
|
-
): UseUserTierWithRepositoryResult {
|
|
61
|
-
const { auth, repository } = params;
|
|
62
|
-
const { user, isGuest, isAuthenticated } = auth;
|
|
63
|
-
|
|
64
|
-
const [isPremium, setIsPremium] = useState<boolean>(false);
|
|
65
|
-
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
66
|
-
const [error, setError] = useState<string | null>(null);
|
|
67
|
-
|
|
68
|
-
// Fetch premium status from repository
|
|
69
|
-
const fetchPremiumStatus = useCallback(async (signal?: AbortSignal) => {
|
|
70
|
-
// Guest users are never premium - no need to fetch
|
|
71
|
-
if (!isAuthenticated || !user) {
|
|
72
|
-
setIsPremium(false);
|
|
73
|
-
setIsLoading(false);
|
|
74
|
-
setError(null);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
setIsLoading(true);
|
|
80
|
-
setError(null);
|
|
81
|
-
|
|
82
|
-
const status = await repository.getSubscriptionStatus(user.uid);
|
|
83
|
-
|
|
84
|
-
// Check if operation was aborted
|
|
85
|
-
if (signal?.aborted) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const isPremiumValue =
|
|
90
|
-
status !== null && repository.isSubscriptionValid(status);
|
|
91
|
-
|
|
92
|
-
// Check again before setting state
|
|
93
|
-
if (!signal?.aborted) {
|
|
94
|
-
setIsPremium(isPremiumValue);
|
|
95
|
-
setIsLoading(false);
|
|
96
|
-
}
|
|
97
|
-
} catch (err) {
|
|
98
|
-
// Don't set state if operation was aborted
|
|
99
|
-
if (!signal?.aborted) {
|
|
100
|
-
const errorMessage =
|
|
101
|
-
err instanceof Error ? err.message : 'Failed to fetch premium status';
|
|
102
|
-
setError(errorMessage);
|
|
103
|
-
setIsPremium(false);
|
|
104
|
-
setIsLoading(false);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}, [isAuthenticated, user, repository]);
|
|
108
|
-
|
|
109
|
-
// Fetch premium status when auth state changes
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
const abortController = new AbortController();
|
|
112
|
-
|
|
113
|
-
fetchPremiumStatus(abortController.signal).catch(() => {
|
|
114
|
-
// Error is handled in fetchPremiumStatus
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
return () => {
|
|
118
|
-
abortController.abort();
|
|
119
|
-
};
|
|
120
|
-
}, [fetchPremiumStatus]);
|
|
121
|
-
|
|
122
|
-
// Refresh function
|
|
123
|
-
const refresh = useCallback(async () => {
|
|
124
|
-
if (!isAuthenticated || !user) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
const abortController = new AbortController();
|
|
128
|
-
try {
|
|
129
|
-
await fetchPremiumStatus(abortController.signal);
|
|
130
|
-
} finally {
|
|
131
|
-
abortController.abort();
|
|
132
|
-
}
|
|
133
|
-
}, [isAuthenticated, user, fetchPremiumStatus]);
|
|
134
|
-
|
|
135
|
-
// Use base useUserTier hook for tier logic
|
|
136
|
-
const useUserTierParams: UseUserTierParams = {
|
|
137
|
-
isGuest: isGuest || !isAuthenticated,
|
|
138
|
-
userId: user?.uid || null,
|
|
139
|
-
isPremium,
|
|
140
|
-
isLoading,
|
|
141
|
-
error,
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const tierInfo = useUserTier(useUserTierParams);
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
...tierInfo,
|
|
148
|
-
refresh,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|