@umituz/react-native-subscription 3.1.27 → 3.1.29
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.29",
|
|
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",
|
|
@@ -2,6 +2,7 @@ import React from "react";
|
|
|
2
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
|
+
import { useResponsive } from "@umituz/react-native-design-system/responsive";
|
|
5
6
|
import type { PaywallTranslations, PaywallLegalUrls } from "../entities/types";
|
|
6
7
|
|
|
7
8
|
interface PaywallFooterProps {
|
|
@@ -12,8 +13,6 @@ interface PaywallFooterProps {
|
|
|
12
13
|
onRestore?: () => Promise<void | boolean>;
|
|
13
14
|
onLegalClick: (url: string | undefined) => void;
|
|
14
15
|
purchaseButtonText?: string;
|
|
15
|
-
isTablet?: boolean;
|
|
16
|
-
isSmallDevice?: boolean;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
export const PaywallFooter: React.FC<PaywallFooterProps> = React.memo(({
|
|
@@ -24,33 +23,45 @@ export const PaywallFooter: React.FC<PaywallFooterProps> = React.memo(({
|
|
|
24
23
|
onRestore,
|
|
25
24
|
onLegalClick,
|
|
26
25
|
purchaseButtonText,
|
|
27
|
-
isTablet = false,
|
|
28
|
-
isSmallDevice = false,
|
|
29
26
|
}) => {
|
|
30
27
|
const tokens = useAppDesignTokens();
|
|
28
|
+
const responsive = useResponsive();
|
|
31
29
|
|
|
32
|
-
//
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}, [isTablet, isSmallDevice]);
|
|
30
|
+
// Base values that will be scaled by spacingMultiplier
|
|
31
|
+
const BASE_BUTTON_HEIGHT = 56;
|
|
32
|
+
const BASE_FONT_SIZE = 17;
|
|
33
|
+
const BASE_PADDING_VERTICAL = 16;
|
|
34
|
+
const BASE_SPACING = 12;
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
// Responsive values using spacingMultiplier
|
|
37
|
+
const buttonHeight = React.useMemo(
|
|
38
|
+
() => Math.round(BASE_BUTTON_HEIGHT * responsive.spacingMultiplier),
|
|
39
|
+
[responsive, BASE_BUTTON_HEIGHT]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const fontSize = React.useMemo(
|
|
43
|
+
() => responsive.getFontSize(BASE_FONT_SIZE),
|
|
44
|
+
[responsive, BASE_FONT_SIZE]
|
|
45
|
+
);
|
|
44
46
|
|
|
45
|
-
const paddingVertical = React.useMemo(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
const paddingVertical = React.useMemo(
|
|
48
|
+
() => Math.round(BASE_PADDING_VERTICAL * responsive.spacingMultiplier),
|
|
49
|
+
[responsive, BASE_PADDING_VERTICAL]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const spacing = React.useMemo(
|
|
53
|
+
() => Math.round(BASE_SPACING * responsive.spacingMultiplier),
|
|
54
|
+
[responsive, BASE_SPACING]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const paddingHorizontal = React.useMemo(
|
|
58
|
+
() => responsive.horizontalPadding,
|
|
59
|
+
[responsive]
|
|
60
|
+
);
|
|
50
61
|
|
|
51
62
|
return (
|
|
52
|
-
<View style={footerStyles.container}>
|
|
53
|
-
{/* Purchase Button -
|
|
63
|
+
<View style={[footerStyles.container, { paddingHorizontal, paddingVertical: spacing, gap: spacing }]}>
|
|
64
|
+
{/* Purchase Button - Responsive sizing */}
|
|
54
65
|
{onPurchase && (
|
|
55
66
|
<TouchableOpacity
|
|
56
67
|
onPress={onPurchase}
|
|
@@ -78,22 +89,25 @@ export const PaywallFooter: React.FC<PaywallFooterProps> = React.memo(({
|
|
|
78
89
|
</TouchableOpacity>
|
|
79
90
|
)}
|
|
80
91
|
|
|
81
|
-
{/*
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
{/* Footer Links - Premium inline layout */}
|
|
93
|
+
<View style={[footerStyles.legalContainer, { marginTop: spacing / 2 }]}>
|
|
94
|
+
{onRestore && (
|
|
95
|
+
<TouchableOpacity
|
|
96
|
+
onPress={onRestore}
|
|
97
|
+
disabled={isProcessing}
|
|
98
|
+
activeOpacity={0.6}
|
|
99
|
+
style={footerStyles.restoreButton}
|
|
100
|
+
>
|
|
101
|
+
<AtomicText style={[footerStyles.legalText, { color: tokens.colors.textSecondary, fontWeight: '600' }]}>
|
|
102
|
+
{translations.restoreButtonText}
|
|
103
|
+
</AtomicText>
|
|
104
|
+
</TouchableOpacity>
|
|
105
|
+
)}
|
|
106
|
+
|
|
107
|
+
{(onRestore && legalUrls.termsUrl) && (
|
|
108
|
+
<AtomicText style={footerStyles.legalSeparator}> • </AtomicText>
|
|
109
|
+
)}
|
|
94
110
|
|
|
95
|
-
{/* Legal Links - Responsive spacing */}
|
|
96
|
-
<View style={footerStyles.legalContainer}>
|
|
97
111
|
{legalUrls.termsUrl && (
|
|
98
112
|
<TouchableOpacity
|
|
99
113
|
onPress={() => onLegalClick(legalUrls.termsUrl)}
|
|
@@ -104,6 +118,7 @@ export const PaywallFooter: React.FC<PaywallFooterProps> = React.memo(({
|
|
|
104
118
|
</AtomicText>
|
|
105
119
|
</TouchableOpacity>
|
|
106
120
|
)}
|
|
121
|
+
|
|
107
122
|
{legalUrls.privacyUrl && (
|
|
108
123
|
<>
|
|
109
124
|
<AtomicText style={footerStyles.legalSeparator}> • </AtomicText>
|
|
@@ -126,9 +141,6 @@ export const PaywallFooter: React.FC<PaywallFooterProps> = React.memo(({
|
|
|
126
141
|
const footerStyles = StyleSheet.create({
|
|
127
142
|
container: {
|
|
128
143
|
width: '100%',
|
|
129
|
-
paddingHorizontal: 20,
|
|
130
|
-
paddingVertical: 16,
|
|
131
|
-
gap: 12,
|
|
132
144
|
},
|
|
133
145
|
purchaseButton: {
|
|
134
146
|
width: '100%',
|
|
@@ -8,6 +8,7 @@ import { View } from "react-native";
|
|
|
8
8
|
import type { ImageSourcePropType } from "react-native";
|
|
9
9
|
import { AtomicText, AtomicIcon } from "@umituz/react-native-design-system/atoms";
|
|
10
10
|
import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
|
|
11
|
+
import { useResponsive } from "@umituz/react-native-design-system/responsive";
|
|
11
12
|
import { Image } from "expo-image";
|
|
12
13
|
import { PlanCard } from "./PlanCard";
|
|
13
14
|
import type { PaywallListItem } from "../utils/paywallLayoutUtils";
|
|
@@ -36,6 +37,25 @@ export const PaywallRenderItem: React.FC<PaywallRenderItemProps> = React.memo(({
|
|
|
36
37
|
onSelectPlan,
|
|
37
38
|
}) => {
|
|
38
39
|
const tokens = useAppDesignTokens();
|
|
40
|
+
const responsive = useResponsive();
|
|
41
|
+
|
|
42
|
+
// Responsive spacing
|
|
43
|
+
const spacing = React.useMemo(
|
|
44
|
+
() => Math.round(16 * responsive.spacingMultiplier),
|
|
45
|
+
[responsive]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Responsive feature icon size - use spacing multiplier directly to avoid max/min constraints from getIconSize
|
|
49
|
+
const featureIconSize = React.useMemo(
|
|
50
|
+
() => Math.round(30 * responsive.spacingMultiplier),
|
|
51
|
+
[responsive]
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// Responsive hero image size
|
|
55
|
+
const heroImageSize = React.useMemo(
|
|
56
|
+
() => responsive.getIconSize(120),
|
|
57
|
+
[responsive]
|
|
58
|
+
);
|
|
39
59
|
|
|
40
60
|
if (!translations) return null;
|
|
41
61
|
|
|
@@ -43,10 +63,15 @@ export const PaywallRenderItem: React.FC<PaywallRenderItemProps> = React.memo(({
|
|
|
43
63
|
case 'HEADER':
|
|
44
64
|
return (
|
|
45
65
|
<View key="header">
|
|
46
|
-
{/* Hero Image */}
|
|
66
|
+
{/* Hero Image - Responsive sizing */}
|
|
47
67
|
{heroImage && (
|
|
48
68
|
<View style={styles.heroContainer}>
|
|
49
|
-
<Image
|
|
69
|
+
<Image
|
|
70
|
+
source={heroImage}
|
|
71
|
+
style={[styles.heroImage, { width: heroImageSize, height: heroImageSize, borderRadius: heroImageSize * 0.25 }]}
|
|
72
|
+
contentFit="cover"
|
|
73
|
+
transition={200}
|
|
74
|
+
/>
|
|
50
75
|
</View>
|
|
51
76
|
)}
|
|
52
77
|
|
|
@@ -66,7 +91,7 @@ export const PaywallRenderItem: React.FC<PaywallRenderItemProps> = React.memo(({
|
|
|
66
91
|
|
|
67
92
|
case 'FEATURE_HEADER':
|
|
68
93
|
return (
|
|
69
|
-
<View key="feat-header" style={styles.sectionHeader}>
|
|
94
|
+
<View key="feat-header" style={[styles.sectionHeader, { marginTop: spacing * 1.5 }]}>
|
|
70
95
|
<AtomicText type="labelLarge" style={[styles.sectionTitle, { color: tokens.colors.textSecondary }]}>
|
|
71
96
|
{translations.featuresTitle || "WHAT'S INCLUDED"}
|
|
72
97
|
</AtomicText>
|
|
@@ -75,9 +100,13 @@ export const PaywallRenderItem: React.FC<PaywallRenderItemProps> = React.memo(({
|
|
|
75
100
|
|
|
76
101
|
case 'FEATURE':
|
|
77
102
|
return (
|
|
78
|
-
<View key={`feat-${item.feature.text}`} style={styles.featureRow}>
|
|
79
|
-
<View style={[styles.featureIcon, { backgroundColor: tokens.colors.primary }]}>
|
|
80
|
-
<AtomicIcon
|
|
103
|
+
<View key={`feat-${item.feature.text}`} style={[styles.featureRow, { marginBottom: spacing }]}>
|
|
104
|
+
<View style={[styles.featureIcon, { width: featureIconSize, height: featureIconSize, backgroundColor: tokens.colors.primary }]}>
|
|
105
|
+
<AtomicIcon
|
|
106
|
+
name={item.feature.icon}
|
|
107
|
+
customSize={responsive.getFontSize(16)}
|
|
108
|
+
customColor={tokens.colors.onPrimary}
|
|
109
|
+
/>
|
|
81
110
|
</View>
|
|
82
111
|
<AtomicText type="bodyMedium" style={[styles.featureText, { color: tokens.colors.textPrimary }]}>
|
|
83
112
|
{item.feature.text}
|
|
@@ -87,7 +116,7 @@ export const PaywallRenderItem: React.FC<PaywallRenderItemProps> = React.memo(({
|
|
|
87
116
|
|
|
88
117
|
case 'PLAN_HEADER':
|
|
89
118
|
return (
|
|
90
|
-
<View key="plan-header" style={styles.sectionHeader}>
|
|
119
|
+
<View key="plan-header" style={[styles.sectionHeader, { marginTop: spacing * 1.5 }]}>
|
|
91
120
|
<AtomicText type="labelLarge" style={[styles.sectionTitle, { color: tokens.colors.textSecondary }]}>
|
|
92
121
|
{translations.plansTitle || "CHOOSE YOUR PLAN"}
|
|
93
122
|
</AtomicText>
|
|
@@ -242,8 +242,6 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
242
242
|
onPurchase={handlePurchase}
|
|
243
243
|
onRestore={handleRestore}
|
|
244
244
|
onLegalClick={handleLegalUrl}
|
|
245
|
-
isTablet={responsive.isTabletDevice}
|
|
246
|
-
isSmallDevice={responsive.isSmallDevice}
|
|
247
245
|
/>
|
|
248
246
|
</View>
|
|
249
247
|
</View>
|