@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.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, index) => (
73
- <SubscriptionPlanCard
74
- key={pkg.product.identifier}
75
- package={pkg}
76
- isSelected={selectedPkg?.product.identifier === pkg.product.identifier}
77
- onSelect={() => onSelect(pkg)}
78
- isBestValue={index === 0}
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 = isYearlyPeriod(period);
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
+ };