@umituz/react-native-subscription 2.2.31 → 2.2.33

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.2.31",
3
+ "version": "2.2.33",
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",
package/src/index.ts CHANGED
@@ -108,6 +108,11 @@ export { SubscriptionPlanCard } from "./presentation/components/paywall/Subscrip
108
108
  export { PaywallFeaturesList } from "./presentation/components/paywall/PaywallFeaturesList";
109
109
  export { PaywallFeatureItem } from "./presentation/components/paywall/PaywallFeatureItem";
110
110
  export { PaywallLegalFooter } from "./presentation/components/paywall/PaywallLegalFooter";
111
+ export {
112
+ PaywallModal,
113
+ type PaywallModalProps,
114
+ type PaywallModalStyles,
115
+ } from "./presentation/components/paywall/PaywallModal";
111
116
 
112
117
  // =============================================================================
113
118
  // PRESENTATION LAYER - Premium Details Components
@@ -30,11 +30,11 @@ export const PaywallLegalFooter: React.FC<PaywallLegalFooterProps> = React.memo(
30
30
  const tokens = useAppDesignTokens();
31
31
 
32
32
  if (__DEV__) {
33
- console.log("[PaywallLegalFooter] Rendering with props:", {
34
- hasPrivacyUrl: !!privacyUrl,
35
- hasTermsUrl: !!termsUrl,
36
- privacyText,
37
- termsText: termsText?.substring(0, 20) + "...",
33
+ console.log("[PaywallLegalFooter] Rendering links:", {
34
+ privacy: !!privacyUrl,
35
+ terms: !!termsUrl,
36
+ pText: privacyText,
37
+ tText: termsOfServiceText
38
38
  });
39
39
  }
40
40
 
@@ -62,32 +62,42 @@ export const PaywallLegalFooter: React.FC<PaywallLegalFooterProps> = React.memo(
62
62
  </AtomicText>
63
63
 
64
64
  {hasLinks && (
65
- <View style={styles.legalLinksContainer}>
66
- {privacyUrl && (
67
- <TouchableOpacity onPress={handlePrivacyPress} activeOpacity={0.7}>
68
- <AtomicText
69
- type="labelSmall"
70
- style={[styles.linkText, { color: tokens.colors.primary }]}
65
+ <View style={styles.legalLinksWrapper}>
66
+ <View style={[styles.legalLinksContainer, { borderColor: tokens.colors.border }]}>
67
+ {privacyUrl && (
68
+ <TouchableOpacity
69
+ onPress={handlePrivacyPress}
70
+ activeOpacity={0.6}
71
+ style={styles.linkItem}
71
72
  >
72
- {privacyText}
73
- </AtomicText>
74
- </TouchableOpacity>
75
- )}
76
- {privacyUrl && termsUrl && (
77
- <AtomicText type="labelSmall" style={[styles.separator, { color: tokens.colors.textTertiary }]}>
78
- {" • "}
79
- </AtomicText>
80
- )}
81
- {termsUrl && (
82
- <TouchableOpacity onPress={handleTermsPress} activeOpacity={0.7}>
83
- <AtomicText
84
- type="labelSmall"
85
- style={[styles.linkText, { color: tokens.colors.primary }]}
73
+ <AtomicText
74
+ type="labelSmall"
75
+ style={[styles.linkText, { color: tokens.colors.textSecondary }]}
76
+ >
77
+ {privacyText}
78
+ </AtomicText>
79
+ </TouchableOpacity>
80
+ )}
81
+
82
+ {privacyUrl && termsUrl && (
83
+ <View style={[styles.dot, { backgroundColor: tokens.colors.border }]} />
84
+ )}
85
+
86
+ {termsUrl && (
87
+ <TouchableOpacity
88
+ onPress={handleTermsPress}
89
+ activeOpacity={0.6}
90
+ style={styles.linkItem}
86
91
  >
87
- {termsOfServiceText}
88
- </AtomicText>
89
- </TouchableOpacity>
90
- )}
92
+ <AtomicText
93
+ type="labelSmall"
94
+ style={[styles.linkText, { color: tokens.colors.textSecondary }]}
95
+ >
96
+ {termsOfServiceText}
97
+ </AtomicText>
98
+ </TouchableOpacity>
99
+ )}
100
+ </View>
91
101
  </View>
92
102
  )}
93
103
  </View>
@@ -101,28 +111,45 @@ const styles = StyleSheet.create({
101
111
  container: {
102
112
  alignItems: "center",
103
113
  paddingHorizontal: 24,
104
- paddingBottom: 32, // Increased for home indicator safety
114
+ paddingBottom: 24,
105
115
  paddingTop: 8,
106
116
  width: "100%",
107
117
  },
108
118
  termsText: {
109
119
  textAlign: "center",
110
- fontSize: 11,
111
- lineHeight: 16,
112
- marginBottom: 10,
120
+ fontSize: 10,
121
+ lineHeight: 14,
122
+ marginBottom: 16,
123
+ opacity: 0.7,
124
+ },
125
+ legalLinksWrapper: {
126
+ width: "100%",
127
+ alignItems: "center",
113
128
  },
114
129
  legalLinksContainer: {
115
130
  flexDirection: "row",
116
131
  alignItems: "center",
117
132
  justifyContent: "center",
118
- marginTop: 4,
119
- flexWrap: "wrap",
133
+ paddingVertical: 8,
134
+ paddingHorizontal: 16,
135
+ borderRadius: 20,
136
+ backgroundColor: "rgba(255, 255, 255, 0.03)",
137
+ borderWidth: 1,
138
+ borderColor: "rgba(255, 255, 255, 0.05)",
139
+ },
140
+ linkItem: {
141
+ paddingVertical: 2,
120
142
  },
121
143
  linkText: {
122
- textDecorationLine: "underline",
123
- fontSize: 12,
144
+ fontSize: 11,
145
+ fontWeight: "500",
146
+ letterSpacing: 0.3,
124
147
  },
125
- separator: {
126
- marginHorizontal: 8,
148
+ dot: {
149
+ width: 3,
150
+ height: 3,
151
+ borderRadius: 1.5,
152
+ marginHorizontal: 12,
153
+ opacity: 0.3,
127
154
  },
128
155
  });
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import React, { useEffect } from "react";
7
- import { View, Modal, StyleSheet, TouchableOpacity } from "react-native";
7
+ import { View, Modal, StyleSheet, TouchableOpacity, Dimensions } from "react-native";
8
8
  import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
9
9
  import { useLocalization } from "@umituz/react-native-localization";
10
10
  import type { PurchasesPackage } from "react-native-purchases";
@@ -13,10 +13,34 @@ import { PaywallHeader } from "./PaywallHeader";
13
13
  import { PaywallTabBar } from "./PaywallTabBar";
14
14
  import { CreditsTabContent } from "./CreditsTabContent";
15
15
  import { SubscriptionTabContent } from "./SubscriptionTabContent";
16
+ import { ModalLayoutConfig } from "./SubscriptionModalOverlay";
16
17
  import type { PaywallTabType } from "../../../domain/entities/paywall/PaywallTab";
17
18
  import type { CreditsPackage } from "../../../domain/entities/paywall/CreditsPackage";
18
19
 
19
- interface PaywallModalProps {
20
+ const { height: SCREEN_HEIGHT } = Dimensions.get("window");
21
+
22
+ export interface PaywallModalStyles {
23
+ headerTopPadding?: number;
24
+ contentHorizontalPadding?: number;
25
+ contentBottomPadding?: number;
26
+ }
27
+
28
+ const DEFAULT_LAYOUT: ModalLayoutConfig = {
29
+ maxWidth: 480,
30
+ maxHeightPercent: 0.88,
31
+ widthPercent: 0.92,
32
+ borderRadius: 32,
33
+ backdropOpacity: 0.85,
34
+ horizontalPadding: 20,
35
+ };
36
+
37
+ const DEFAULT_STYLES: PaywallModalStyles = {
38
+ headerTopPadding: 48,
39
+ contentHorizontalPadding: 24,
40
+ contentBottomPadding: 32,
41
+ };
42
+
43
+ export interface PaywallModalProps {
20
44
  visible: boolean;
21
45
  onClose: () => void;
22
46
  initialTab?: PaywallTabType;
@@ -36,32 +60,40 @@ interface PaywallModalProps {
36
60
  privacyText?: string;
37
61
  termsOfServiceText?: string;
38
62
  restoreButtonText?: string;
63
+ layoutConfig?: ModalLayoutConfig;
64
+ styleConfig?: PaywallModalStyles;
39
65
  }
40
66
 
41
67
  export const PaywallModal: React.FC<PaywallModalProps> = React.memo(
42
- ({
43
- visible,
44
- onClose,
45
- initialTab = "credits",
46
- creditsPackages,
47
- subscriptionPackages,
48
- currentCredits,
49
- requiredCredits,
50
- onCreditsPurchase,
51
- onSubscriptionPurchase,
52
- onRestore,
53
- subscriptionFeatures = [],
54
- isLoading = false,
55
- title,
56
- subtitle,
57
- privacyUrl,
58
- termsUrl,
59
- privacyText,
60
- termsOfServiceText,
61
- restoreButtonText,
62
- }) => {
68
+ (props) => {
69
+ const {
70
+ visible,
71
+ onClose,
72
+ initialTab = "credits",
73
+ creditsPackages,
74
+ subscriptionPackages,
75
+ currentCredits,
76
+ requiredCredits,
77
+ onCreditsPurchase,
78
+ onSubscriptionPurchase,
79
+ onRestore,
80
+ subscriptionFeatures = [],
81
+ isLoading = false,
82
+ title,
83
+ subtitle,
84
+ privacyUrl,
85
+ termsUrl,
86
+ privacyText,
87
+ termsOfServiceText,
88
+ restoreButtonText,
89
+ layoutConfig,
90
+ styleConfig,
91
+ } = props;
92
+
63
93
  const tokens = useAppDesignTokens();
64
94
  const { t } = useLocalization();
95
+ const config = { ...DEFAULT_LAYOUT, ...layoutConfig };
96
+ const styles_ = { ...DEFAULT_STYLES, ...styleConfig };
65
97
 
66
98
  const {
67
99
  activeTab,
@@ -96,16 +128,29 @@ export const PaywallModal: React.FC<PaywallModalProps> = React.memo(
96
128
  animationType="fade"
97
129
  onRequestClose={onClose}
98
130
  >
99
- <View style={styles.overlay}>
131
+ <View style={[styles.overlay, { paddingHorizontal: config.horizontalPadding }]}>
100
132
  <TouchableOpacity
101
- style={styles.backdrop}
133
+ style={[
134
+ styles.backdrop,
135
+ { backgroundColor: `rgba(0, 0, 0, ${config.backdropOpacity ?? 0.85})` }
136
+ ]}
102
137
  activeOpacity={1}
103
138
  onPress={onClose}
104
139
  />
105
140
  <View
106
- style={[styles.content, { backgroundColor: tokens.colors.surface }]}
141
+ style={[
142
+ styles.content,
143
+ {
144
+ backgroundColor: tokens.colors.surface,
145
+ borderRadius: config.borderRadius ?? 32,
146
+ maxHeight: SCREEN_HEIGHT * (config.maxHeightPercent ?? 0.88),
147
+ height: SCREEN_HEIGHT * (config.maxHeightPercent ?? 0.88), // Explicit height to prevent collapse
148
+ width: `${(config.widthPercent ?? 0.92) * 100}%`,
149
+ maxWidth: config.maxWidth ?? 480,
150
+ }
151
+ ]}
107
152
  >
108
- <View style={styles.contentInner}>
153
+ <View style={[styles.contentInner, { paddingTop: styles_.headerTopPadding }]}>
109
154
  <PaywallHeader
110
155
  title={displayTitle}
111
156
  subtitle={displaySubtitle}
@@ -120,34 +165,36 @@ export const PaywallModal: React.FC<PaywallModalProps> = React.memo(
120
165
  subscriptionLabel={t("paywall.tabs.subscription", { defaultValue: "Subscription" })}
121
166
  />
122
167
 
123
- {activeTab === "credits" ? (
124
- <CreditsTabContent
125
- packages={creditsPackages}
126
- selectedPackageId={selectedCreditsPackageId}
127
- onSelectPackage={handleCreditsPackageSelect}
128
- onPurchase={handleCreditsPurchase}
129
- currentCredits={currentCredits}
130
- requiredCredits={requiredCredits}
131
- isLoading={isLoading}
132
- purchaseButtonText={t("paywall.purchase", { defaultValue: "Purchase" })}
133
- />
134
- ) : (
135
- <SubscriptionTabContent
136
- packages={subscriptionPackages}
137
- selectedPackage={selectedSubscriptionPkg}
138
- onSelectPackage={handleSubscriptionPackageSelect}
139
- onPurchase={handleSubscriptionPurchase}
140
- features={subscriptionFeatures}
141
- isLoading={isLoading}
142
- purchaseButtonText={t("paywall.subscribe", { defaultValue: "Subscribe" })}
143
- onRestore={onRestore}
144
- privacyUrl={privacyUrl}
145
- termsUrl={termsUrl}
146
- privacyText={privacyText}
147
- termsOfServiceText={termsOfServiceText}
148
- restoreButtonText={restoreButtonText}
149
- />
150
- )}
168
+ <View style={{ flex: 1 }}>
169
+ {activeTab === "credits" ? (
170
+ <CreditsTabContent
171
+ packages={creditsPackages}
172
+ selectedPackageId={selectedCreditsPackageId}
173
+ onSelectPackage={handleCreditsPackageSelect}
174
+ onPurchase={handleCreditsPurchase}
175
+ currentCredits={currentCredits}
176
+ requiredCredits={requiredCredits}
177
+ isLoading={isLoading}
178
+ purchaseButtonText={t("paywall.purchase", { defaultValue: "Purchase" })}
179
+ />
180
+ ) : (
181
+ <SubscriptionTabContent
182
+ packages={subscriptionPackages}
183
+ selectedPackage={selectedSubscriptionPkg}
184
+ onSelectPackage={handleSubscriptionPackageSelect}
185
+ onPurchase={handleSubscriptionPurchase}
186
+ features={subscriptionFeatures}
187
+ isLoading={isLoading}
188
+ purchaseButtonText={t("paywall.subscribe", { defaultValue: "Subscribe" })}
189
+ onRestore={onRestore}
190
+ privacyUrl={privacyUrl}
191
+ termsUrl={termsUrl}
192
+ privacyText={privacyText}
193
+ termsOfServiceText={termsOfServiceText}
194
+ restoreButtonText={restoreButtonText}
195
+ />
196
+ )}
197
+ </View>
151
198
  </View>
152
199
  </View>
153
200
  </View>
@@ -163,24 +210,17 @@ const styles = StyleSheet.create({
163
210
  flex: 1,
164
211
  justifyContent: "center",
165
212
  alignItems: "center",
166
- paddingHorizontal: 20,
167
213
  },
168
214
  backdrop: {
169
215
  ...StyleSheet.absoluteFillObject,
170
- backgroundColor: "rgba(0, 0, 0, 0.85)",
171
216
  },
172
217
  content: {
173
- borderRadius: 32,
174
- maxHeight: "88%",
175
- width: "92%",
176
- maxWidth: 480,
177
218
  overflow: "hidden",
178
219
  borderWidth: 1,
179
220
  borderColor: "rgba(255, 255, 255, 0.08)",
180
221
  },
181
222
  contentInner: {
182
223
  flex: 1,
183
- paddingTop: 20,
184
224
  paddingBottom: 20,
185
225
  },
186
226
  });