@umituz/react-native-subscription 2.12.16 → 2.12.18
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 +14 -7
- package/src/application/ports/ISubscriptionRepository.ts +1 -1
- package/src/application/ports/ISubscriptionService.ts +1 -1
- package/src/domain/value-objects/SubscriptionConfig.ts +1 -1
- package/src/domains/paywall/components/CreditCard.tsx +5 -2
- package/src/domains/paywall/components/FeatureList.tsx +1 -1
- package/src/domains/paywall/components/PaywallModal.tsx +7 -3
- package/src/domains/paywall/components/PaywallTabBar.tsx +1 -1
- package/src/domains/paywall/components/PlanCard.tsx +3 -1
- package/src/domains/paywall/entities/types.ts +1 -0
- package/src/index.ts +55 -53
- package/src/infrastructure/repositories/CreditsRepository.ts +3 -3
- package/src/infrastructure/repositories/CreditsRepositoryProvider.ts +2 -2
- package/src/infrastructure/services/ActivationHandler.ts +3 -3
- package/src/infrastructure/services/CreditsInitializer.ts +1 -1
- package/src/infrastructure/services/SubscriptionService.ts +6 -6
- package/src/presentation/components/details/PremiumDetailsCard.tsx +19 -48
- package/src/presentation/components/details/PremiumDetailsCardTypes.ts +5 -3
- package/src/presentation/components/details/PremiumStatusBadge.tsx +9 -9
- package/src/presentation/hooks/useCreditChecker.ts +3 -3
- package/src/presentation/hooks/useCredits.ts +2 -2
- package/src/presentation/hooks/useDeductCredit.ts +2 -2
- package/src/presentation/hooks/useDevTestCallbacks.ts +143 -0
- package/src/presentation/hooks/usePremiumWithConfig.ts +2 -2
- package/src/presentation/hooks/useSubscription.ts +3 -3
- package/src/presentation/hooks/useSubscriptionDetails.ts +2 -2
- package/src/presentation/hooks/useUserTier.ts +2 -2
- package/src/presentation/hooks/useUserTierWithRepository.ts +1 -1
- package/src/presentation/screens/SubscriptionDetailScreen.tsx +12 -11
- package/src/presentation/screens/components/CreditItem.tsx +8 -11
- package/src/presentation/screens/components/SubscriptionActions.tsx +10 -22
- package/src/presentation/screens/components/SubscriptionHeader.tsx +22 -22
- package/src/revenuecat/infrastructure/handlers/PackageHandler.ts +2 -2
- package/src/revenuecat/infrastructure/managers/SubscriptionManager.ts +7 -7
- package/src/revenuecat/infrastructure/services/CustomerInfoListenerManager.ts +2 -2
- package/src/revenuecat/infrastructure/services/PurchaseHandler.ts +5 -5
- package/src/revenuecat/infrastructure/services/RestoreHandler.ts +5 -5
- package/src/revenuecat/infrastructure/services/RevenueCatInitializer.ts +4 -4
- package/src/revenuecat/infrastructure/services/RevenueCatService.ts +3 -3
- package/src/revenuecat/infrastructure/services/ServiceStateManager.ts +2 -2
- package/src/revenuecat/infrastructure/utils/ApiKeyResolver.ts +1 -1
- package/src/revenuecat/infrastructure/utils/ExpirationDateCalculator.ts +1 -1
- package/src/revenuecat/infrastructure/utils/PremiumStatusSyncer.ts +2 -2
- package/src/revenuecat/presentation/hooks/useInitializeSubscription.ts +1 -1
- package/src/revenuecat/presentation/hooks/usePurchasePackage.ts +1 -1
- package/src/revenuecat/presentation/hooks/useRestorePurchase.ts +1 -1
- package/src/revenuecat/presentation/hooks/useRevenueCat.ts +2 -2
- package/src/revenuecat/presentation/hooks/useSubscriptionPackages.ts +1 -1
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React from "react";
|
|
8
|
-
import { View,
|
|
9
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
8
|
+
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
|
9
|
+
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
10
10
|
import { PremiumStatusBadge } from "./PremiumStatusBadge";
|
|
11
11
|
import { DetailRow } from "./DetailRow";
|
|
12
12
|
import { CreditRow } from "./CreditRow";
|
|
@@ -33,14 +33,15 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
|
|
|
33
33
|
<View style={[styles.card, { backgroundColor: tokens.colors.surface }]}>
|
|
34
34
|
{(isPremium || showCredits) && (
|
|
35
35
|
<View style={styles.header}>
|
|
36
|
-
<
|
|
36
|
+
<AtomicText type="titleLarge" style={{ color: tokens.colors.textPrimary }}>
|
|
37
37
|
{translations.title}
|
|
38
|
-
</
|
|
38
|
+
</AtomicText>
|
|
39
39
|
<PremiumStatusBadge
|
|
40
40
|
status={statusType}
|
|
41
41
|
activeLabel={translations.statusActive}
|
|
42
42
|
expiredLabel={translations.statusExpired}
|
|
43
43
|
noneLabel={translations.statusFree}
|
|
44
|
+
canceledLabel={translations.statusCanceled}
|
|
44
45
|
/>
|
|
45
46
|
</View>
|
|
46
47
|
)}
|
|
@@ -48,12 +49,12 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
|
|
|
48
49
|
{!isPremium && !showCredits && (
|
|
49
50
|
<View style={styles.freeUserHeader}>
|
|
50
51
|
<View style={styles.freeUserTextContainer}>
|
|
51
|
-
<
|
|
52
|
+
<AtomicText type="headlineSmall" style={{ color: tokens.colors.textPrimary, fontWeight: "700" }}>
|
|
52
53
|
{translations.title}
|
|
53
|
-
</
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
</
|
|
54
|
+
</AtomicText>
|
|
55
|
+
<AtomicText type="bodyMedium" style={{ color: tokens.colors.textSecondary }}>
|
|
56
|
+
{translations.freeDescription}
|
|
57
|
+
</AtomicText>
|
|
57
58
|
</View>
|
|
58
59
|
</View>
|
|
59
60
|
)}
|
|
@@ -94,9 +95,9 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
|
|
|
94
95
|
style={[styles.creditsSection, { borderTopColor: tokens.colors.border }]}
|
|
95
96
|
>
|
|
96
97
|
{translations.creditsTitle && (
|
|
97
|
-
<
|
|
98
|
+
<AtomicText type="labelMedium" style={[styles.sectionTitle, { color: tokens.colors.textPrimary }]}>
|
|
98
99
|
{translations.creditsTitle}
|
|
99
|
-
</
|
|
100
|
+
</AtomicText>
|
|
100
101
|
)}
|
|
101
102
|
{credits.map((credit) => (
|
|
102
103
|
<CreditRow
|
|
@@ -119,9 +120,9 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
|
|
|
119
120
|
]}
|
|
120
121
|
onPress={onManageSubscription}
|
|
121
122
|
>
|
|
122
|
-
<
|
|
123
|
+
<AtomicText type="labelLarge" style={{ color: tokens.colors.textPrimary }}>
|
|
123
124
|
{translations.manageButton}
|
|
124
|
-
</
|
|
125
|
+
</AtomicText>
|
|
125
126
|
</TouchableOpacity>
|
|
126
127
|
)}
|
|
127
128
|
{!isPremium && onUpgrade && translations.upgradeButton && (
|
|
@@ -129,11 +130,12 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
|
|
|
129
130
|
style={[styles.premiumButton, { backgroundColor: tokens.colors.primary }]}
|
|
130
131
|
onPress={onUpgrade}
|
|
131
132
|
>
|
|
132
|
-
<
|
|
133
|
-
|
|
133
|
+
<AtomicText
|
|
134
|
+
type="titleMedium"
|
|
135
|
+
style={{ color: tokens.colors.onPrimary, fontWeight: "700" }}
|
|
134
136
|
>
|
|
135
137
|
{translations.upgradeButton}
|
|
136
|
-
</
|
|
138
|
+
</AtomicText>
|
|
137
139
|
</TouchableOpacity>
|
|
138
140
|
)}
|
|
139
141
|
</View>
|
|
@@ -152,41 +154,23 @@ const styles = StyleSheet.create({
|
|
|
152
154
|
justifyContent: "space-between",
|
|
153
155
|
alignItems: "center",
|
|
154
156
|
},
|
|
155
|
-
title: {
|
|
156
|
-
fontSize: 18,
|
|
157
|
-
fontWeight: "600",
|
|
158
|
-
},
|
|
159
157
|
freeUserHeader: {
|
|
160
158
|
marginBottom: 4,
|
|
161
159
|
},
|
|
162
160
|
freeUserTextContainer: {
|
|
163
161
|
gap: 6,
|
|
164
162
|
},
|
|
165
|
-
freeUserTitle: {
|
|
166
|
-
fontSize: 20,
|
|
167
|
-
fontWeight: "700",
|
|
168
|
-
},
|
|
169
|
-
freeUserDescription: {
|
|
170
|
-
fontSize: 15,
|
|
171
|
-
fontWeight: "400",
|
|
172
|
-
lineHeight: 20,
|
|
173
|
-
},
|
|
174
163
|
premiumButton: {
|
|
175
164
|
paddingVertical: 16,
|
|
176
165
|
borderRadius: 12,
|
|
177
166
|
alignItems: "center",
|
|
178
167
|
},
|
|
179
|
-
premiumButtonText: {
|
|
180
|
-
fontSize: 16,
|
|
181
|
-
fontWeight: "700",
|
|
182
|
-
},
|
|
183
168
|
detailsSection: {
|
|
184
169
|
gap: 8,
|
|
185
170
|
},
|
|
186
171
|
sectionTitle: {
|
|
187
|
-
fontSize: 14,
|
|
188
|
-
fontWeight: "600",
|
|
189
172
|
marginBottom: 4,
|
|
173
|
+
fontWeight: "600",
|
|
190
174
|
},
|
|
191
175
|
creditsSection: {
|
|
192
176
|
gap: 8,
|
|
@@ -196,22 +180,9 @@ const styles = StyleSheet.create({
|
|
|
196
180
|
actionsSection: {
|
|
197
181
|
gap: 8,
|
|
198
182
|
},
|
|
199
|
-
primaryButton: {
|
|
200
|
-
paddingVertical: 12,
|
|
201
|
-
borderRadius: 8,
|
|
202
|
-
alignItems: "center",
|
|
203
|
-
},
|
|
204
|
-
primaryButtonText: {
|
|
205
|
-
fontSize: 14,
|
|
206
|
-
fontWeight: "600",
|
|
207
|
-
},
|
|
208
183
|
secondaryButton: {
|
|
209
184
|
paddingVertical: 12,
|
|
210
185
|
borderRadius: 8,
|
|
211
186
|
alignItems: "center",
|
|
212
187
|
},
|
|
213
|
-
secondaryButtonText: {
|
|
214
|
-
fontSize: 14,
|
|
215
|
-
fontWeight: "500",
|
|
216
|
-
},
|
|
217
188
|
});
|
|
@@ -14,6 +14,7 @@ export interface CreditInfo {
|
|
|
14
14
|
|
|
15
15
|
export interface PremiumDetailsTranslations {
|
|
16
16
|
title: string;
|
|
17
|
+
freeDescription?: string;
|
|
17
18
|
statusLabel: string;
|
|
18
19
|
expiresLabel: string;
|
|
19
20
|
purchasedLabel: string;
|
|
@@ -22,9 +23,10 @@ export interface PremiumDetailsTranslations {
|
|
|
22
23
|
manageButton?: string;
|
|
23
24
|
upgradeButton?: string;
|
|
24
25
|
lifetimeLabel?: string;
|
|
25
|
-
statusActive
|
|
26
|
-
statusExpired
|
|
27
|
-
statusFree
|
|
26
|
+
statusActive: string;
|
|
27
|
+
statusExpired: string;
|
|
28
|
+
statusFree: string;
|
|
29
|
+
statusCanceled: string;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export interface PremiumDetailsCardProps {
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
import React from "react";
|
|
7
7
|
import { View, StyleSheet } from "react-native";
|
|
8
8
|
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
|
-
import { SubscriptionStatusType } from "
|
|
9
|
+
import { SubscriptionStatusType } from "@domain/entities/SubscriptionStatus";
|
|
10
10
|
export type { SubscriptionStatusType };
|
|
11
11
|
|
|
12
12
|
export interface PremiumStatusBadgeProps {
|
|
13
13
|
status: SubscriptionStatusType;
|
|
14
|
-
activeLabel
|
|
15
|
-
expiredLabel
|
|
16
|
-
noneLabel
|
|
17
|
-
canceledLabel
|
|
14
|
+
activeLabel: string;
|
|
15
|
+
expiredLabel: string;
|
|
16
|
+
noneLabel: string;
|
|
17
|
+
canceledLabel: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -22,10 +22,10 @@ export interface PremiumStatusBadgeProps {
|
|
|
22
22
|
*/
|
|
23
23
|
export const PremiumStatusBadge: React.FC<PremiumStatusBadgeProps> = ({
|
|
24
24
|
status,
|
|
25
|
-
activeLabel
|
|
26
|
-
expiredLabel
|
|
27
|
-
noneLabel
|
|
28
|
-
canceledLabel
|
|
25
|
+
activeLabel,
|
|
26
|
+
expiredLabel,
|
|
27
|
+
noneLabel,
|
|
28
|
+
canceledLabel,
|
|
29
29
|
}) => {
|
|
30
30
|
const tokens = useAppDesignTokens();
|
|
31
31
|
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { useMemo } from "react";
|
|
8
|
-
import type { CreditType } from "
|
|
9
|
-
import { getCreditsRepository } from "
|
|
8
|
+
import type { CreditType } from "@domain/entities/Credits";
|
|
9
|
+
import { getCreditsRepository } from "@infrastructure/repositories/CreditsRepositoryProvider";
|
|
10
10
|
import {
|
|
11
11
|
createCreditChecker,
|
|
12
12
|
type CreditCheckResult,
|
|
13
|
-
} from "
|
|
13
|
+
} from "@utils/creditChecker";
|
|
14
14
|
|
|
15
15
|
export interface UseCreditCheckerParams {
|
|
16
16
|
getCreditType: (operationType: string) => CreditType;
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { useQuery } from "@tanstack/react-query";
|
|
9
|
-
import type { UserCredits, CreditType } from "
|
|
9
|
+
import type { UserCredits, CreditType } from "@domain/entities/Credits";
|
|
10
10
|
import {
|
|
11
11
|
getCreditsRepository,
|
|
12
12
|
getCreditsConfig,
|
|
13
|
-
} from "
|
|
13
|
+
} from "@infrastructure/repositories/CreditsRepositoryProvider";
|
|
14
14
|
|
|
15
15
|
const CACHE_CONFIG = {
|
|
16
16
|
staleTime: 30 * 1000,
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import { useCallback } from "react";
|
|
9
9
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
10
|
-
import type { CreditType, UserCredits } from "
|
|
11
|
-
import { getCreditsRepository } from "
|
|
10
|
+
import type { CreditType, UserCredits } from "@domain/entities/Credits";
|
|
11
|
+
import { getCreditsRepository } from "@infrastructure/repositories/CreditsRepositoryProvider";
|
|
12
12
|
import { creditsQueryKeys } from "./useCredits";
|
|
13
13
|
|
|
14
14
|
export interface UseDeductCreditParams {
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev Test Callbacks Hook
|
|
3
|
+
* Provides test functions for subscription renewal testing
|
|
4
|
+
* Only used in __DEV__ mode
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useCallback } from "react";
|
|
8
|
+
import { Alert } from "react-native";
|
|
9
|
+
import { useAuth } from "@umituz/react-native-auth";
|
|
10
|
+
import { getCreditsRepository } from "@infrastructure/repositories/CreditsRepositoryProvider";
|
|
11
|
+
import { useCredits } from "./useCredits";
|
|
12
|
+
import type { DevTestActions } from "@presentation/screens/components/DevTestSection";
|
|
13
|
+
|
|
14
|
+
export const useDevTestCallbacks = (): DevTestActions | undefined => {
|
|
15
|
+
const { user } = useAuth();
|
|
16
|
+
const { credits, refetch } = useCredits({ userId: user?.uid });
|
|
17
|
+
|
|
18
|
+
const onTestRenewal = useCallback(async () => {
|
|
19
|
+
if (!user?.uid) {
|
|
20
|
+
Alert.alert("Error", "No user logged in");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const repository = getCreditsRepository();
|
|
26
|
+
const renewalId = `dev_renewal_${Date.now()}`;
|
|
27
|
+
const productId = "test_yearly_subscription";
|
|
28
|
+
|
|
29
|
+
if (__DEV__) {
|
|
30
|
+
console.log("🧪 [Dev Test] Simulating auto-renewal...", {
|
|
31
|
+
userId: user.uid,
|
|
32
|
+
renewalId,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const result = await repository.initializeCredits(
|
|
37
|
+
user.uid,
|
|
38
|
+
renewalId,
|
|
39
|
+
productId,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (__DEV__) {
|
|
43
|
+
console.log("✅ [Dev Test] Renewal completed:", {
|
|
44
|
+
success: result.success,
|
|
45
|
+
textCredits: result.data?.textCredits,
|
|
46
|
+
imageCredits: result.data?.imageCredits,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await refetch();
|
|
51
|
+
|
|
52
|
+
Alert.alert(
|
|
53
|
+
"✅ Test Renewal Success",
|
|
54
|
+
`Credits Updated!\n\nText: ${result.data?.textCredits || 0}\nImage: ${result.data?.imageCredits || 0}\n\n(ACCUMULATE mode - credits added to existing)`,
|
|
55
|
+
[{ text: "OK" }],
|
|
56
|
+
);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (__DEV__) {
|
|
59
|
+
console.error("❌ [Dev Test] Renewal failed:", error);
|
|
60
|
+
}
|
|
61
|
+
Alert.alert(
|
|
62
|
+
"Test Failed",
|
|
63
|
+
error instanceof Error ? error.message : "Unknown error",
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}, [user?.uid, refetch]);
|
|
67
|
+
|
|
68
|
+
const onCheckCredits = useCallback(() => {
|
|
69
|
+
if (!credits) {
|
|
70
|
+
Alert.alert("Credits", "No credits data available");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Alert.alert(
|
|
75
|
+
"📊 Current Credits",
|
|
76
|
+
`Text Generation: ${credits.textCredits}\nImage Generation: ${credits.imageCredits}\n\nPurchased: ${credits.purchasedAt?.toLocaleDateString() || "N/A"}`,
|
|
77
|
+
[{ text: "OK" }],
|
|
78
|
+
);
|
|
79
|
+
}, [credits]);
|
|
80
|
+
|
|
81
|
+
const onTestDuplicate = useCallback(async () => {
|
|
82
|
+
if (!user?.uid) {
|
|
83
|
+
Alert.alert("Error", "No user logged in");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const repository = getCreditsRepository();
|
|
89
|
+
const sameRenewalId = "dev_duplicate_test_12345";
|
|
90
|
+
|
|
91
|
+
if (__DEV__) {
|
|
92
|
+
console.log("🧪 [Dev Test] Testing duplicate protection...");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const result1 = await repository.initializeCredits(
|
|
96
|
+
user.uid,
|
|
97
|
+
sameRenewalId,
|
|
98
|
+
"test_product",
|
|
99
|
+
);
|
|
100
|
+
if (__DEV__) {
|
|
101
|
+
console.log("First call:", result1.data);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const result2 = await repository.initializeCredits(
|
|
105
|
+
user.uid,
|
|
106
|
+
sameRenewalId,
|
|
107
|
+
"test_product",
|
|
108
|
+
);
|
|
109
|
+
if (__DEV__) {
|
|
110
|
+
console.log("Second call:", result2.data);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
await refetch();
|
|
114
|
+
|
|
115
|
+
const duplicateProtectionWorks =
|
|
116
|
+
result2.data?.textCredits === result1.data?.textCredits;
|
|
117
|
+
|
|
118
|
+
Alert.alert(
|
|
119
|
+
"Duplicate Test",
|
|
120
|
+
`First call: ${result1.success ? "✅ Added credits" : "❌ Failed"}\n\nSecond call: ${duplicateProtectionWorks ? "✅ Skipped (protection works!)" : "❌ Added again (protection failed!)"}`,
|
|
121
|
+
[{ text: "OK" }],
|
|
122
|
+
);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (__DEV__) {
|
|
125
|
+
console.error("❌ [Dev Test] Duplicate test failed:", error);
|
|
126
|
+
}
|
|
127
|
+
Alert.alert(
|
|
128
|
+
"Test Failed",
|
|
129
|
+
error instanceof Error ? error.message : "Unknown error",
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}, [user?.uid, refetch]);
|
|
133
|
+
|
|
134
|
+
if (!__DEV__) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
onTestRenewal,
|
|
140
|
+
onCheckCredits,
|
|
141
|
+
onTestDuplicate,
|
|
142
|
+
};
|
|
143
|
+
};
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
|
|
12
12
|
import { useCallback } from "react";
|
|
13
13
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
14
|
-
import type { UserCredits } from "
|
|
14
|
+
import type { UserCredits } from "@domain/entities/Credits";
|
|
15
15
|
import { useCredits } from "./useCredits";
|
|
16
16
|
import {
|
|
17
17
|
useSubscriptionPackages,
|
|
18
18
|
usePurchasePackage,
|
|
19
19
|
useRestorePurchase,
|
|
20
|
-
} from "
|
|
20
|
+
} from "@revenuecat/presentation/hooks/useSubscriptionQueries";
|
|
21
21
|
import { usePaywallVisibility } from "./usePaywallVisibility";
|
|
22
22
|
|
|
23
23
|
export interface UsePremiumWithConfigParams {
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { useState, useCallback } from 'react';
|
|
7
|
-
import { getSubscriptionService } from '
|
|
8
|
-
import type { SubscriptionStatus } from '
|
|
9
|
-
import { isSubscriptionValid } from '
|
|
7
|
+
import { getSubscriptionService } from '@infrastructure/services/SubscriptionService';
|
|
8
|
+
import type { SubscriptionStatus } from '@domain/entities/SubscriptionStatus';
|
|
9
|
+
import { isSubscriptionValid } from '@domain/entities/SubscriptionStatus';
|
|
10
10
|
|
|
11
11
|
export interface UseSubscriptionResult {
|
|
12
12
|
/** Current subscription status */
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { useMemo } from "react";
|
|
7
|
-
import type { SubscriptionStatus } from "
|
|
7
|
+
import type { SubscriptionStatus } from "@domain/entities/SubscriptionStatus";
|
|
8
8
|
import {
|
|
9
9
|
getDaysUntilExpiration,
|
|
10
10
|
isSubscriptionExpired,
|
|
11
|
-
} from "
|
|
11
|
+
} from "@utils/dateValidationUtils";
|
|
12
12
|
|
|
13
13
|
export interface SubscriptionDetails {
|
|
14
14
|
/** Raw subscription status */
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
import { useMemo } from 'react';
|
|
30
|
-
import { getUserTierInfo } from '
|
|
31
|
-
import type { UserTierInfo } from '
|
|
30
|
+
import { getUserTierInfo } from '@utils/tierUtils';
|
|
31
|
+
import type { UserTierInfo } from '@utils/types';
|
|
32
32
|
|
|
33
33
|
export interface UseUserTierParams {
|
|
34
34
|
/** Whether user is a guest */
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import { useEffect, useState, useCallback } from 'react';
|
|
27
27
|
import { useUserTier, type UseUserTierParams } from './useUserTier';
|
|
28
|
-
import type { ISubscriptionRepository } from '
|
|
28
|
+
import type { ISubscriptionRepository } from '@application/ports/ISubscriptionRepository';
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Auth provider interface
|
|
@@ -16,18 +16,19 @@ import type { CreditInfo } from "../components/details/PremiumDetailsCard";
|
|
|
16
16
|
|
|
17
17
|
export interface SubscriptionDetailTranslations {
|
|
18
18
|
title: string;
|
|
19
|
-
statusLabel
|
|
20
|
-
statusActive
|
|
21
|
-
statusExpired
|
|
22
|
-
statusFree
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
statusLabel: string;
|
|
20
|
+
statusActive: string;
|
|
21
|
+
statusExpired: string;
|
|
22
|
+
statusFree: string;
|
|
23
|
+
statusCanceled: string;
|
|
24
|
+
expiresLabel: string;
|
|
25
|
+
purchasedLabel: string;
|
|
26
|
+
lifetimeLabel: string;
|
|
27
|
+
creditsTitle: string;
|
|
28
|
+
remainingLabel: string;
|
|
28
29
|
usageTitle?: string;
|
|
29
|
-
manageButton
|
|
30
|
-
upgradeButton
|
|
30
|
+
manageButton: string;
|
|
31
|
+
upgradeButton: string;
|
|
31
32
|
creditsResetInfo?: string;
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { View,
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
7
|
+
import { View, StyleSheet } from "react-native";
|
|
8
|
+
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
9
|
|
|
10
10
|
interface CreditItemProps {
|
|
11
11
|
label: string;
|
|
@@ -34,13 +34,13 @@ export const CreditItem: React.FC<CreditItemProps> = ({
|
|
|
34
34
|
return (
|
|
35
35
|
<View style={styles.container}>
|
|
36
36
|
<View style={styles.header}>
|
|
37
|
-
<
|
|
37
|
+
<AtomicText type="bodyMedium" style={[styles.label, { color: tokens.colors.textPrimary }]}>
|
|
38
38
|
{label}
|
|
39
|
-
</
|
|
39
|
+
</AtomicText>
|
|
40
40
|
<View style={[styles.badge, { backgroundColor: tokens.colors.surfaceSecondary }]}>
|
|
41
|
-
<
|
|
41
|
+
<AtomicText type="labelSmall" style={[styles.count, { color: getColor() }]}>
|
|
42
42
|
{current} / {total}
|
|
43
|
-
</
|
|
43
|
+
</AtomicText>
|
|
44
44
|
</View>
|
|
45
45
|
</View>
|
|
46
46
|
<View
|
|
@@ -59,9 +59,9 @@ export const CreditItem: React.FC<CreditItemProps> = ({
|
|
|
59
59
|
]}
|
|
60
60
|
/>
|
|
61
61
|
</View>
|
|
62
|
-
<
|
|
62
|
+
<AtomicText type="bodySmall" style={[styles.remaining, { color: tokens.colors.textSecondary }]}>
|
|
63
63
|
{current} {remainingLabel}
|
|
64
|
-
</
|
|
64
|
+
</AtomicText>
|
|
65
65
|
</View>
|
|
66
66
|
);
|
|
67
67
|
};
|
|
@@ -76,7 +76,6 @@ const styles = StyleSheet.create({
|
|
|
76
76
|
alignItems: "center",
|
|
77
77
|
},
|
|
78
78
|
label: {
|
|
79
|
-
fontSize: 15,
|
|
80
79
|
fontWeight: "500",
|
|
81
80
|
},
|
|
82
81
|
badge: {
|
|
@@ -85,7 +84,6 @@ const styles = StyleSheet.create({
|
|
|
85
84
|
borderRadius: 12,
|
|
86
85
|
},
|
|
87
86
|
count: {
|
|
88
|
-
fontSize: 13,
|
|
89
87
|
fontWeight: "600",
|
|
90
88
|
},
|
|
91
89
|
progressBar: {
|
|
@@ -98,6 +96,5 @@ const styles = StyleSheet.create({
|
|
|
98
96
|
borderRadius: 4,
|
|
99
97
|
},
|
|
100
98
|
remaining: {
|
|
101
|
-
fontSize: 12,
|
|
102
99
|
},
|
|
103
100
|
});
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { View,
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
7
|
+
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
|
8
|
+
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
9
|
|
|
10
10
|
interface SubscriptionActionsProps {
|
|
11
11
|
isPremium: boolean;
|
|
@@ -34,14 +34,12 @@ export const SubscriptionActions: React.FC<SubscriptionActionsProps> = ({
|
|
|
34
34
|
]}
|
|
35
35
|
onPress={onManage}
|
|
36
36
|
>
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{ color: tokens.colors.textPrimary },
|
|
41
|
-
]}
|
|
37
|
+
<AtomicText
|
|
38
|
+
type="titleMedium"
|
|
39
|
+
style={{ color: tokens.colors.textPrimary, fontWeight: "600" }}
|
|
42
40
|
>
|
|
43
41
|
{manageButtonLabel}
|
|
44
|
-
</
|
|
42
|
+
</AtomicText>
|
|
45
43
|
</TouchableOpacity>
|
|
46
44
|
)}
|
|
47
45
|
{!isPremium && onUpgrade && upgradeButtonLabel && (
|
|
@@ -49,14 +47,12 @@ export const SubscriptionActions: React.FC<SubscriptionActionsProps> = ({
|
|
|
49
47
|
style={[styles.primaryButton, { backgroundColor: tokens.colors.primary }]}
|
|
50
48
|
onPress={onUpgrade}
|
|
51
49
|
>
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
{ color: tokens.colors.onPrimary },
|
|
56
|
-
]}
|
|
50
|
+
<AtomicText
|
|
51
|
+
type="titleMedium"
|
|
52
|
+
style={{ color: tokens.colors.onPrimary, fontWeight: "700" }}
|
|
57
53
|
>
|
|
58
54
|
{upgradeButtonLabel}
|
|
59
|
-
</
|
|
55
|
+
</AtomicText>
|
|
60
56
|
</TouchableOpacity>
|
|
61
57
|
)}
|
|
62
58
|
</View>
|
|
@@ -73,17 +69,9 @@ const styles = StyleSheet.create({
|
|
|
73
69
|
borderRadius: 12,
|
|
74
70
|
alignItems: "center",
|
|
75
71
|
},
|
|
76
|
-
primaryButtonText: {
|
|
77
|
-
fontSize: 16,
|
|
78
|
-
fontWeight: "700",
|
|
79
|
-
},
|
|
80
72
|
secondaryButton: {
|
|
81
73
|
paddingVertical: 16,
|
|
82
74
|
borderRadius: 12,
|
|
83
75
|
alignItems: "center",
|
|
84
76
|
},
|
|
85
|
-
secondaryButtonText: {
|
|
86
|
-
fontSize: 16,
|
|
87
|
-
fontWeight: "600",
|
|
88
|
-
},
|
|
89
77
|
});
|