@umituz/react-native-subscription 2.10.10 → 2.10.12
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.12",
|
|
4
4
|
"description": "Complete subscription management with RevenueCat, paywall UI, and credits system for React Native apps",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -4,6 +4,7 @@ import { AtomicText } from "@umituz/react-native-design-system";
|
|
|
4
4
|
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
5
5
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
6
6
|
import { SubscriptionPlanCard } from "./SubscriptionPlanCard";
|
|
7
|
+
import { isYearlyPackage } from "../../../utils/packagePeriodUtils";
|
|
7
8
|
|
|
8
9
|
interface SubscriptionPackageListProps {
|
|
9
10
|
isLoading: boolean;
|
|
@@ -12,6 +13,10 @@ interface SubscriptionPackageListProps {
|
|
|
12
13
|
loadingText: string;
|
|
13
14
|
emptyText: string;
|
|
14
15
|
onSelect: (pkg: PurchasesPackage) => void;
|
|
16
|
+
/** Optional: Manually specify which package should show "Best Value" badge by identifier */
|
|
17
|
+
bestValueIdentifier?: string;
|
|
18
|
+
/** Optional: Map of product identifier to credit amount (e.g., { "weekly": 6, "monthly": 25, "yearly": 300 }) */
|
|
19
|
+
creditAmounts?: Record<string, number>;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
export const SubscriptionPackageList: React.FC<SubscriptionPackageListProps> = React.memo(
|
|
@@ -22,6 +27,8 @@ export const SubscriptionPackageList: React.FC<SubscriptionPackageListProps> = R
|
|
|
22
27
|
loadingText,
|
|
23
28
|
emptyText,
|
|
24
29
|
onSelect,
|
|
30
|
+
bestValueIdentifier,
|
|
31
|
+
creditAmounts,
|
|
25
32
|
}) => {
|
|
26
33
|
const tokens = useAppDesignTokens();
|
|
27
34
|
const hasPackages = packages.length > 0;
|
|
@@ -69,15 +76,32 @@ export const SubscriptionPackageList: React.FC<SubscriptionPackageListProps> = R
|
|
|
69
76
|
|
|
70
77
|
return (
|
|
71
78
|
<View style={styles.packagesContainer}>
|
|
72
|
-
{packages.map((pkg
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
isBestValue=
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
{packages.map((pkg) => {
|
|
80
|
+
// Determine if this package should show "Best Value" badge
|
|
81
|
+
let isBestValue = false;
|
|
82
|
+
|
|
83
|
+
if (bestValueIdentifier) {
|
|
84
|
+
// Use manual override if provided
|
|
85
|
+
isBestValue = pkg.product.identifier === bestValueIdentifier;
|
|
86
|
+
} else {
|
|
87
|
+
// Auto-detect: mark yearly packages as best value
|
|
88
|
+
isBestValue = isYearlyPackage(pkg);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Get credit amount for this package if provided
|
|
92
|
+
const creditAmount = creditAmounts?.[pkg.product.identifier];
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<SubscriptionPlanCard
|
|
96
|
+
key={pkg.product.identifier}
|
|
97
|
+
package={pkg}
|
|
98
|
+
isSelected={selectedPkg?.product.identifier === pkg.product.identifier}
|
|
99
|
+
onSelect={() => onSelect(pkg)}
|
|
100
|
+
isBestValue={isBestValue}
|
|
101
|
+
creditAmount={creditAmount}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
})}
|
|
81
105
|
</View>
|
|
82
106
|
);
|
|
83
107
|
}
|
|
@@ -12,6 +12,7 @@ import { formatPrice } from "../../../utils/priceUtils";
|
|
|
12
12
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
13
13
|
import { BestValueBadge } from "./BestValueBadge";
|
|
14
14
|
|
|
15
|
+
import { getPeriodLabel, isYearlyPackage } from "../../../utils/packagePeriodUtils";
|
|
15
16
|
// @ts-ignore
|
|
16
17
|
import { LinearGradient } from "expo-linear-gradient";
|
|
17
18
|
|
|
@@ -20,28 +21,17 @@ interface SubscriptionPlanCardProps {
|
|
|
20
21
|
isSelected: boolean;
|
|
21
22
|
onSelect: () => void;
|
|
22
23
|
isBestValue?: boolean;
|
|
24
|
+
/** Optional: Number of credits/generations included with this package */
|
|
25
|
+
creditAmount?: number;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
const getPeriodLabel = (period: string | null | undefined): string => {
|
|
26
|
-
if (!period) return "";
|
|
27
|
-
if (period.includes("Y") || period.includes("year")) return "yearly";
|
|
28
|
-
if (period.includes("M") || period.includes("month")) return "monthly";
|
|
29
|
-
if (period.includes("W") || period.includes("week")) return "weekly";
|
|
30
|
-
if (period.includes("D") || period.includes("day")) return "daily";
|
|
31
|
-
return "";
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const isYearlyPeriod = (period: string | null | undefined): boolean => {
|
|
35
|
-
return period?.includes("Y") || period?.includes("year") || false;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
28
|
export const SubscriptionPlanCard: React.FC<SubscriptionPlanCardProps> =
|
|
39
|
-
React.memo(({ package: pkg, isSelected, onSelect, isBestValue = false }) => {
|
|
29
|
+
React.memo(({ package: pkg, isSelected, onSelect, isBestValue = false, creditAmount }) => {
|
|
40
30
|
const tokens = useAppDesignTokens();
|
|
41
31
|
const { t } = useLocalization();
|
|
42
32
|
|
|
43
33
|
const period = pkg.product.subscriptionPeriod;
|
|
44
|
-
const isYearly =
|
|
34
|
+
const isYearly = isYearlyPackage(pkg);
|
|
45
35
|
const periodLabel = getPeriodLabel(period);
|
|
46
36
|
const price = formatPrice(pkg.product.price, pkg.product.currencyCode);
|
|
47
37
|
const monthlyEquivalent = isYearly
|
|
@@ -117,6 +107,25 @@ export const SubscriptionPlanCard: React.FC<SubscriptionPlanCardProps> =
|
|
|
117
107
|
</View>
|
|
118
108
|
|
|
119
109
|
<View style={styles.rightSection}>
|
|
110
|
+
{creditAmount && (
|
|
111
|
+
<View
|
|
112
|
+
style={[
|
|
113
|
+
styles.creditBadge,
|
|
114
|
+
{ backgroundColor: tokens.colors.primary + "15" },
|
|
115
|
+
]}
|
|
116
|
+
>
|
|
117
|
+
<AtomicText
|
|
118
|
+
type="labelSmall"
|
|
119
|
+
style={{
|
|
120
|
+
color: tokens.colors.primary,
|
|
121
|
+
fontWeight: "700",
|
|
122
|
+
fontSize: 11,
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
{creditAmount} {t("paywall.credits")}
|
|
126
|
+
</AtomicText>
|
|
127
|
+
</View>
|
|
128
|
+
)}
|
|
120
129
|
<AtomicText
|
|
121
130
|
type="titleMedium"
|
|
122
131
|
style={[styles.price, { color: tokens.colors.textPrimary }]}
|
|
@@ -176,6 +185,12 @@ const styles = StyleSheet.create({
|
|
|
176
185
|
fontWeight: "600",
|
|
177
186
|
marginBottom: 2,
|
|
178
187
|
},
|
|
188
|
+
creditBadge: {
|
|
189
|
+
paddingHorizontal: 10,
|
|
190
|
+
paddingVertical: 4,
|
|
191
|
+
borderRadius: 12,
|
|
192
|
+
marginBottom: 4,
|
|
193
|
+
},
|
|
179
194
|
rightSection: {
|
|
180
195
|
alignItems: "flex-end",
|
|
181
196
|
},
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package Period Utilities
|
|
3
|
+
* Helper functions for working with subscription periods
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PurchasesPackage } from "react-native-purchases";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get period label from subscription period string
|
|
10
|
+
*/
|
|
11
|
+
export const getPeriodLabel = (period: string | null | undefined): string => {
|
|
12
|
+
if (!period) return "";
|
|
13
|
+
if (period.includes("Y") || period.includes("year")) return "yearly";
|
|
14
|
+
if (period.includes("M") || period.includes("month")) return "monthly";
|
|
15
|
+
if (period.includes("W") || period.includes("week")) return "weekly";
|
|
16
|
+
if (period.includes("D") || period.includes("day")) return "daily";
|
|
17
|
+
return "";
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if a package has a yearly subscription period
|
|
22
|
+
*/
|
|
23
|
+
export const isYearlyPackage = (pkg: PurchasesPackage): boolean => {
|
|
24
|
+
const period = pkg.product.subscriptionPeriod;
|
|
25
|
+
return period?.includes("Y") || period?.includes("year") || false;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if a package has a monthly subscription period
|
|
30
|
+
*/
|
|
31
|
+
export const isMonthlyPackage = (pkg: PurchasesPackage): boolean => {
|
|
32
|
+
const period = pkg.product.subscriptionPeriod;
|
|
33
|
+
return period?.includes("M") || period?.includes("month") || false;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if a package has a weekly subscription period
|
|
38
|
+
*/
|
|
39
|
+
export const isWeeklyPackage = (pkg: PurchasesPackage): boolean => {
|
|
40
|
+
const period = pkg.product.subscriptionPeriod;
|
|
41
|
+
return period?.includes("W") || period?.includes("week") || false;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Find the first yearly package in an array of packages
|
|
46
|
+
*/
|
|
47
|
+
export const findYearlyPackage = (
|
|
48
|
+
packages: PurchasesPackage[]
|
|
49
|
+
): PurchasesPackage | undefined => {
|
|
50
|
+
return packages.find(isYearlyPackage);
|
|
51
|
+
};
|