@umituz/react-native-subscription 3.1.24 → 3.1.26
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": "3.1.
|
|
3
|
+
"version": "3.1.26",
|
|
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",
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { View, TouchableOpacity } from "react-native";
|
|
2
|
+
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
3
3
|
import { AtomicText, AtomicSpinner } from "@umituz/react-native-design-system/atoms";
|
|
4
4
|
import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
|
|
5
5
|
import type { PaywallTranslations, PaywallLegalUrls } from "../entities/types";
|
|
6
|
-
import { paywallScreenStyles as styles } from "./PaywallScreen.styles";
|
|
7
6
|
|
|
8
7
|
interface PaywallFooterProps {
|
|
9
8
|
translations: PaywallTranslations;
|
|
@@ -13,6 +12,8 @@ interface PaywallFooterProps {
|
|
|
13
12
|
onRestore?: () => Promise<void | boolean>;
|
|
14
13
|
onLegalClick: (url: string | undefined) => void;
|
|
15
14
|
purchaseButtonText?: string;
|
|
15
|
+
isTablet?: boolean;
|
|
16
|
+
isSmallDevice?: boolean;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export const PaywallFooter: React.FC<PaywallFooterProps> = React.memo(({
|
|
@@ -23,58 +24,152 @@ export const PaywallFooter: React.FC<PaywallFooterProps> = React.memo(({
|
|
|
23
24
|
onRestore,
|
|
24
25
|
onLegalClick,
|
|
25
26
|
purchaseButtonText,
|
|
27
|
+
isTablet = false,
|
|
28
|
+
isSmallDevice = false,
|
|
26
29
|
}) => {
|
|
27
30
|
const tokens = useAppDesignTokens();
|
|
28
31
|
|
|
32
|
+
// Device-based sizing
|
|
33
|
+
const buttonHeight = React.useMemo(() => {
|
|
34
|
+
if (isTablet) return 64; // Larger button on tablet
|
|
35
|
+
if (isSmallDevice) return 52; // Slightly smaller on small phones
|
|
36
|
+
return 56; // Standard size
|
|
37
|
+
}, [isTablet, isSmallDevice]);
|
|
38
|
+
|
|
39
|
+
const fontSize = React.useMemo(() => {
|
|
40
|
+
if (isTablet) return 19;
|
|
41
|
+
if (isSmallDevice) return 16;
|
|
42
|
+
return 17;
|
|
43
|
+
}, [isTablet, isSmallDevice]);
|
|
44
|
+
|
|
45
|
+
const paddingVertical = React.useMemo(() => {
|
|
46
|
+
if (isTablet) return 20;
|
|
47
|
+
if (isSmallDevice) return 12;
|
|
48
|
+
return 16;
|
|
49
|
+
}, [isTablet, isSmallDevice]);
|
|
50
|
+
|
|
29
51
|
return (
|
|
30
|
-
<View style={
|
|
31
|
-
{/* Purchase Button */}
|
|
52
|
+
<View style={footerStyles.container}>
|
|
53
|
+
{/* Purchase Button - Device-responsive */}
|
|
32
54
|
{onPurchase && (
|
|
33
55
|
<TouchableOpacity
|
|
34
56
|
onPress={onPurchase}
|
|
35
57
|
disabled={isProcessing}
|
|
58
|
+
activeOpacity={0.8}
|
|
36
59
|
style={[
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
60
|
+
footerStyles.purchaseButton,
|
|
61
|
+
{
|
|
62
|
+
backgroundColor: tokens.colors.primary,
|
|
63
|
+
height: buttonHeight,
|
|
64
|
+
paddingVertical: paddingVertical,
|
|
65
|
+
},
|
|
66
|
+
isProcessing && footerStyles.buttonDisabled,
|
|
40
67
|
]}
|
|
41
68
|
>
|
|
42
69
|
{isProcessing ? (
|
|
43
|
-
<
|
|
70
|
+
<View style={footerStyles.loadingContainer}>
|
|
71
|
+
<AtomicSpinner size="sm" />
|
|
72
|
+
</View>
|
|
44
73
|
) : (
|
|
45
|
-
<AtomicText style={[
|
|
74
|
+
<AtomicText style={[footerStyles.purchaseButtonText, { color: tokens.colors.textPrimary, fontSize }]}>
|
|
46
75
|
{purchaseButtonText || translations.purchaseButtonText}
|
|
47
76
|
</AtomicText>
|
|
48
77
|
)}
|
|
49
78
|
</TouchableOpacity>
|
|
50
79
|
)}
|
|
51
80
|
|
|
52
|
-
{/* Restore
|
|
81
|
+
{/* Restore Link - Device-spaced */}
|
|
53
82
|
{onRestore && (
|
|
54
|
-
<TouchableOpacity
|
|
55
|
-
|
|
56
|
-
|
|
83
|
+
<TouchableOpacity
|
|
84
|
+
onPress={onRestore}
|
|
85
|
+
disabled={isProcessing}
|
|
86
|
+
activeOpacity={0.6}
|
|
87
|
+
style={[footerStyles.restoreButton, { marginTop: isTablet ? 16 : 8 }]}
|
|
88
|
+
>
|
|
89
|
+
<AtomicText style={[footerStyles.restoreText, { color: tokens.colors.textSecondary }]}>
|
|
90
|
+
{translations.restoreButtonText}
|
|
57
91
|
</AtomicText>
|
|
58
92
|
</TouchableOpacity>
|
|
59
93
|
)}
|
|
60
94
|
|
|
61
|
-
{/* Legal Links */}
|
|
62
|
-
<View style={
|
|
95
|
+
{/* Legal Links - Responsive spacing */}
|
|
96
|
+
<View style={footerStyles.legalContainer}>
|
|
63
97
|
{legalUrls.termsUrl && (
|
|
64
|
-
<TouchableOpacity
|
|
65
|
-
|
|
98
|
+
<TouchableOpacity
|
|
99
|
+
onPress={() => onLegalClick(legalUrls.termsUrl)}
|
|
100
|
+
activeOpacity={0.6}
|
|
101
|
+
>
|
|
102
|
+
<AtomicText style={[footerStyles.legalText, { color: tokens.colors.textTertiary }]}>
|
|
66
103
|
{translations.termsOfServiceText}
|
|
67
104
|
</AtomicText>
|
|
68
105
|
</TouchableOpacity>
|
|
69
106
|
)}
|
|
70
107
|
{legalUrls.privacyUrl && (
|
|
71
|
-
|
|
72
|
-
<AtomicText
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
108
|
+
<>
|
|
109
|
+
<AtomicText style={footerStyles.legalSeparator}> • </AtomicText>
|
|
110
|
+
<TouchableOpacity
|
|
111
|
+
onPress={() => onLegalClick(legalUrls.privacyUrl)}
|
|
112
|
+
activeOpacity={0.6}
|
|
113
|
+
>
|
|
114
|
+
<AtomicText style={[footerStyles.legalText, { color: tokens.colors.textTertiary }]}>
|
|
115
|
+
{translations.privacyText}
|
|
116
|
+
</AtomicText>
|
|
117
|
+
</TouchableOpacity>
|
|
118
|
+
</>
|
|
76
119
|
)}
|
|
77
120
|
</View>
|
|
78
121
|
</View>
|
|
79
122
|
);
|
|
80
123
|
});
|
|
124
|
+
|
|
125
|
+
// Modern footer styles
|
|
126
|
+
const footerStyles = StyleSheet.create({
|
|
127
|
+
container: {
|
|
128
|
+
width: '100%',
|
|
129
|
+
paddingHorizontal: 20,
|
|
130
|
+
paddingVertical: 16,
|
|
131
|
+
gap: 12,
|
|
132
|
+
},
|
|
133
|
+
purchaseButton: {
|
|
134
|
+
width: '100%',
|
|
135
|
+
borderRadius: 16,
|
|
136
|
+
justifyContent: 'center',
|
|
137
|
+
alignItems: 'center',
|
|
138
|
+
shadowColor: '#000',
|
|
139
|
+
shadowOffset: { width: 0, height: 4 },
|
|
140
|
+
shadowOpacity: 0.3,
|
|
141
|
+
shadowRadius: 12,
|
|
142
|
+
elevation: 8,
|
|
143
|
+
},
|
|
144
|
+
buttonDisabled: {
|
|
145
|
+
opacity: 0.5,
|
|
146
|
+
},
|
|
147
|
+
loadingContainer: {
|
|
148
|
+
justifyContent: 'center',
|
|
149
|
+
alignItems: 'center',
|
|
150
|
+
},
|
|
151
|
+
purchaseButtonText: {
|
|
152
|
+
fontWeight: '700',
|
|
153
|
+
letterSpacing: 0.5,
|
|
154
|
+
},
|
|
155
|
+
restoreButton: {
|
|
156
|
+
alignItems: 'center',
|
|
157
|
+
},
|
|
158
|
+
restoreText: {
|
|
159
|
+
fontWeight: '500',
|
|
160
|
+
textDecorationLine: 'underline',
|
|
161
|
+
},
|
|
162
|
+
legalContainer: {
|
|
163
|
+
flexDirection: 'row',
|
|
164
|
+
alignItems: 'center',
|
|
165
|
+
justifyContent: 'center',
|
|
166
|
+
flexWrap: 'wrap',
|
|
167
|
+
},
|
|
168
|
+
legalText: {
|
|
169
|
+
lineHeight: 16,
|
|
170
|
+
},
|
|
171
|
+
legalSeparator: {
|
|
172
|
+
color: 'rgba(255, 255, 255, 0.4)',
|
|
173
|
+
marginHorizontal: 4,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
@@ -121,46 +121,31 @@ export const paywallScreenStyles = StyleSheet.create({
|
|
|
121
121
|
letterSpacing: 0.5,
|
|
122
122
|
},
|
|
123
123
|
|
|
124
|
-
// Footer
|
|
125
|
-
|
|
124
|
+
// Footer container with modern design
|
|
125
|
+
footerContainer: {
|
|
126
126
|
position: "absolute",
|
|
127
127
|
bottom: 0,
|
|
128
128
|
left: 0,
|
|
129
129
|
right: 0,
|
|
130
|
-
paddingHorizontal:
|
|
131
|
-
paddingTop:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
borderTopColor: "rgba(255, 255, 255, 0.08)",
|
|
130
|
+
paddingHorizontal: 20,
|
|
131
|
+
paddingTop: 16,
|
|
132
|
+
borderTopWidth: 0.5,
|
|
133
|
+
borderTopColor: "rgba(255, 255, 255, 0.1)",
|
|
135
134
|
...Platform.select({
|
|
136
135
|
ios: {
|
|
136
|
+
backgroundColor: "rgba(0, 0, 0, 0.8)",
|
|
137
|
+
backdropFilter: "blur(20px)",
|
|
137
138
|
shadowColor: "#000",
|
|
138
|
-
shadowOffset: { width: 0, height: -
|
|
139
|
-
shadowOpacity: 0.
|
|
140
|
-
shadowRadius:
|
|
139
|
+
shadowOffset: { width: 0, height: -8 },
|
|
140
|
+
shadowOpacity: 0.3,
|
|
141
|
+
shadowRadius: 20,
|
|
141
142
|
},
|
|
142
143
|
android: {
|
|
143
|
-
|
|
144
|
+
backgroundColor: "#000000",
|
|
145
|
+
elevation: 12,
|
|
144
146
|
},
|
|
145
147
|
}),
|
|
146
148
|
},
|
|
147
|
-
purchaseButton: {
|
|
148
|
-
borderRadius: 18,
|
|
149
|
-
height: 60,
|
|
150
|
-
justifyContent: "center",
|
|
151
|
-
alignItems: "center",
|
|
152
|
-
shadowColor: "#000",
|
|
153
|
-
shadowOffset: { width: 0, height: 4 },
|
|
154
|
-
shadowOpacity: 0.3,
|
|
155
|
-
shadowRadius: 8,
|
|
156
|
-
elevation: 6,
|
|
157
|
-
marginBottom: 16,
|
|
158
|
-
},
|
|
159
|
-
purchaseButtonText: {
|
|
160
|
-
fontWeight: "700",
|
|
161
|
-
letterSpacing: 0.5,
|
|
162
|
-
fontSize: 16,
|
|
163
|
-
},
|
|
164
149
|
restoreButton: {
|
|
165
150
|
paddingVertical: 4,
|
|
166
151
|
},
|
|
@@ -194,6 +179,7 @@ export const paywallScreenStyles = StyleSheet.create({
|
|
|
194
179
|
// List
|
|
195
180
|
listContent: {
|
|
196
181
|
paddingBottom: 40,
|
|
182
|
+
paddingHorizontal: 20, // Add horizontal padding
|
|
197
183
|
},
|
|
198
184
|
loadingContainer: {
|
|
199
185
|
flex: 1,
|
|
@@ -18,6 +18,7 @@ import { useNavigation } from "@react-navigation/native";
|
|
|
18
18
|
import { AtomicIcon, AtomicSpinner } from "@umituz/react-native-design-system/atoms";
|
|
19
19
|
import { useSafeAreaInsets } from "@umituz/react-native-design-system/safe-area";
|
|
20
20
|
import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
|
|
21
|
+
import { useResponsive } from "@umituz/react-native-design-system/responsive";
|
|
21
22
|
import { paywallScreenStyles as styles } from "./PaywallScreen.styles";
|
|
22
23
|
import { PaywallFooter } from "./PaywallFooter";
|
|
23
24
|
import { usePaywallActions } from "../hooks/usePaywallActions";
|
|
@@ -61,6 +62,7 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
61
62
|
|
|
62
63
|
const tokens = useAppDesignTokens();
|
|
63
64
|
const insets = useSafeAreaInsets();
|
|
65
|
+
const responsive = useResponsive();
|
|
64
66
|
|
|
65
67
|
const handleClose = useCallback(() => {
|
|
66
68
|
if (__DEV__) console.log('[PaywallScreen] 🔙 Closing paywall');
|
|
@@ -190,11 +192,11 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
190
192
|
<View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
|
|
191
193
|
<StatusBar barStyle="light-content" />
|
|
192
194
|
|
|
193
|
-
{/* Absolute Close Button */}
|
|
195
|
+
{/* Absolute Close Button - Responsive positioning */}
|
|
194
196
|
<View style={{
|
|
195
197
|
position: 'absolute',
|
|
196
198
|
top: Math.max(insets.top, 16),
|
|
197
|
-
right:
|
|
199
|
+
right: 16,
|
|
198
200
|
zIndex: 10,
|
|
199
201
|
}}>
|
|
200
202
|
<TouchableOpacity
|
|
@@ -206,7 +208,7 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
206
208
|
</TouchableOpacity>
|
|
207
209
|
</View>
|
|
208
210
|
|
|
209
|
-
{/* Main Content */}
|
|
211
|
+
{/* Main Content - Responsive spacing */}
|
|
210
212
|
<FlatList
|
|
211
213
|
data={flatData}
|
|
212
214
|
renderItem={renderItem}
|
|
@@ -220,22 +222,27 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
220
222
|
contentContainerStyle={[
|
|
221
223
|
styles.listContent,
|
|
222
224
|
{
|
|
223
|
-
paddingTop: Math.max(insets.top, 20) +
|
|
224
|
-
paddingBottom:
|
|
225
|
+
paddingTop: Math.max(insets.top, 20) + 60, // Responsive header spacing
|
|
226
|
+
paddingBottom: 280 + responsive.verticalPadding, // Dynamic footer spacing
|
|
227
|
+
paddingHorizontal: responsive.horizontalPadding, // Device-based horizontal padding
|
|
225
228
|
}
|
|
226
229
|
]}
|
|
227
230
|
showsVerticalScrollIndicator={false}
|
|
228
231
|
/>
|
|
229
232
|
|
|
230
|
-
{/* Fixed Footer */}
|
|
231
|
-
<
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
233
|
+
{/* Fixed Footer - Responsive positioning */}
|
|
234
|
+
<View style={[styles.footerContainer, { paddingBottom: insets.bottom + responsive.verticalPadding }]}>
|
|
235
|
+
<PaywallFooter
|
|
236
|
+
translations={translations}
|
|
237
|
+
legalUrls={legalUrls}
|
|
238
|
+
isProcessing={isProcessing}
|
|
239
|
+
onPurchase={handlePurchase}
|
|
240
|
+
onRestore={handleRestore}
|
|
241
|
+
onLegalClick={handleLegalUrl}
|
|
242
|
+
isTablet={responsive.isTabletDevice}
|
|
243
|
+
isSmallDevice={responsive.isSmallDevice}
|
|
244
|
+
/>
|
|
245
|
+
</View>
|
|
239
246
|
</View>
|
|
240
247
|
);
|
|
241
248
|
});
|