@umituz/react-native-subscription 2.2.18 → 2.2.19

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.18",
3
+ "version": "2.2.19",
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",
@@ -10,6 +10,7 @@ import {
10
10
  StyleSheet,
11
11
  TouchableOpacity,
12
12
  ScrollView,
13
+ Dimensions,
13
14
  } from "react-native";
14
15
  import { SafeAreaView } from "react-native-safe-area-context";
15
16
  import type { PurchasesPackage } from "react-native-purchases";
@@ -19,6 +20,8 @@ import { PaywallFeaturesList } from "./PaywallFeaturesList";
19
20
  import { SubscriptionPackageList } from "./SubscriptionPackageList";
20
21
  import { SubscriptionFooter } from "./SubscriptionFooter";
21
22
 
23
+ const { height: SCREEN_HEIGHT } = Dimensions.get("window");
24
+
22
25
  export interface SubscriptionModalProps {
23
26
  visible: boolean;
24
27
  onClose: () => void;
@@ -97,93 +100,108 @@ export const SubscriptionModal: React.FC<SubscriptionModalProps> = React.memo(
97
100
 
98
101
  const isFullScreen = variant === "fullscreen";
99
102
 
103
+ // Fullscreen variant uses SafeAreaView, bottom-sheet uses regular View
100
104
  const ContentWrapper = isFullScreen ? SafeAreaView : View;
101
105
  const wrapperProps = isFullScreen ? { edges: ["top", "bottom"] as const } : {};
102
106
 
103
- const Content = (
104
- <ContentWrapper
105
- {...wrapperProps}
106
- style={isFullScreen ? styles.safeAreaFullScreen : styles.safeAreaBottomSheet}
107
+ // Scrollable content (packages + features)
108
+ const ScrollContent = (
109
+ <ScrollView
110
+ style={styles.scrollView}
111
+ contentContainerStyle={styles.scrollContent}
112
+ showsVerticalScrollIndicator={false}
113
+ bounces={false}
107
114
  >
108
- <View style={styles.header}>
109
- <TouchableOpacity style={styles.closeButton} onPress={onClose}>
110
- <AtomicText style={[styles.closeIcon, { color: tokens.colors.textSecondary }]}>
111
- ×
112
- </AtomicText>
113
- </TouchableOpacity>
114
- <AtomicText
115
- type="headlineMedium"
116
- style={[styles.title, { color: tokens.colors.textPrimary }]}
117
- >
118
- {title}
119
- </AtomicText>
120
- {subtitle && (
121
- <AtomicText
122
- type="bodyMedium"
123
- style={[styles.subtitle, { color: tokens.colors.textSecondary }]}
124
- >
125
- {subtitle}
126
- </AtomicText>
127
- )}
128
- </View>
129
-
130
- <ScrollView
131
- style={isFullScreen ? styles.scrollViewFullScreen : styles.scrollViewBottomSheet}
132
- contentContainerStyle={styles.scrollContent}
133
- >
134
- <SubscriptionPackageList
135
- packages={packages}
136
- isLoading={isLoading}
137
- selectedPkg={selectedPkg}
138
- onSelect={setSelectedPkg}
139
- loadingText={loadingText}
140
- emptyText={emptyText}
141
- />
142
- {features.length > 0 && (
143
- <View style={[styles.featuresSection, { backgroundColor: tokens.colors.surfaceSecondary }]}>
144
- <PaywallFeaturesList features={features} gap={12} />
145
- </View>
146
- )}
147
- </ScrollView>
148
-
149
- <SubscriptionFooter
150
- isProcessing={isProcessing}
115
+ <SubscriptionPackageList
116
+ packages={packages}
151
117
  isLoading={isLoading}
152
- processingText={processingText}
153
- purchaseButtonText={purchaseButtonText}
154
- hasPackages={packages.length > 0}
155
118
  selectedPkg={selectedPkg}
156
- restoreButtonText={restoreButtonText}
157
- showRestoreButton={showRestoreButton}
158
- privacyUrl={privacyUrl}
159
- termsUrl={termsUrl}
160
- privacyText={privacyText}
161
- termsOfServiceText={termsOfServiceText}
162
- onPurchase={handlePurchase}
163
- onRestore={handleRestore}
119
+ onSelect={setSelectedPkg}
120
+ loadingText={loadingText}
121
+ emptyText={emptyText}
164
122
  />
165
- </ContentWrapper>
123
+ {features.length > 0 && (
124
+ <View style={[styles.featuresSection, { backgroundColor: tokens.colors.surfaceSecondary }]}>
125
+ <PaywallFeaturesList features={features} gap={12} />
126
+ </View>
127
+ )}
128
+ </ScrollView>
129
+ );
130
+
131
+ // Footer with buttons and legal links
132
+ const Footer = (
133
+ <SubscriptionFooter
134
+ isProcessing={isProcessing}
135
+ isLoading={isLoading}
136
+ processingText={processingText}
137
+ purchaseButtonText={purchaseButtonText}
138
+ hasPackages={packages.length > 0}
139
+ selectedPkg={selectedPkg}
140
+ restoreButtonText={restoreButtonText}
141
+ showRestoreButton={showRestoreButton}
142
+ privacyUrl={privacyUrl}
143
+ termsUrl={termsUrl}
144
+ privacyText={privacyText}
145
+ termsOfServiceText={termsOfServiceText}
146
+ onPurchase={handlePurchase}
147
+ onRestore={handleRestore}
148
+ />
166
149
  );
167
150
 
168
- if (variant === "fullscreen") {
151
+ // Header with title and close button
152
+ const Header = (
153
+ <View style={styles.header}>
154
+ <TouchableOpacity style={styles.closeButton} onPress={onClose}>
155
+ <AtomicText style={[styles.closeIcon, { color: tokens.colors.textSecondary }]}>
156
+ ×
157
+ </AtomicText>
158
+ </TouchableOpacity>
159
+ <AtomicText
160
+ type="headlineMedium"
161
+ style={[styles.title, { color: tokens.colors.textPrimary }]}
162
+ >
163
+ {title}
164
+ </AtomicText>
165
+ {subtitle && (
166
+ <AtomicText
167
+ type="bodyMedium"
168
+ style={[styles.subtitle, { color: tokens.colors.textSecondary }]}
169
+ >
170
+ {subtitle}
171
+ </AtomicText>
172
+ )}
173
+ </View>
174
+ );
175
+
176
+ // FULLSCREEN VARIANT
177
+ if (isFullScreen) {
169
178
  const Wrapper = BackgroundComponent || View;
170
- const wrapperStyle = !BackgroundComponent ? { flex: 1, backgroundColor: tokens.colors.backgroundPrimary } : { flex: 1 };
179
+ const wrapperStyle = !BackgroundComponent
180
+ ? { flex: 1, backgroundColor: tokens.colors.backgroundPrimary }
181
+ : { flex: 1 };
171
182
 
172
183
  return (
173
184
  <Modal visible={visible} transparent={false} animationType="slide" onRequestClose={onClose}>
174
185
  <Wrapper style={wrapperStyle}>
175
- {Content}
186
+ <ContentWrapper {...wrapperProps} style={styles.fullscreenContainer}>
187
+ {Header}
188
+ {ScrollContent}
189
+ {Footer}
190
+ </ContentWrapper>
176
191
  </Wrapper>
177
192
  </Modal>
178
193
  );
179
194
  }
180
195
 
196
+ // BOTTOM-SHEET VARIANT
181
197
  return (
182
198
  <Modal visible={visible} transparent animationType="slide" onRequestClose={onClose}>
183
199
  <View style={styles.overlay}>
184
200
  <TouchableOpacity style={styles.backdrop} activeOpacity={1} onPress={onClose} />
185
- <View style={[styles.bottomSheetContent, { backgroundColor: tokens.colors.surface }]}>
186
- {Content}
201
+ <View style={[styles.bottomSheetContainer, { backgroundColor: tokens.colors.surface }]}>
202
+ {Header}
203
+ {ScrollContent}
204
+ {Footer}
187
205
  </View>
188
206
  </View>
189
207
  </Modal>
@@ -194,22 +212,70 @@ export const SubscriptionModal: React.FC<SubscriptionModalProps> = React.memo(
194
212
  SubscriptionModal.displayName = "SubscriptionModal";
195
213
 
196
214
  const styles = StyleSheet.create({
197
- overlay: { flex: 1, justifyContent: "flex-end" },
198
- backdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: "rgba(0, 0, 0, 0.5)" },
199
- bottomSheetContent: { borderTopLeftRadius: 24, borderTopRightRadius: 24, maxHeight: "90%" },
215
+ // Overlay and backdrop for bottom-sheet
216
+ overlay: {
217
+ flex: 1,
218
+ justifyContent: "flex-end",
219
+ },
220
+ backdrop: {
221
+ ...StyleSheet.absoluteFillObject,
222
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
223
+ },
224
+
225
+ // Bottom-sheet container - KEY FIX: explicit max height calculation
226
+ bottomSheetContainer: {
227
+ borderTopLeftRadius: 24,
228
+ borderTopRightRadius: 24,
229
+ maxHeight: SCREEN_HEIGHT * 0.85,
230
+ paddingTop: 16,
231
+ paddingBottom: 24,
232
+ },
200
233
 
201
- safeAreaFullScreen: { flex: 1, paddingTop: 16 },
202
- safeAreaBottomSheet: { flex: 1, paddingTop: 16, paddingBottom: 24 },
234
+ // Fullscreen container
235
+ fullscreenContainer: {
236
+ flex: 1,
237
+ paddingTop: 16,
238
+ },
203
239
 
204
- header: { alignItems: "center", paddingHorizontal: 24, paddingBottom: 20 },
205
- closeButton: { position: "absolute", top: 0, right: 16, padding: 8, zIndex: 1 },
206
- closeIcon: { fontSize: 28, fontWeight: "300" },
207
- title: { marginBottom: 8, textAlign: "center" },
208
- subtitle: { textAlign: "center" },
240
+ // Header
241
+ header: {
242
+ alignItems: "center",
243
+ paddingHorizontal: 24,
244
+ paddingBottom: 16,
245
+ },
246
+ closeButton: {
247
+ position: "absolute",
248
+ top: 0,
249
+ right: 16,
250
+ padding: 8,
251
+ zIndex: 1,
252
+ },
253
+ closeIcon: {
254
+ fontSize: 28,
255
+ fontWeight: "300",
256
+ },
257
+ title: {
258
+ marginBottom: 8,
259
+ textAlign: "center",
260
+ },
261
+ subtitle: {
262
+ textAlign: "center",
263
+ },
209
264
 
210
- scrollViewFullScreen: { flex: 1 },
211
- scrollViewBottomSheet: {},
265
+ // ScrollView - takes remaining space
266
+ scrollView: {
267
+ flexGrow: 0,
268
+ flexShrink: 1,
269
+ },
270
+ scrollContent: {
271
+ paddingHorizontal: 24,
272
+ paddingBottom: 16,
273
+ },
212
274
 
213
- scrollContent: { paddingHorizontal: 24, paddingBottom: 16 },
214
- featuresSection: { borderRadius: 16, padding: 16, marginTop: 20 },
275
+ // Features section
276
+ featuresSection: {
277
+ borderRadius: 16,
278
+ padding: 16,
279
+ marginTop: 16,
280
+ },
215
281
  });