@umituz/react-native-subscription 2.3.4 → 2.3.6
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.3.
|
|
3
|
+
"version": "2.3.6",
|
|
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
|
@@ -66,15 +66,9 @@ export {
|
|
|
66
66
|
export {
|
|
67
67
|
SubscriptionModal,
|
|
68
68
|
type SubscriptionModalProps,
|
|
69
|
-
type SubscriptionModalStyles,
|
|
70
69
|
} from "./presentation/components/paywall/SubscriptionModal";
|
|
71
70
|
|
|
72
71
|
export { SubscriptionModalHeader } from "./presentation/components/paywall/SubscriptionModalHeader";
|
|
73
|
-
export {
|
|
74
|
-
SubscriptionModalOverlay,
|
|
75
|
-
type SubscriptionModalVariant,
|
|
76
|
-
type ModalLayoutConfig,
|
|
77
|
-
} from "./presentation/components/paywall/SubscriptionModalOverlay";
|
|
78
72
|
|
|
79
73
|
export { SubscriptionPlanCard } from "./presentation/components/paywall/SubscriptionPlanCard";
|
|
80
74
|
export { PaywallFeaturesList } from "./presentation/components/paywall/PaywallFeaturesList";
|
|
@@ -83,7 +77,6 @@ export { PaywallLegalFooter } from "./presentation/components/paywall/PaywallLeg
|
|
|
83
77
|
export {
|
|
84
78
|
PaywallModal,
|
|
85
79
|
type PaywallModalProps,
|
|
86
|
-
type PaywallModalStyles,
|
|
87
80
|
} from "./presentation/components/paywall/PaywallModal";
|
|
88
81
|
|
|
89
82
|
// =============================================================================
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Paywall Modal Component
|
|
3
|
-
*
|
|
3
|
+
* Displays paywall with Credits and Subscription tabs
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React, { useEffect } from "react";
|
|
7
|
-
import { View,
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
7
|
+
import { View, StyleSheet } from "react-native";
|
|
8
|
+
import { useAppDesignTokens, BaseModal, useResponsive } from "@umituz/react-native-design-system";
|
|
9
9
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
10
10
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
11
11
|
import { usePaywall } from "../../hooks/usePaywall";
|
|
@@ -13,33 +13,15 @@ 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";
|
|
17
16
|
import type { PaywallTabType } from "../../../domain/entities/paywall/PaywallTab";
|
|
18
17
|
import type { CreditsPackage } from "../../../domain/entities/paywall/CreditsPackage";
|
|
19
18
|
|
|
20
|
-
const { height: SCREEN_HEIGHT } = Dimensions.get("window");
|
|
21
|
-
|
|
22
19
|
export interface PaywallModalStyles {
|
|
23
20
|
headerTopPadding?: number;
|
|
24
21
|
contentHorizontalPadding?: number;
|
|
25
22
|
contentBottomPadding?: number;
|
|
26
23
|
}
|
|
27
24
|
|
|
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
25
|
export interface PaywallModalProps {
|
|
44
26
|
visible: boolean;
|
|
45
27
|
onClose: () => void;
|
|
@@ -60,167 +42,125 @@ export interface PaywallModalProps {
|
|
|
60
42
|
privacyText?: string;
|
|
61
43
|
termsOfServiceText?: string;
|
|
62
44
|
restoreButtonText?: string;
|
|
63
|
-
layoutConfig?: ModalLayoutConfig;
|
|
64
|
-
styleConfig?: PaywallModalStyles;
|
|
65
45
|
}
|
|
66
46
|
|
|
67
|
-
export const PaywallModal: React.FC<PaywallModalProps> = React.memo(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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")}
|
|
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")}
|
|
189
|
-
onRestore={onRestore}
|
|
190
|
-
privacyUrl={privacyUrl}
|
|
191
|
-
termsUrl={termsUrl}
|
|
192
|
-
privacyText={privacyText}
|
|
193
|
-
termsOfServiceText={termsOfServiceText}
|
|
194
|
-
restoreButtonText={restoreButtonText}
|
|
195
|
-
/>
|
|
196
|
-
)}
|
|
197
|
-
</View>
|
|
198
|
-
</View>
|
|
199
|
-
</View>
|
|
47
|
+
export const PaywallModal: React.FC<PaywallModalProps> = React.memo((props) => {
|
|
48
|
+
const {
|
|
49
|
+
visible,
|
|
50
|
+
onClose,
|
|
51
|
+
initialTab = "credits",
|
|
52
|
+
creditsPackages,
|
|
53
|
+
subscriptionPackages,
|
|
54
|
+
currentCredits,
|
|
55
|
+
requiredCredits,
|
|
56
|
+
onCreditsPurchase,
|
|
57
|
+
onSubscriptionPurchase,
|
|
58
|
+
onRestore,
|
|
59
|
+
subscriptionFeatures = [],
|
|
60
|
+
isLoading = false,
|
|
61
|
+
title,
|
|
62
|
+
subtitle,
|
|
63
|
+
privacyUrl,
|
|
64
|
+
termsUrl,
|
|
65
|
+
privacyText,
|
|
66
|
+
termsOfServiceText,
|
|
67
|
+
restoreButtonText,
|
|
68
|
+
} = props;
|
|
69
|
+
|
|
70
|
+
const tokens = useAppDesignTokens();
|
|
71
|
+
const { t } = useLocalization();
|
|
72
|
+
const { modalLayout } = useResponsive();
|
|
73
|
+
|
|
74
|
+
const {
|
|
75
|
+
activeTab,
|
|
76
|
+
selectedCreditsPackageId,
|
|
77
|
+
selectedSubscriptionPkg,
|
|
78
|
+
handleTabChange,
|
|
79
|
+
handleCreditsPackageSelect,
|
|
80
|
+
handleSubscriptionPackageSelect,
|
|
81
|
+
handleCreditsPurchase,
|
|
82
|
+
handleSubscriptionPurchase,
|
|
83
|
+
} = usePaywall({
|
|
84
|
+
initialTab,
|
|
85
|
+
onCreditsPurchase,
|
|
86
|
+
onSubscriptionPurchase,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const displayTitle = title || t("paywall.title");
|
|
90
|
+
const displaySubtitle = subtitle || t("paywall.subtitle");
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (__DEV__) {
|
|
94
|
+
console.log("[PaywallModal] State:", {
|
|
95
|
+
visible,
|
|
96
|
+
activeTab,
|
|
97
|
+
modalLayout,
|
|
98
|
+
creditsPackagesCount: creditsPackages?.length ?? 0,
|
|
99
|
+
subscriptionPackagesCount: subscriptionPackages?.length ?? 0,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}, [visible, activeTab, modalLayout, creditsPackages?.length, subscriptionPackages?.length]);
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<BaseModal visible={visible} onClose={onClose}>
|
|
106
|
+
<View style={styles.container}>
|
|
107
|
+
<PaywallHeader
|
|
108
|
+
title={displayTitle}
|
|
109
|
+
subtitle={displaySubtitle}
|
|
110
|
+
onClose={onClose}
|
|
111
|
+
variant="fullscreen"
|
|
112
|
+
/>
|
|
113
|
+
|
|
114
|
+
<PaywallTabBar
|
|
115
|
+
activeTab={activeTab}
|
|
116
|
+
onTabChange={handleTabChange}
|
|
117
|
+
creditsLabel={t("paywall.tabs.credits")}
|
|
118
|
+
subscriptionLabel={t("paywall.tabs.subscription")}
|
|
119
|
+
/>
|
|
120
|
+
|
|
121
|
+
<View style={styles.tabContent}>
|
|
122
|
+
{activeTab === "credits" ? (
|
|
123
|
+
<CreditsTabContent
|
|
124
|
+
packages={creditsPackages}
|
|
125
|
+
selectedPackageId={selectedCreditsPackageId}
|
|
126
|
+
onSelectPackage={handleCreditsPackageSelect}
|
|
127
|
+
onPurchase={handleCreditsPurchase}
|
|
128
|
+
currentCredits={currentCredits}
|
|
129
|
+
requiredCredits={requiredCredits}
|
|
130
|
+
isLoading={isLoading}
|
|
131
|
+
purchaseButtonText={t("paywall.purchase")}
|
|
132
|
+
/>
|
|
133
|
+
) : (
|
|
134
|
+
<SubscriptionTabContent
|
|
135
|
+
packages={subscriptionPackages}
|
|
136
|
+
selectedPackage={selectedSubscriptionPkg}
|
|
137
|
+
onSelectPackage={handleSubscriptionPackageSelect}
|
|
138
|
+
onPurchase={handleSubscriptionPurchase}
|
|
139
|
+
features={subscriptionFeatures}
|
|
140
|
+
isLoading={isLoading}
|
|
141
|
+
purchaseButtonText={t("paywall.subscribe")}
|
|
142
|
+
onRestore={onRestore}
|
|
143
|
+
privacyUrl={privacyUrl}
|
|
144
|
+
termsUrl={termsUrl}
|
|
145
|
+
privacyText={privacyText}
|
|
146
|
+
termsOfServiceText={termsOfServiceText}
|
|
147
|
+
restoreButtonText={restoreButtonText}
|
|
148
|
+
/>
|
|
149
|
+
)}
|
|
200
150
|
</View>
|
|
201
|
-
</
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
);
|
|
151
|
+
</View>
|
|
152
|
+
</BaseModal>
|
|
153
|
+
);
|
|
154
|
+
});
|
|
205
155
|
|
|
206
156
|
PaywallModal.displayName = "PaywallModal";
|
|
207
157
|
|
|
208
158
|
const styles = StyleSheet.create({
|
|
209
|
-
|
|
159
|
+
container: {
|
|
210
160
|
flex: 1,
|
|
211
|
-
|
|
212
|
-
alignItems: "center",
|
|
213
|
-
},
|
|
214
|
-
backdrop: {
|
|
215
|
-
...StyleSheet.absoluteFillObject,
|
|
216
|
-
},
|
|
217
|
-
content: {
|
|
218
|
-
overflow: "hidden",
|
|
219
|
-
borderWidth: 1,
|
|
220
|
-
borderColor: "rgba(255, 255, 255, 0.08)",
|
|
161
|
+
width: "100%",
|
|
221
162
|
},
|
|
222
|
-
|
|
163
|
+
tabContent: {
|
|
223
164
|
flex: 1,
|
|
224
|
-
paddingBottom: 20,
|
|
225
165
|
},
|
|
226
166
|
});
|
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Subscription Modal Component
|
|
3
|
-
*
|
|
3
|
+
* Fullscreen subscription flow using BaseModal from design system
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
7
|
import { View, StyleSheet, ScrollView } from "react-native";
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
8
|
+
import { useAppDesignTokens, BaseModal, useResponsive } from "@umituz/react-native-design-system";
|
|
9
9
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
10
10
|
|
|
11
11
|
import { SubscriptionModalHeader } from "./SubscriptionModalHeader";
|
|
12
|
-
import {
|
|
13
|
-
SubscriptionModalOverlay,
|
|
14
|
-
SubscriptionModalVariant,
|
|
15
|
-
ModalLayoutConfig,
|
|
16
|
-
} from "./SubscriptionModalOverlay";
|
|
17
12
|
import { PaywallFeaturesList } from "./PaywallFeaturesList";
|
|
18
13
|
import { SubscriptionPackageList } from "./SubscriptionPackageList";
|
|
19
14
|
import { SubscriptionFooter } from "./SubscriptionFooter";
|
|
20
15
|
import { useSubscriptionModal } from "../../hooks/useSubscriptionModal";
|
|
21
16
|
|
|
22
|
-
export interface SubscriptionModalStyles {
|
|
23
|
-
headerTopPadding?: number;
|
|
24
|
-
contentHorizontalPadding?: number;
|
|
25
|
-
contentBottomPadding?: number;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
17
|
export interface SubscriptionModalProps {
|
|
29
18
|
visible: boolean;
|
|
30
19
|
onClose: () => void;
|
|
@@ -45,17 +34,8 @@ export interface SubscriptionModalProps {
|
|
|
45
34
|
privacyText?: string;
|
|
46
35
|
termsOfServiceText?: string;
|
|
47
36
|
showRestoreButton?: boolean;
|
|
48
|
-
variant?: SubscriptionModalVariant;
|
|
49
|
-
layoutConfig?: ModalLayoutConfig;
|
|
50
|
-
styleConfig?: SubscriptionModalStyles;
|
|
51
37
|
}
|
|
52
38
|
|
|
53
|
-
const DEFAULT_STYLES: SubscriptionModalStyles = {
|
|
54
|
-
headerTopPadding: 48,
|
|
55
|
-
contentHorizontalPadding: 24,
|
|
56
|
-
contentBottomPadding: 32,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
39
|
export const SubscriptionModal: React.FC<SubscriptionModalProps> = React.memo((props) => {
|
|
60
40
|
const {
|
|
61
41
|
visible,
|
|
@@ -77,13 +57,10 @@ export const SubscriptionModal: React.FC<SubscriptionModalProps> = React.memo((p
|
|
|
77
57
|
privacyText,
|
|
78
58
|
termsOfServiceText,
|
|
79
59
|
showRestoreButton = true,
|
|
80
|
-
variant = "bottom-sheet",
|
|
81
|
-
layoutConfig,
|
|
82
|
-
styleConfig,
|
|
83
60
|
} = props;
|
|
84
61
|
|
|
85
62
|
const tokens = useAppDesignTokens();
|
|
86
|
-
const
|
|
63
|
+
const { modalLayout } = useResponsive();
|
|
87
64
|
|
|
88
65
|
const {
|
|
89
66
|
selectedPkg,
|
|
@@ -97,55 +74,32 @@ export const SubscriptionModal: React.FC<SubscriptionModalProps> = React.memo((p
|
|
|
97
74
|
onClose,
|
|
98
75
|
});
|
|
99
76
|
|
|
100
|
-
// Debug logging in development
|
|
101
77
|
if (__DEV__) {
|
|
102
78
|
console.log("[SubscriptionModal] State:", {
|
|
103
79
|
visible,
|
|
104
|
-
variant,
|
|
105
80
|
isLoading,
|
|
106
81
|
packagesCount: packages?.length ?? 0,
|
|
107
|
-
|
|
108
|
-
id: p?.identifier,
|
|
109
|
-
price: p?.product?.priceString,
|
|
110
|
-
title: p?.product?.title,
|
|
111
|
-
})),
|
|
112
|
-
featuresCount: features?.length ?? 0,
|
|
113
|
-
title,
|
|
114
|
-
subtitle: subtitle?.substring(0, 50),
|
|
115
|
-
hasTokens: !!tokens,
|
|
82
|
+
modalLayout,
|
|
116
83
|
selectedPkg: selectedPkg?.identifier ?? null,
|
|
117
84
|
isProcessing,
|
|
118
85
|
});
|
|
119
86
|
}
|
|
120
87
|
|
|
121
|
-
if (!visible) return null;
|
|
122
|
-
|
|
123
|
-
const isFullscreen = variant === "fullscreen";
|
|
124
|
-
const containerPaddingTop = isFullscreen ? styles_.headerTopPadding : 0;
|
|
125
|
-
|
|
126
88
|
return (
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
onClose={onClose}
|
|
130
|
-
variant={variant}
|
|
131
|
-
layoutConfig={layoutConfig}
|
|
132
|
-
>
|
|
133
|
-
<View style={[styles.container, { paddingTop: containerPaddingTop }]}>
|
|
89
|
+
<BaseModal visible={visible} onClose={onClose}>
|
|
90
|
+
<View style={styles.container}>
|
|
134
91
|
<SubscriptionModalHeader
|
|
135
92
|
title={title}
|
|
136
93
|
subtitle={subtitle}
|
|
137
94
|
onClose={onClose}
|
|
138
|
-
variant=
|
|
95
|
+
variant="fullscreen"
|
|
139
96
|
/>
|
|
140
97
|
|
|
141
98
|
<ScrollView
|
|
142
99
|
style={styles.scrollView}
|
|
143
100
|
contentContainerStyle={[
|
|
144
101
|
styles.scrollContent,
|
|
145
|
-
{
|
|
146
|
-
paddingHorizontal: styles_.contentHorizontalPadding,
|
|
147
|
-
paddingBottom: styles_.contentBottomPadding,
|
|
148
|
-
}
|
|
102
|
+
{ paddingHorizontal: modalLayout.horizontalPadding }
|
|
149
103
|
]}
|
|
150
104
|
showsVerticalScrollIndicator={false}
|
|
151
105
|
bounces={false}
|
|
@@ -160,7 +114,15 @@ export const SubscriptionModal: React.FC<SubscriptionModalProps> = React.memo((p
|
|
|
160
114
|
/>
|
|
161
115
|
|
|
162
116
|
{features.length > 0 && (
|
|
163
|
-
<View
|
|
117
|
+
<View
|
|
118
|
+
style={[
|
|
119
|
+
styles.featuresSection,
|
|
120
|
+
{
|
|
121
|
+
backgroundColor: tokens.colors.surfaceSecondary,
|
|
122
|
+
borderColor: tokens.colors.border
|
|
123
|
+
}
|
|
124
|
+
]}
|
|
125
|
+
>
|
|
164
126
|
<PaywallFeaturesList features={features} gap={12} />
|
|
165
127
|
</View>
|
|
166
128
|
)}
|
|
@@ -183,7 +145,7 @@ export const SubscriptionModal: React.FC<SubscriptionModalProps> = React.memo((p
|
|
|
183
145
|
onRestore={handleRestore}
|
|
184
146
|
/>
|
|
185
147
|
</View>
|
|
186
|
-
</
|
|
148
|
+
</BaseModal>
|
|
187
149
|
);
|
|
188
150
|
});
|
|
189
151
|
|
|
@@ -196,16 +158,15 @@ const styles = StyleSheet.create({
|
|
|
196
158
|
},
|
|
197
159
|
scrollView: {
|
|
198
160
|
flex: 1,
|
|
199
|
-
width: "100%",
|
|
200
161
|
},
|
|
201
162
|
scrollContent: {
|
|
202
163
|
flexGrow: 1,
|
|
164
|
+
paddingBottom: 32,
|
|
203
165
|
},
|
|
204
166
|
featuresSection: {
|
|
205
167
|
borderRadius: 24,
|
|
206
168
|
padding: 24,
|
|
207
169
|
marginTop: 12,
|
|
208
170
|
borderWidth: 1,
|
|
209
|
-
borderColor: "rgba(255, 255, 255, 0.05)",
|
|
210
171
|
},
|
|
211
172
|
});
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subscription Modal Overlay Component
|
|
3
|
-
* Handles the Modal wrapper and backdrop logic for different variants
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import {
|
|
8
|
-
View,
|
|
9
|
-
Modal,
|
|
10
|
-
StyleSheet,
|
|
11
|
-
TouchableOpacity,
|
|
12
|
-
Dimensions,
|
|
13
|
-
ViewStyle,
|
|
14
|
-
} from "react-native";
|
|
15
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
16
|
-
|
|
17
|
-
const { height: SCREEN_HEIGHT, width: SCREEN_WIDTH } = Dimensions.get("window");
|
|
18
|
-
|
|
19
|
-
export type SubscriptionModalVariant = "bottom-sheet" | "fullscreen" | "dialog";
|
|
20
|
-
|
|
21
|
-
export interface ModalLayoutConfig {
|
|
22
|
-
maxWidth?: number;
|
|
23
|
-
maxHeightPercent?: number;
|
|
24
|
-
widthPercent?: number;
|
|
25
|
-
borderRadius?: number;
|
|
26
|
-
backdropOpacity?: number;
|
|
27
|
-
horizontalPadding?: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const DEFAULT_LAYOUT: ModalLayoutConfig = {
|
|
31
|
-
maxWidth: 480,
|
|
32
|
-
maxHeightPercent: 0.88,
|
|
33
|
-
widthPercent: 0.92,
|
|
34
|
-
borderRadius: 32,
|
|
35
|
-
backdropOpacity: 0.85,
|
|
36
|
-
horizontalPadding: 20,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
interface SubscriptionModalOverlayProps {
|
|
40
|
-
visible: boolean;
|
|
41
|
-
onClose: () => void;
|
|
42
|
-
children: React.ReactNode;
|
|
43
|
-
variant: SubscriptionModalVariant;
|
|
44
|
-
layoutConfig?: ModalLayoutConfig;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const SubscriptionModalOverlay: React.FC<SubscriptionModalOverlayProps> = ({
|
|
48
|
-
visible,
|
|
49
|
-
onClose,
|
|
50
|
-
children,
|
|
51
|
-
variant,
|
|
52
|
-
layoutConfig,
|
|
53
|
-
}) => {
|
|
54
|
-
const tokens = useAppDesignTokens();
|
|
55
|
-
const config = { ...DEFAULT_LAYOUT, ...layoutConfig };
|
|
56
|
-
|
|
57
|
-
// Debug logging in development
|
|
58
|
-
if (__DEV__) {
|
|
59
|
-
console.log("[SubscriptionModalOverlay] State:", {
|
|
60
|
-
visible,
|
|
61
|
-
variant,
|
|
62
|
-
config,
|
|
63
|
-
screenWidth: SCREEN_WIDTH,
|
|
64
|
-
screenHeight: SCREEN_HEIGHT,
|
|
65
|
-
backgroundColor: tokens?.colors?.backgroundPrimary ?? "NO_TOKEN",
|
|
66
|
-
hasChildren: !!children,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const isFullScreen = variant === "fullscreen";
|
|
71
|
-
|
|
72
|
-
const getFullscreenContentStyle = (): ViewStyle => {
|
|
73
|
-
const width = Math.min(SCREEN_WIDTH * (config.widthPercent ?? 0.92), config.maxWidth ?? 480);
|
|
74
|
-
const height = SCREEN_HEIGHT * (config.maxHeightPercent ?? 0.88);
|
|
75
|
-
|
|
76
|
-
if (__DEV__) {
|
|
77
|
-
console.log("[SubscriptionModalOverlay] Content dimensions:", { width, height });
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
width,
|
|
82
|
-
height, // Changed from maxHeight to height - this is critical!
|
|
83
|
-
borderRadius: config.borderRadius ?? 32,
|
|
84
|
-
overflow: "hidden",
|
|
85
|
-
borderWidth: 2,
|
|
86
|
-
borderColor: "rgba(255, 255, 255, 0.15)",
|
|
87
|
-
backgroundColor: tokens.colors.backgroundPrimary,
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
if (isFullScreen) {
|
|
92
|
-
return (
|
|
93
|
-
<Modal
|
|
94
|
-
visible={visible}
|
|
95
|
-
transparent
|
|
96
|
-
animationType="fade"
|
|
97
|
-
onRequestClose={onClose}
|
|
98
|
-
>
|
|
99
|
-
<View style={[styles.fullscreenOverlay, { paddingHorizontal: config.horizontalPadding }]}>
|
|
100
|
-
<TouchableOpacity
|
|
101
|
-
style={[
|
|
102
|
-
styles.fullscreenBackdrop,
|
|
103
|
-
{ backgroundColor: `rgba(0, 0, 0, ${config.backdropOpacity ?? 0.85})` }
|
|
104
|
-
]}
|
|
105
|
-
activeOpacity={1}
|
|
106
|
-
onPress={onClose}
|
|
107
|
-
/>
|
|
108
|
-
<View style={getFullscreenContentStyle()}>
|
|
109
|
-
{children}
|
|
110
|
-
</View>
|
|
111
|
-
</View>
|
|
112
|
-
</Modal>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return (
|
|
117
|
-
<Modal
|
|
118
|
-
visible={visible}
|
|
119
|
-
transparent
|
|
120
|
-
animationType="fade"
|
|
121
|
-
onRequestClose={onClose}
|
|
122
|
-
>
|
|
123
|
-
<View style={[styles.overlay, variant === "dialog" && styles.overlayCentered]}>
|
|
124
|
-
<TouchableOpacity
|
|
125
|
-
style={styles.backdrop}
|
|
126
|
-
activeOpacity={1}
|
|
127
|
-
onPress={onClose}
|
|
128
|
-
/>
|
|
129
|
-
<View
|
|
130
|
-
style={[
|
|
131
|
-
variant === "bottom-sheet" ? styles.bottomSheet : styles.dialog,
|
|
132
|
-
{ backgroundColor: tokens.colors.surface }
|
|
133
|
-
]}
|
|
134
|
-
>
|
|
135
|
-
{children}
|
|
136
|
-
</View>
|
|
137
|
-
</View>
|
|
138
|
-
</Modal>
|
|
139
|
-
);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const styles = StyleSheet.create({
|
|
143
|
-
overlay: {
|
|
144
|
-
flex: 1,
|
|
145
|
-
justifyContent: "flex-end",
|
|
146
|
-
},
|
|
147
|
-
overlayCentered: {
|
|
148
|
-
justifyContent: "center",
|
|
149
|
-
paddingHorizontal: 20,
|
|
150
|
-
},
|
|
151
|
-
backdrop: {
|
|
152
|
-
...StyleSheet.absoluteFillObject,
|
|
153
|
-
backgroundColor: "rgba(0, 0, 0, 0.6)",
|
|
154
|
-
},
|
|
155
|
-
fullscreenOverlay: {
|
|
156
|
-
flex: 1,
|
|
157
|
-
justifyContent: "center",
|
|
158
|
-
alignItems: "center",
|
|
159
|
-
},
|
|
160
|
-
fullscreenBackdrop: {
|
|
161
|
-
...StyleSheet.absoluteFillObject,
|
|
162
|
-
},
|
|
163
|
-
bottomSheet: {
|
|
164
|
-
borderTopLeftRadius: 32,
|
|
165
|
-
borderTopRightRadius: 32,
|
|
166
|
-
maxHeight: SCREEN_HEIGHT * 0.9,
|
|
167
|
-
minHeight: 400,
|
|
168
|
-
width: "100%",
|
|
169
|
-
},
|
|
170
|
-
dialog: {
|
|
171
|
-
alignSelf: "center",
|
|
172
|
-
width: Math.min(SCREEN_WIDTH * 0.94, 500),
|
|
173
|
-
maxHeight: SCREEN_HEIGHT * 0.85,
|
|
174
|
-
borderRadius: 32,
|
|
175
|
-
overflow: "hidden",
|
|
176
|
-
borderWidth: 1,
|
|
177
|
-
borderColor: "rgba(255, 255, 255, 0.1)",
|
|
178
|
-
},
|
|
179
|
-
});
|