@umituz/react-native-subscription 2.11.6 → 2.11.8

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 (25) hide show
  1. package/package.json +1 -1
  2. package/src/presentation/components/details/CreditRow.tsx +83 -0
  3. package/src/presentation/components/details/DetailRow.tsx +53 -0
  4. package/src/presentation/components/details/PremiumDetailsCard.tsx +5 -155
  5. package/src/presentation/components/details/PremiumDetailsCardTypes.ts +41 -0
  6. package/src/presentation/components/paywall/BestValueBadge.tsx +0 -1
  7. package/src/presentation/components/paywall/PaywallFeaturesList.tsx +0 -3
  8. package/src/presentation/components/paywall/PaywallLegalFooter.tsx +5 -62
  9. package/src/presentation/components/paywall/PaywallLegalFooterStyles.ts +53 -0
  10. package/src/presentation/components/paywall/PaywallLegalFooterTypes.ts +19 -0
  11. package/src/presentation/components/paywall/PaywallModal.tsx +1 -1
  12. package/src/presentation/components/paywall/SubscriptionFooter.tsx +2 -6
  13. package/src/presentation/components/paywall/SubscriptionModal.tsx +0 -10
  14. package/src/presentation/components/paywall/SubscriptionPlanCard.tsx +5 -68
  15. package/src/presentation/components/paywall/SubscriptionPlanCardStyles.ts +61 -0
  16. package/src/presentation/components/paywall/SubscriptionPlanCardTypes.ts +15 -0
  17. package/src/presentation/hooks/useDeductCredit.ts +5 -4
  18. package/src/presentation/hooks/usePremiumWithConfig.ts +2 -1
  19. package/src/presentation/hooks/useSubscriptionModal.ts +2 -2
  20. package/src/revenuecat/presentation/hooks/subscriptionQueryKeys.ts +16 -0
  21. package/src/revenuecat/presentation/hooks/useInitializeSubscription.ts +56 -0
  22. package/src/revenuecat/presentation/hooks/usePurchasePackage.ts +79 -0
  23. package/src/revenuecat/presentation/hooks/useRestorePurchase.ts +68 -0
  24. package/src/revenuecat/presentation/hooks/useSubscriptionPackages.ts +44 -0
  25. package/src/revenuecat/presentation/hooks/useSubscriptionQueries.ts +9 -216
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "2.11.6",
3
+ "version": "2.11.8",
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",
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Credit Row Component
3
+ * Displays credit information with progress bar
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, Text, StyleSheet } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
9
+
10
+ export interface CreditRowProps {
11
+ label: string;
12
+ current: number;
13
+ total: number;
14
+ remainingLabel?: string;
15
+ }
16
+
17
+ export const CreditRow: React.FC<CreditRowProps> = ({
18
+ label,
19
+ current,
20
+ total,
21
+ remainingLabel = "remaining",
22
+ }) => {
23
+ const tokens = useAppDesignTokens();
24
+ const percentage = total > 0 ? (current / total) * 100 : 0;
25
+ const isLow = percentage <= 20;
26
+
27
+ return (
28
+ <View style={styles.container}>
29
+ <View style={styles.header}>
30
+ <Text style={[styles.label, { color: tokens.colors.text }]}>
31
+ {label}
32
+ </Text>
33
+ <Text
34
+ style={[
35
+ styles.count,
36
+ { color: isLow ? tokens.colors.warning : tokens.colors.textSecondary },
37
+ ]}
38
+ >
39
+ {current} / {total} {remainingLabel}
40
+ </Text>
41
+ </View>
42
+ <View
43
+ style={[styles.progressBar, { backgroundColor: tokens.colors.surfaceSecondary }]}
44
+ >
45
+ <View
46
+ style={[
47
+ styles.progressFill,
48
+ {
49
+ width: `${percentage}%`,
50
+ backgroundColor: isLow ? tokens.colors.warning : tokens.colors.primary,
51
+ },
52
+ ]}
53
+ />
54
+ </View>
55
+ </View>
56
+ );
57
+ };
58
+
59
+ const styles = StyleSheet.create({
60
+ container: {
61
+ gap: 4,
62
+ },
63
+ header: {
64
+ flexDirection: "row",
65
+ justifyContent: "space-between",
66
+ alignItems: "center",
67
+ },
68
+ label: {
69
+ fontSize: 13,
70
+ },
71
+ count: {
72
+ fontSize: 12,
73
+ },
74
+ progressBar: {
75
+ height: 6,
76
+ borderRadius: 3,
77
+ overflow: "hidden",
78
+ },
79
+ progressFill: {
80
+ height: "100%",
81
+ borderRadius: 3,
82
+ },
83
+ });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Detail Row Component
3
+ * Displays a single detail row with label and value
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, Text, StyleSheet } from "react-native";
8
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
9
+
10
+ export interface DetailRowProps {
11
+ label: string;
12
+ value: string;
13
+ highlight?: boolean;
14
+ }
15
+
16
+ export const DetailRow: React.FC<DetailRowProps> = ({
17
+ label,
18
+ value,
19
+ highlight = false,
20
+ }) => {
21
+ const tokens = useAppDesignTokens();
22
+
23
+ return (
24
+ <View style={styles.container}>
25
+ <Text style={[styles.label, { color: tokens.colors.textSecondary }]}>
26
+ {label}
27
+ </Text>
28
+ <Text
29
+ style={[
30
+ styles.value,
31
+ { color: highlight ? tokens.colors.warning : tokens.colors.text },
32
+ ]}
33
+ >
34
+ {value}
35
+ </Text>
36
+ </View>
37
+ );
38
+ };
39
+
40
+ const styles = StyleSheet.create({
41
+ container: {
42
+ flexDirection: "row",
43
+ justifyContent: "space-between",
44
+ alignItems: "center",
45
+ },
46
+ label: {
47
+ fontSize: 14,
48
+ },
49
+ value: {
50
+ fontSize: 14,
51
+ fontWeight: "500",
52
+ },
53
+ });
@@ -7,45 +7,12 @@
7
7
  import React from "react";
8
8
  import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
9
9
  import { useAppDesignTokens } from "@umituz/react-native-design-system";
10
- import {
11
- PremiumStatusBadge,
12
- type SubscriptionStatusType,
13
- } from "./PremiumStatusBadge";
10
+ import { PremiumStatusBadge } from "./PremiumStatusBadge";
11
+ import { DetailRow } from "./DetailRow";
12
+ import { CreditRow } from "./CreditRow";
13
+ import type { PremiumDetailsCardProps } from "./PremiumDetailsCardTypes";
14
14
 
15
- export interface CreditInfo {
16
- id: string;
17
- label: string;
18
- current: number;
19
- total: number;
20
- }
21
-
22
- export interface PremiumDetailsTranslations {
23
- title: string;
24
- statusLabel: string;
25
- expiresLabel: string;
26
- purchasedLabel: string;
27
- creditsTitle?: string;
28
- remainingLabel?: string;
29
- manageButton?: string;
30
- upgradeButton?: string;
31
- lifetimeLabel?: string;
32
- statusActive?: string;
33
- statusExpired?: string;
34
- statusFree?: string;
35
- }
36
-
37
- export interface PremiumDetailsCardProps {
38
- statusType: SubscriptionStatusType;
39
- isPremium: boolean;
40
- expirationDate?: string | null;
41
- purchaseDate?: string | null;
42
- isLifetime?: boolean;
43
- daysRemaining?: number | null;
44
- credits?: CreditInfo[];
45
- translations: PremiumDetailsTranslations;
46
- onManageSubscription?: () => void;
47
- onUpgrade?: () => void;
48
- }
15
+ export type { CreditInfo, PremiumDetailsTranslations, PremiumDetailsCardProps } from "./PremiumDetailsCardTypes";
49
16
 
50
17
  export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
51
18
  statusType,
@@ -82,7 +49,6 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
82
49
  <DetailRow
83
50
  label={translations.statusLabel}
84
51
  value={translations.lifetimeLabel || "Lifetime"}
85
- tokens={tokens}
86
52
  />
87
53
  ) : (
88
54
  <>
@@ -95,14 +61,12 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
95
61
  daysRemaining !== undefined &&
96
62
  daysRemaining <= 7
97
63
  }
98
- tokens={tokens}
99
64
  />
100
65
  )}
101
66
  {purchaseDate && (
102
67
  <DetailRow
103
68
  label={translations.purchasedLabel}
104
69
  value={purchaseDate}
105
- tokens={tokens}
106
70
  />
107
71
  )}
108
72
  </>
@@ -126,7 +90,6 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
126
90
  current={credit.current}
127
91
  total={credit.total}
128
92
  remainingLabel={translations.remainingLabel}
129
- tokens={tokens}
130
93
  />
131
94
  ))}
132
95
  </View>
@@ -163,84 +126,6 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = ({
163
126
  );
164
127
  };
165
128
 
166
- interface DetailRowProps {
167
- label: string;
168
- value: string;
169
- highlight?: boolean;
170
- tokens: ReturnType<typeof useAppDesignTokens>;
171
- }
172
-
173
- const DetailRow: React.FC<DetailRowProps> = ({
174
- label,
175
- value,
176
- highlight = false,
177
- tokens,
178
- }) => (
179
- <View style={styles.detailRow}>
180
- <Text style={[styles.detailLabel, { color: tokens.colors.textSecondary }]}>
181
- {label}
182
- </Text>
183
- <Text
184
- style={[
185
- styles.detailValue,
186
- { color: highlight ? tokens.colors.warning : tokens.colors.text },
187
- ]}
188
- >
189
- {value}
190
- </Text>
191
- </View>
192
- );
193
-
194
- interface CreditRowProps {
195
- label: string;
196
- current: number;
197
- total: number;
198
- remainingLabel?: string;
199
- tokens: ReturnType<typeof useAppDesignTokens>;
200
- }
201
-
202
- const CreditRow: React.FC<CreditRowProps> = ({
203
- label,
204
- current,
205
- total,
206
- remainingLabel = "remaining",
207
- tokens,
208
- }) => {
209
- const percentage = total > 0 ? (current / total) * 100 : 0;
210
- const isLow = percentage <= 20;
211
-
212
- return (
213
- <View style={styles.creditRow}>
214
- <View style={styles.creditHeader}>
215
- <Text style={[styles.creditLabel, { color: tokens.colors.text }]}>
216
- {label}
217
- </Text>
218
- <Text
219
- style={[
220
- styles.creditCount,
221
- { color: isLow ? tokens.colors.warning : tokens.colors.textSecondary },
222
- ]}
223
- >
224
- {current} / {total} {remainingLabel}
225
- </Text>
226
- </View>
227
- <View
228
- style={[styles.progressBar, { backgroundColor: tokens.colors.surfaceSecondary }]}
229
- >
230
- <View
231
- style={[
232
- styles.progressFill,
233
- {
234
- width: `${percentage}%`,
235
- backgroundColor: isLow ? tokens.colors.warning : tokens.colors.primary,
236
- },
237
- ]}
238
- />
239
- </View>
240
- </View>
241
- );
242
- };
243
-
244
129
  const styles = StyleSheet.create({
245
130
  card: {
246
131
  borderRadius: 12,
@@ -264,46 +149,11 @@ const styles = StyleSheet.create({
264
149
  fontWeight: "600",
265
150
  marginBottom: 4,
266
151
  },
267
- detailRow: {
268
- flexDirection: "row",
269
- justifyContent: "space-between",
270
- alignItems: "center",
271
- },
272
- detailLabel: {
273
- fontSize: 14,
274
- },
275
- detailValue: {
276
- fontSize: 14,
277
- fontWeight: "500",
278
- },
279
152
  creditsSection: {
280
153
  gap: 8,
281
154
  paddingTop: 12,
282
155
  borderTopWidth: 1,
283
156
  },
284
- creditRow: {
285
- gap: 4,
286
- },
287
- creditHeader: {
288
- flexDirection: "row",
289
- justifyContent: "space-between",
290
- alignItems: "center",
291
- },
292
- creditLabel: {
293
- fontSize: 13,
294
- },
295
- creditCount: {
296
- fontSize: 12,
297
- },
298
- progressBar: {
299
- height: 6,
300
- borderRadius: 3,
301
- overflow: "hidden",
302
- },
303
- progressFill: {
304
- height: "100%",
305
- borderRadius: 3,
306
- },
307
157
  actionsSection: {
308
158
  gap: 8,
309
159
  },
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Premium Details Card Types
3
+ * Type definitions for premium subscription details display
4
+ */
5
+
6
+ import type { SubscriptionStatusType } from "./PremiumStatusBadge";
7
+
8
+ export interface CreditInfo {
9
+ id: string;
10
+ label: string;
11
+ current: number;
12
+ total: number;
13
+ }
14
+
15
+ export interface PremiumDetailsTranslations {
16
+ title: string;
17
+ statusLabel: string;
18
+ expiresLabel: string;
19
+ purchasedLabel: string;
20
+ creditsTitle?: string;
21
+ remainingLabel?: string;
22
+ manageButton?: string;
23
+ upgradeButton?: string;
24
+ lifetimeLabel?: string;
25
+ statusActive?: string;
26
+ statusExpired?: string;
27
+ statusFree?: string;
28
+ }
29
+
30
+ export interface PremiumDetailsCardProps {
31
+ statusType: SubscriptionStatusType;
32
+ isPremium: boolean;
33
+ expirationDate?: string | null;
34
+ purchaseDate?: string | null;
35
+ isLifetime?: boolean;
36
+ daysRemaining?: number | null;
37
+ credits?: CreditInfo[];
38
+ translations: PremiumDetailsTranslations;
39
+ onManageSubscription?: () => void;
40
+ onUpgrade?: () => void;
41
+ }
@@ -1,7 +1,6 @@
1
1
  import React from "react";
2
2
  import { StyleSheet } from "react-native";
3
3
  import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
4
- // @ts-ignore
5
4
  import { LinearGradient } from "expo-linear-gradient";
6
5
 
7
6
  interface BestValueBadgeProps {
@@ -18,9 +18,6 @@ interface PaywallFeaturesListProps {
18
18
 
19
19
  export const PaywallFeaturesList: React.FC<PaywallFeaturesListProps> = React.memo(
20
20
  ({ features, containerStyle, gap = 12 }) => {
21
- if (__DEV__) {
22
- console.log("[PaywallFeaturesList] Rendering features count:", features.length);
23
- }
24
21
  return (
25
22
  <View style={[styles.container, containerStyle]}>
26
23
  {features.map((feature, index) => (
@@ -4,24 +4,14 @@
4
4
  */
5
5
 
6
6
  import React from "react";
7
- import { View, StyleSheet, TouchableOpacity, Linking } from "react-native";
7
+ import { View, TouchableOpacity, Linking } from "react-native";
8
8
  import { AtomicText } from "@umituz/react-native-design-system";
9
9
  import { useAppDesignTokens } from "@umituz/react-native-design-system";
10
+ import type { PaywallLegalFooterProps } from "./PaywallLegalFooterTypes";
11
+ import { DEFAULT_TERMS } from "./PaywallLegalFooterTypes";
12
+ import { styles } from "./PaywallLegalFooterStyles";
10
13
 
11
- interface PaywallLegalFooterProps {
12
- termsText?: string;
13
- privacyUrl?: string;
14
- termsUrl?: string;
15
- privacyText?: string;
16
- termsOfServiceText?: string;
17
- showRestoreButton?: boolean;
18
- restoreButtonText?: string;
19
- onRestore?: () => void;
20
- isProcessing?: boolean;
21
- }
22
-
23
- const DEFAULT_TERMS =
24
- "Payment will be charged to your account. Subscription automatically renews unless cancelled.";
14
+ export type { PaywallLegalFooterProps } from "./PaywallLegalFooterTypes";
25
15
 
26
16
  export const PaywallLegalFooter: React.FC<PaywallLegalFooterProps> = React.memo(
27
17
  ({
@@ -153,50 +143,3 @@ export const PaywallLegalFooter: React.FC<PaywallLegalFooterProps> = React.memo(
153
143
  );
154
144
 
155
145
  PaywallLegalFooter.displayName = "PaywallLegalFooter";
156
-
157
- const styles = StyleSheet.create({
158
- container: {
159
- alignItems: "center",
160
- paddingHorizontal: 24,
161
- paddingBottom: 24,
162
- paddingTop: 8,
163
- width: "100%",
164
- },
165
- termsText: {
166
- textAlign: "center",
167
- fontSize: 10,
168
- lineHeight: 14,
169
- marginBottom: 16,
170
- opacity: 0.7,
171
- },
172
- legalLinksWrapper: {
173
- width: "100%",
174
- alignItems: "center",
175
- },
176
- legalLinksContainer: {
177
- flexDirection: "row",
178
- alignItems: "center",
179
- justifyContent: "center",
180
- paddingVertical: 8,
181
- paddingHorizontal: 16,
182
- borderRadius: 20,
183
- backgroundColor: "rgba(255, 255, 255, 0.03)",
184
- borderWidth: 1,
185
- borderColor: "rgba(255, 255, 255, 0.05)",
186
- },
187
- linkItem: {
188
- paddingVertical: 2,
189
- },
190
- linkText: {
191
- fontSize: 11,
192
- fontWeight: "500",
193
- letterSpacing: 0.3,
194
- },
195
- dot: {
196
- width: 3,
197
- height: 3,
198
- borderRadius: 1.5,
199
- marginHorizontal: 12,
200
- opacity: 0.3,
201
- },
202
- });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Paywall Legal Footer Styles
3
+ * StyleSheet definitions for paywall legal footer
4
+ */
5
+
6
+ import { StyleSheet } from "react-native";
7
+
8
+ export const styles = StyleSheet.create({
9
+ container: {
10
+ alignItems: "center",
11
+ paddingHorizontal: 24,
12
+ paddingBottom: 24,
13
+ paddingTop: 8,
14
+ width: "100%",
15
+ },
16
+ termsText: {
17
+ textAlign: "center",
18
+ fontSize: 10,
19
+ lineHeight: 14,
20
+ marginBottom: 16,
21
+ opacity: 0.7,
22
+ },
23
+ legalLinksWrapper: {
24
+ width: "100%",
25
+ alignItems: "center",
26
+ },
27
+ legalLinksContainer: {
28
+ flexDirection: "row",
29
+ alignItems: "center",
30
+ justifyContent: "center",
31
+ paddingVertical: 8,
32
+ paddingHorizontal: 16,
33
+ borderRadius: 20,
34
+ backgroundColor: "rgba(255, 255, 255, 0.03)",
35
+ borderWidth: 1,
36
+ borderColor: "rgba(255, 255, 255, 0.05)",
37
+ },
38
+ linkItem: {
39
+ paddingVertical: 2,
40
+ },
41
+ linkText: {
42
+ fontSize: 11,
43
+ fontWeight: "500",
44
+ letterSpacing: 0.3,
45
+ },
46
+ dot: {
47
+ width: 3,
48
+ height: 3,
49
+ borderRadius: 1.5,
50
+ marginHorizontal: 12,
51
+ opacity: 0.3,
52
+ },
53
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Paywall Legal Footer Types
3
+ * Type definitions for paywall legal footer
4
+ */
5
+
6
+ export interface PaywallLegalFooterProps {
7
+ termsText?: string;
8
+ privacyUrl?: string;
9
+ termsUrl?: string;
10
+ privacyText?: string;
11
+ termsOfServiceText?: string;
12
+ showRestoreButton?: boolean;
13
+ restoreButtonText?: string;
14
+ onRestore?: () => void;
15
+ isProcessing?: boolean;
16
+ }
17
+
18
+ export const DEFAULT_TERMS =
19
+ "Payment will be charged to your account. Subscription automatically renews unless cancelled.";
@@ -3,7 +3,7 @@
3
3
  * Displays paywall with Credits and Subscription tabs
4
4
  */
5
5
 
6
- import React, { useEffect } from "react";
6
+ import React from "react";
7
7
  import { View, StyleSheet } from "react-native";
8
8
  import { BaseModal } from "@umituz/react-native-design-system";
9
9
  import type { PurchasesPackage } from "react-native-purchases";
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { View, StyleSheet, TouchableOpacity } from "react-native";
3
+ import type { PurchasesPackage } from "react-native-purchases";
3
4
  import { AtomicText } from "@umituz/react-native-design-system";
4
5
  import { useAppDesignTokens } from "@umituz/react-native-design-system";
5
6
  import { PaywallLegalFooter } from "./PaywallLegalFooter";
@@ -10,7 +11,7 @@ interface SubscriptionFooterProps {
10
11
  processingText: string;
11
12
  purchaseButtonText: string;
12
13
  hasPackages: boolean;
13
- selectedPkg: any; // Using any to avoid circular deps if needed, but preferably strict
14
+ selectedPkg: PurchasesPackage | null;
14
15
  restoreButtonText: string;
15
16
  showRestoreButton: boolean;
16
17
  privacyUrl?: string;
@@ -21,7 +22,6 @@ interface SubscriptionFooterProps {
21
22
  onRestore: () => void;
22
23
  }
23
24
 
24
- // @ts-ignore
25
25
  import { LinearGradient } from "expo-linear-gradient";
26
26
 
27
27
  export const SubscriptionFooter: React.FC<SubscriptionFooterProps> = React.memo(
@@ -108,10 +108,6 @@ const styles = StyleSheet.create({
108
108
  borderRadius: 16,
109
109
  alignItems: "center",
110
110
  justifyContent: "center",
111
- shadowColor: "#000",
112
- shadowOffset: { width: 0, height: 4 },
113
- shadowOpacity: 0.2,
114
- shadowRadius: 8,
115
111
  elevation: 4,
116
112
  },
117
113
  restoreButton: {
@@ -79,16 +79,6 @@ export const SubscriptionModal: React.FC<SubscriptionModalProps> = React.memo((p
79
79
  onClose,
80
80
  });
81
81
 
82
- if (__DEV__) {
83
- console.log("[SubscriptionModal] State:", {
84
- visible,
85
- isLoading,
86
- packagesCount: packages?.length ?? 0,
87
- selectedPkg: selectedPkg?.identifier ?? null,
88
- isProcessing,
89
- });
90
- }
91
-
92
82
  return (
93
83
  <BaseModal visible={visible} onClose={onClose}>
94
84
  <View style={styles.container}>