@umituz/react-native-subscription 2.2.31 → 2.2.32

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.32",
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
@@ -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
  });