@umituz/react-native-subscription 3.1.27 → 3.1.28
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.28",
|
|
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
|
+
);
|
|
46
|
+
|
|
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
|
+
);
|
|
44
56
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}, [isTablet, isSmallDevice]);
|
|
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,13 +89,13 @@ export const PaywallFooter: React.FC<PaywallFooterProps> = React.memo(({
|
|
|
78
89
|
</TouchableOpacity>
|
|
79
90
|
)}
|
|
80
91
|
|
|
81
|
-
{/* Restore Link -
|
|
92
|
+
{/* Restore Link - Responsive spacing */}
|
|
82
93
|
{onRestore && (
|
|
83
94
|
<TouchableOpacity
|
|
84
95
|
onPress={onRestore}
|
|
85
96
|
disabled={isProcessing}
|
|
86
97
|
activeOpacity={0.6}
|
|
87
|
-
style={[footerStyles.restoreButton, { marginTop:
|
|
98
|
+
style={[footerStyles.restoreButton, { marginTop: spacing / 2 }]}
|
|
88
99
|
>
|
|
89
100
|
<AtomicText style={[footerStyles.restoreText, { color: tokens.colors.textSecondary }]}>
|
|
90
101
|
{translations.restoreButtonText}
|
|
@@ -126,9 +137,6 @@ export const PaywallFooter: React.FC<PaywallFooterProps> = React.memo(({
|
|
|
126
137
|
const footerStyles = StyleSheet.create({
|
|
127
138
|
container: {
|
|
128
139
|
width: '100%',
|
|
129
|
-
paddingHorizontal: 20,
|
|
130
|
-
paddingVertical: 16,
|
|
131
|
-
gap: 12,
|
|
132
140
|
},
|
|
133
141
|
purchaseButton: {
|
|
134
142
|
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
|
|
49
|
+
const featureIconSize = React.useMemo(
|
|
50
|
+
() => responsive.getIconSize(30),
|
|
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>
|