@umituz/react-native-subscription 2.26.13 → 2.26.15

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.
Files changed (39) hide show
  1. package/package.json +1 -1
  2. package/src/domains/paywall/components/PaywallModal.tsx +36 -13
  3. package/src/domains/paywall/components/PlanCard.tsx +16 -3
  4. package/src/domains/paywall/entities/types.ts +4 -0
  5. package/src/domains/paywall/hooks/usePaywallTranslations.ts +8 -0
  6. package/src/presentation/hooks/index.ts +0 -15
  7. package/src/presentation/hooks/useFeatureGate.ts +41 -116
  8. package/src/revenuecat/domain/types/RevenueCatTypes.ts +32 -0
  9. package/src/revenuecat/index.ts +1 -0
  10. package/src/revenuecat/presentation/hooks/useRevenueCatTrialEligibility.ts +179 -0
  11. package/src/presentation/hooks/useAuthAwarePurchase.md +0 -92
  12. package/src/presentation/hooks/useAuthAwarePurchase.ts +0 -138
  13. package/src/presentation/hooks/useAuthGate.md +0 -89
  14. package/src/presentation/hooks/useAuthGate.ts +0 -65
  15. package/src/presentation/hooks/useCreditChecker.md +0 -102
  16. package/src/presentation/hooks/useCreditChecker.ts +0 -41
  17. package/src/presentation/hooks/useCreditsGate.md +0 -94
  18. package/src/presentation/hooks/useCreditsGate.ts +0 -81
  19. package/src/presentation/hooks/useDevTestCallbacks.md +0 -91
  20. package/src/presentation/hooks/useDevTestCallbacks.ts +0 -142
  21. package/src/presentation/hooks/useInitializeCredits.md +0 -92
  22. package/src/presentation/hooks/useInitializeCredits.ts +0 -57
  23. package/src/presentation/hooks/usePremiumGate.md +0 -88
  24. package/src/presentation/hooks/usePremiumGate.ts +0 -116
  25. package/src/presentation/hooks/usePremiumWithCredits.md +0 -92
  26. package/src/presentation/hooks/usePremiumWithCredits.ts +0 -48
  27. package/src/presentation/hooks/useSubscription.md +0 -94
  28. package/src/presentation/hooks/useSubscription.ts +0 -119
  29. package/src/presentation/hooks/useSubscriptionDetails.md +0 -93
  30. package/src/presentation/hooks/useSubscriptionDetails.ts +0 -85
  31. package/src/presentation/hooks/useSubscriptionGate.md +0 -84
  32. package/src/presentation/hooks/useSubscriptionGate.ts +0 -67
  33. package/src/presentation/hooks/useSubscriptionStatus.md +0 -94
  34. package/src/presentation/hooks/useSubscriptionStatus.ts +0 -64
  35. package/src/presentation/hooks/useTrialEligibility.ts +0 -66
  36. package/src/presentation/hooks/useUserTier.md +0 -91
  37. package/src/presentation/hooks/useUserTier.ts +0 -78
  38. package/src/presentation/hooks/useUserTierWithRepository.md +0 -92
  39. package/src/presentation/hooks/useUserTierWithRepository.ts +0 -151
@@ -1,64 +0,0 @@
1
- /**
2
- * useSubscriptionStatus Hook
3
- *
4
- * TanStack Query hook for checking real subscription status from RevenueCat.
5
- * This provides the actual premium status based on entitlements, not credits.
6
- */
7
-
8
- import { useQuery } from "@umituz/react-native-design-system";
9
- import { SubscriptionManager } from "../../revenuecat/infrastructure/managers/SubscriptionManager";
10
-
11
- export const subscriptionStatusQueryKeys = {
12
- all: ["subscriptionStatus"] as const,
13
- user: (userId: string) => ["subscriptionStatus", userId] as const,
14
- };
15
-
16
- export interface SubscriptionStatusResult {
17
- isPremium: boolean;
18
- expirationDate: Date | null;
19
- isLoading: boolean;
20
- error: Error | null;
21
- refetch: () => void;
22
- }
23
-
24
- export interface UseSubscriptionStatusParams {
25
- userId: string | undefined;
26
- enabled?: boolean;
27
- }
28
-
29
- /**
30
- * Check real subscription status from RevenueCat
31
- *
32
- * @param userId - User ID
33
- * @param enabled - Whether to enable the query
34
- * @returns Subscription status with isPremium flag
35
- */
36
- export const useSubscriptionStatus = ({
37
- userId,
38
- enabled = true,
39
- }: UseSubscriptionStatusParams): SubscriptionStatusResult => {
40
- const { data, isLoading, error, refetch } = useQuery({
41
- queryKey: subscriptionStatusQueryKeys.user(userId ?? ""),
42
- queryFn: async () => {
43
- if (!userId) {
44
- return { isPremium: false, expirationDate: null };
45
- }
46
-
47
- return SubscriptionManager.checkPremiumStatus();
48
- },
49
- enabled: enabled && !!userId && SubscriptionManager.isInitializedForUser(userId),
50
- staleTime: 0, // No cache - always fetch fresh premium status
51
- gcTime: 0, // Don't cache - garbage collect immediately
52
- refetchOnMount: true,
53
- refetchOnWindowFocus: true,
54
- refetchOnReconnect: true,
55
- });
56
-
57
- return {
58
- isPremium: data?.isPremium ?? false,
59
- expirationDate: data?.expirationDate ?? null,
60
- isLoading,
61
- error: error as Error | null,
62
- refetch,
63
- };
64
- };
@@ -1,66 +0,0 @@
1
- /**
2
- * useTrialEligibility Hook
3
- * Checks if device is eligible for free trial
4
- * Uses persistent device ID to prevent trial abuse
5
- */
6
-
7
- import { useState, useEffect, useCallback } from "react";
8
- import {
9
- checkTrialEligibility,
10
- getDeviceId,
11
- } from "../../infrastructure/services/TrialService";
12
-
13
- export interface UseTrialEligibilityResult {
14
- /** Whether device is eligible for trial */
15
- isEligible: boolean;
16
- /** Whether eligibility check is in progress */
17
- isLoading: boolean;
18
- /** Reason why not eligible (if applicable) */
19
- reason?: "already_used" | "device_not_found" | "error";
20
- /** Device ID used for checking */
21
- deviceId: string | null;
22
- /** Refresh eligibility status */
23
- refresh: () => Promise<void>;
24
- }
25
-
26
- /**
27
- * Hook to check trial eligibility based on device ID
28
- * Device ID persists across app reinstalls via Keychain
29
- */
30
- export function useTrialEligibility(): UseTrialEligibilityResult {
31
- const [isEligible, setIsEligible] = useState(true);
32
- const [isLoading, setIsLoading] = useState(true);
33
- const [reason, setReason] = useState<"already_used" | "device_not_found" | "error">();
34
- const [deviceId, setDeviceId] = useState<string | null>(null);
35
-
36
- const checkEligibility = useCallback(async () => {
37
- setIsLoading(true);
38
-
39
- try {
40
- const id = await getDeviceId();
41
- setDeviceId(id);
42
-
43
- const result = await checkTrialEligibility(id);
44
- setIsEligible(result.eligible);
45
- setReason(result.reason);
46
- } catch {
47
- // On error, allow trial (better UX)
48
- setIsEligible(true);
49
- setReason("error");
50
- } finally {
51
- setIsLoading(false);
52
- }
53
- }, []);
54
-
55
- useEffect(() => {
56
- checkEligibility();
57
- }, [checkEligibility]);
58
-
59
- return {
60
- isEligible,
61
- isLoading,
62
- reason,
63
- deviceId,
64
- refresh: checkEligibility,
65
- };
66
- }
@@ -1,91 +0,0 @@
1
- # useUserTier Hook
2
-
3
- Hook for determining and tracking user tier (guest, free, premium).
4
-
5
- ## Location
6
-
7
- **Import Path**: `@umituz/react-native-subscription`
8
-
9
- **File**: `src/presentation/hooks/useUserTier.ts`
10
-
11
- **Type**: Hook
12
-
13
- ## Strategy
14
-
15
- ### Tier Determination Flow
16
-
17
- 1. **Auth State Check**: Determine if user is authenticated
18
- 2. **Subscription Check**: Verify active subscription status
19
- 3. **Tier Assignment**: Assign tier based on auth + subscription
20
- 4. **Real-time Updates**: Update tier when auth or subscription changes
21
- 5. **Caching**: Cache tier status for performance
22
- 6. **Transition Tracking**: Monitor and track tier changes
23
-
24
- ### Integration Points
25
-
26
- - **Auth Context**: User authentication 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
- - **Tier Handling**: MUST handle all three tiers (guest, free, premium)
36
- - **Loading State**: MUST handle loading state
37
- - **Auth Integration**: MUST sync with authentication state
38
- - **Guest Support**: MUST support unauthenticated users
39
-
40
- ### PROHIBITED
41
-
42
- - **NEVER** assume user is authenticated (check isGuest)
43
- - **NEVER** use for security decisions without server validation
44
- - **DO NOT** hardcode tier values (use hook return values)
45
-
46
- ### CRITICAL SAFETY
47
-
48
- - **ALWAYS** check loading state before using tier values
49
- - **NEVER** trust client-side tier for security enforcement
50
- - **MUST** handle tier transitions gracefully
51
- - **ALWAYS** test with all three tier types
52
-
53
- ## AI Agent Guidelines
54
-
55
- ### When Implementing Tier-Based Features
56
-
57
- 1. **Always** handle loading state before checking tier
58
- 2. **Always** handle all three tiers (guest, free, premium)
59
- 3. **Always** provide upgrade path for free users
60
- 4. **Never** show features to guests that require auth
61
- 5. **Always** track tier changes in analytics
62
-
63
- ### Integration Checklist
64
-
65
- - [ ] Import from correct path: `@umituz/react-native-subscription`
66
- - [ ] Handle loading state
67
- - [ ] Implement logic for guest users
68
- - [ ] Implement logic for free users
69
- - [ ] Implement logic for premium users
70
- - [ ] Provide upgrade prompts for free users
71
- - [ ] Provide auth prompts for guest users
72
- - [ ] Test tier transitions (guest → free → premium)
73
- - [ ] Test tier downgrade (premium → free)
74
- - [ ] Track tier changes in analytics
75
-
76
- ### Common Patterns
77
-
78
- 1. **Conditional Rendering**: Show/hide features based on tier
79
- 2. **Navigation Guards**: Redirect based on tier requirements
80
- 3. **Feature Flags**: Enable features per tier
81
- 4. **Progress Indicators**: Show tier progression
82
- 5. **Upgrade Prompts**: Guide users to higher tiers
83
-
84
- ## Related Documentation
85
-
86
- - **useAuth**: Authentication state
87
- - **usePremium**: Premium subscription check
88
- - **useSubscription**: Detailed subscription information
89
- - **useAuthGate**: Authentication and subscription gating
90
- - **Domain Entities**: `src/domain/entities/README.md`
91
- - **User Tier Utils**: `src/utils/README.md`
@@ -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
-