@umituz/react-native-subscription 2.12.2 → 2.12.3

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.
Files changed (46) hide show
  1. package/package.json +1 -1
  2. package/src/domains/index.ts +5 -0
  3. package/src/domains/paywall/components/CreditCard.tsx +117 -0
  4. package/src/domains/paywall/components/FeatureItem.tsx +50 -0
  5. package/src/domains/paywall/components/FeatureList.tsx +34 -0
  6. package/src/domains/paywall/components/PaywallFooter.tsx +98 -0
  7. package/src/{presentation/components/paywall/PaywallHeroHeader.tsx → domains/paywall/components/PaywallHeader.tsx} +15 -44
  8. package/src/domains/paywall/components/PaywallModal.tsx +187 -0
  9. package/src/domains/paywall/components/PaywallTabBar.tsx +102 -0
  10. package/src/domains/paywall/components/PlanCard.tsx +124 -0
  11. package/src/domains/paywall/components/index.ts +14 -0
  12. package/src/domains/paywall/entities/index.ts +5 -0
  13. package/src/domains/paywall/entities/types.ts +48 -0
  14. package/src/domains/paywall/hooks/index.ts +6 -0
  15. package/src/{presentation → domains/paywall}/hooks/usePaywall.ts +1 -1
  16. package/src/domains/paywall/index.ts +13 -0
  17. package/src/index.ts +15 -22
  18. package/src/domain/entities/paywall/CreditsPackage.ts +0 -16
  19. package/src/domain/entities/paywall/PaywallMode.ts +0 -6
  20. package/src/domain/entities/paywall/PaywallTab.ts +0 -11
  21. package/src/domain/entities/paywall/SubscriptionPlan.ts +0 -27
  22. package/src/presentation/components/paywall/BestValueBadge.tsx +0 -59
  23. package/src/presentation/components/paywall/CreditsPackageCard.tsx +0 -161
  24. package/src/presentation/components/paywall/CreditsTabContent.tsx +0 -123
  25. package/src/presentation/components/paywall/PaywallFeatureItem.tsx +0 -79
  26. package/src/presentation/components/paywall/PaywallFeaturesList.tsx +0 -47
  27. package/src/presentation/components/paywall/PaywallHeader.tsx +0 -82
  28. package/src/presentation/components/paywall/PaywallLegalFooter.tsx +0 -145
  29. package/src/presentation/components/paywall/PaywallLegalFooterStyles.ts +0 -53
  30. package/src/presentation/components/paywall/PaywallLegalFooterTypes.ts +0 -19
  31. package/src/presentation/components/paywall/PaywallModal.tsx +0 -162
  32. package/src/presentation/components/paywall/PaywallTabBar.tsx +0 -120
  33. package/src/presentation/components/paywall/SubscriptionFooter.tsx +0 -116
  34. package/src/presentation/components/paywall/SubscriptionModal.tsx +0 -168
  35. package/src/presentation/components/paywall/SubscriptionModalHeader.tsx +0 -78
  36. package/src/presentation/components/paywall/SubscriptionPackageList.tsx +0 -171
  37. package/src/presentation/components/paywall/SubscriptionPlanCard.tsx +0 -213
  38. package/src/presentation/components/paywall/SubscriptionPlanCardStyles.ts +0 -61
  39. package/src/presentation/components/paywall/SubscriptionPlanCardTypes.ts +0 -15
  40. package/src/presentation/components/paywall/SubscriptionTabContent.tsx +0 -139
  41. package/src/presentation/components/paywall/accordion/AccordionPlanCard.tsx +0 -98
  42. package/src/presentation/components/paywall/accordion/AccordionPlanCardTypes.ts +0 -39
  43. package/src/presentation/components/paywall/accordion/PlanCardDetails.tsx +0 -107
  44. package/src/presentation/components/paywall/accordion/PlanCardHeader.tsx +0 -155
  45. package/src/presentation/components/paywall/accordion/index.ts +0 -12
  46. /package/src/{presentation → domains/paywall}/hooks/useSubscriptionModal.ts +0 -0
@@ -1,123 +0,0 @@
1
- /**
2
- * Credits Tab Content Component
3
- * Single Responsibility: Display credits packages list
4
- */
5
-
6
- import React from "react";
7
- import { View, StyleSheet, ScrollView } from "react-native";
8
- import { AtomicText, AtomicButton, Grid, useResponsiveDesignTokens } from "@umituz/react-native-design-system";
9
- import { useLocalization } from "@umituz/react-native-localization";
10
- import { CreditsPackageCard } from "./CreditsPackageCard";
11
- import { PaywallLegalFooter } from "./PaywallLegalFooter";
12
- import type { CreditsPackage } from "../../../domain/entities/paywall/CreditsPackage";
13
-
14
- interface CreditsTabContentProps {
15
- packages: CreditsPackage[];
16
- selectedPackageId: string | null;
17
- onSelectPackage: (packageId: string) => void;
18
- onPurchase: () => void;
19
- currentCredits: number;
20
- requiredCredits?: number;
21
- isLoading?: boolean;
22
- purchaseButtonText?: string;
23
- creditsInfoText?: string;
24
- processingText?: string;
25
- }
26
-
27
- export const CreditsTabContent: React.FC<CreditsTabContentProps> = React.memo(
28
- ({
29
- packages,
30
- selectedPackageId,
31
- onSelectPackage,
32
- onPurchase,
33
- currentCredits,
34
- requiredCredits,
35
- isLoading = false,
36
- purchaseButtonText,
37
- creditsInfoText,
38
- processingText,
39
- }) => {
40
- const tokens = useResponsiveDesignTokens();
41
- const { t } = useLocalization();
42
-
43
- const needsCredits = requiredCredits && requiredCredits > currentCredits;
44
-
45
- const displayPurchaseButtonText = purchaseButtonText ||
46
- t("paywall.purchase");
47
- const displayProcessingText = processingText ||
48
- t("paywall.processing");
49
- const displayCreditsInfoText = creditsInfoText ||
50
- t("paywall.creditsInfo");
51
-
52
- return (
53
- <View style={styles.container}>
54
- {needsCredits && (
55
- <View
56
- style={[
57
- styles.infoCard,
58
- { backgroundColor: tokens.colors.surfaceSecondary },
59
- ]}
60
- >
61
- <AtomicText
62
- type="bodyMedium"
63
- style={{ color: tokens.colors.textSecondary }}
64
- >
65
- {displayCreditsInfoText
66
- .replace("{{required}}", String(requiredCredits))
67
- .replace("{{current}}", String(currentCredits))}
68
- </AtomicText>
69
- </View>
70
- )}
71
-
72
- <ScrollView
73
- style={styles.scrollView}
74
- contentContainerStyle={[styles.scrollContent, { paddingHorizontal: tokens.spacing.lg, paddingBottom: tokens.spacing.md }]}
75
- showsVerticalScrollIndicator={false}
76
- >
77
- <Grid mobileColumns={1} tabletColumns={2} gap={tokens.spacing.sm}>
78
- {packages.map((pkg) => (
79
- <CreditsPackageCard
80
- key={pkg.id}
81
- package={pkg}
82
- isSelected={selectedPackageId === pkg.id}
83
- onSelect={() => onSelectPackage(pkg.id)}
84
- />
85
- ))}
86
- </Grid>
87
- </ScrollView>
88
-
89
- <View style={styles.footer}>
90
- <AtomicButton
91
- title={isLoading ? displayProcessingText : displayPurchaseButtonText}
92
- onPress={onPurchase}
93
- disabled={!selectedPackageId || isLoading}
94
- />
95
- </View>
96
-
97
- <PaywallLegalFooter />
98
- </View>
99
- );
100
- },
101
- );
102
-
103
- CreditsTabContent.displayName = "CreditsTabContent";
104
-
105
- const styles = StyleSheet.create({
106
- container: {
107
- flex: 1,
108
- },
109
- infoCard: {
110
- marginHorizontal: 24,
111
- marginBottom: 16,
112
- padding: 12,
113
- borderRadius: 12,
114
- },
115
- scrollView: {
116
- flex: 1,
117
- },
118
- scrollContent: {},
119
- footer: {
120
- padding: 24,
121
- paddingTop: 16,
122
- },
123
- });
@@ -1,79 +0,0 @@
1
- /**
2
- * Paywall Feature Item Component
3
- * Single feature in the features list
4
- */
5
-
6
- import React, { useMemo } from "react";
7
- import { View, StyleSheet } from "react-native";
8
- import {
9
- AtomicText,
10
- AtomicIcon,
11
- useAppDesignTokens,
12
- useResponsive,
13
- } from "@umituz/react-native-design-system";
14
-
15
- interface PaywallFeatureItemProps {
16
- icon: string;
17
- text: string;
18
- }
19
-
20
- export const PaywallFeatureItem: React.FC<PaywallFeatureItemProps> = React.memo(
21
- ({ icon, text }) => {
22
- const tokens = useAppDesignTokens();
23
- const { spacingMultiplier, getFontSize } = useResponsive();
24
-
25
- const styles = useMemo(() => createStyles(spacingMultiplier), [spacingMultiplier]);
26
- const fontSize = getFontSize(15);
27
- const iconSize = getFontSize(18);
28
-
29
- const iconName = useMemo(() => {
30
- if (!icon) return "checkmark-circle";
31
- return icon;
32
- }, [icon]);
33
-
34
- return (
35
- <View style={styles.featureItem}>
36
- <View
37
- style={[
38
- styles.iconContainer,
39
- { backgroundColor: tokens.colors.primaryLight },
40
- ]}
41
- >
42
- <AtomicIcon
43
- name={iconName}
44
- customSize={iconSize}
45
- customColor={tokens.colors.primary}
46
- />
47
- </View>
48
- <AtomicText
49
- type="bodyMedium"
50
- style={[styles.featureText, { color: tokens.colors.textPrimary, fontSize }]}
51
- >
52
- {text}
53
- </AtomicText>
54
- </View>
55
- );
56
- }
57
- );
58
-
59
- PaywallFeatureItem.displayName = "PaywallFeatureItem";
60
-
61
- const createStyles = (spacingMult: number) =>
62
- StyleSheet.create({
63
- featureItem: {
64
- flexDirection: "row",
65
- alignItems: "center",
66
- },
67
- iconContainer: {
68
- width: 32 * spacingMult,
69
- height: 32 * spacingMult,
70
- borderRadius: 16 * spacingMult,
71
- justifyContent: "center",
72
- alignItems: "center",
73
- marginRight: 12 * spacingMult,
74
- },
75
- featureText: {
76
- flex: 1,
77
- fontWeight: "500",
78
- },
79
- });
@@ -1,47 +0,0 @@
1
- /**
2
- * Paywall Features List Component
3
- * Displays premium features list
4
- */
5
-
6
- import React from "react";
7
- import { View, StyleSheet } from "react-native";
8
- import { useResponsive } from "@umituz/react-native-design-system";
9
- import { PaywallFeatureItem } from "./PaywallFeatureItem";
10
-
11
- interface PaywallFeaturesListProps {
12
- /** Features list */
13
- features: Array<{ icon: string; text: string }>;
14
- /** Optional custom container style */
15
- containerStyle?: object;
16
- /** Optional gap between items (default: 12) */
17
- gap?: number;
18
- }
19
-
20
- export const PaywallFeaturesList: React.FC<PaywallFeaturesListProps> = React.memo(
21
- ({ features, containerStyle, gap = 12 }) => {
22
- const { spacingMultiplier } = useResponsive();
23
- const responsiveGap = gap * spacingMultiplier;
24
-
25
- return (
26
- <View style={[styles.container, containerStyle]}>
27
- {features.map((feature, index) => (
28
- <View
29
- key={`${feature.icon}-${feature.text}-${index}`}
30
- style={{ marginBottom: index < features.length - 1 ? responsiveGap : 0 }}
31
- >
32
- <PaywallFeatureItem icon={feature.icon} text={feature.text} />
33
- </View>
34
- ))}
35
- </View>
36
- );
37
- },
38
- );
39
-
40
- PaywallFeaturesList.displayName = "PaywallFeaturesList";
41
-
42
- const styles = StyleSheet.create({
43
- container: {
44
- width: "100%",
45
- },
46
- });
47
-
@@ -1,82 +0,0 @@
1
- /**
2
- * Paywall Header Component
3
- * Single Responsibility: Display paywall header with close button
4
- */
5
-
6
- import React from "react";
7
- import { View, TouchableOpacity, StyleSheet } from "react-native";
8
- import { AtomicText, AtomicIcon } from "@umituz/react-native-design-system";
9
- import { useAppDesignTokens } from "@umituz/react-native-design-system";
10
-
11
- interface PaywallHeaderProps {
12
- title: string;
13
- subtitle?: string;
14
- onClose: () => void;
15
- }
16
-
17
- export const PaywallHeader: React.FC<PaywallHeaderProps> = React.memo(
18
- ({ title, subtitle, onClose }) => {
19
- const tokens = useAppDesignTokens();
20
-
21
- return (
22
- <View style={styles.container}>
23
- <View style={styles.titleContainer}>
24
- <AtomicText
25
- type="headlineLarge"
26
- style={[styles.title, { color: tokens.colors.textPrimary }]}
27
- >
28
- {title}
29
- </AtomicText>
30
- {subtitle && (
31
- <AtomicText
32
- type="bodyMedium"
33
- style={[styles.subtitle, { color: tokens.colors.textSecondary }]}
34
- >
35
- {subtitle}
36
- </AtomicText>
37
- )}
38
- </View>
39
- <TouchableOpacity
40
- onPress={onClose}
41
- style={[
42
- styles.closeButton,
43
- { backgroundColor: tokens.colors.surfaceSecondary },
44
- ]}
45
- hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
46
- >
47
- <AtomicIcon name="X" size="md" color="secondary" />
48
- </TouchableOpacity>
49
- </View>
50
- );
51
- },
52
- );
53
-
54
- PaywallHeader.displayName = "PaywallHeader";
55
-
56
- const styles = StyleSheet.create({
57
- container: {
58
- flexDirection: "row",
59
- justifyContent: "space-between",
60
- alignItems: "flex-start",
61
- paddingHorizontal: 24,
62
- paddingTop: 48,
63
- paddingBottom: 16,
64
- },
65
- titleContainer: {
66
- flex: 1,
67
- marginRight: 16,
68
- },
69
- title: {
70
- fontWeight: "700",
71
- },
72
- subtitle: {
73
- marginTop: 4,
74
- },
75
- closeButton: {
76
- width: 32,
77
- height: 32,
78
- borderRadius: 16,
79
- justifyContent: "center",
80
- alignItems: "center",
81
- },
82
- });
@@ -1,145 +0,0 @@
1
- /**
2
- * Paywall Legal Footer Component
3
- * Display legal links and terms for App Store compliance
4
- */
5
-
6
- import React from "react";
7
- import { View, TouchableOpacity, Linking } from "react-native";
8
- import { AtomicText } from "@umituz/react-native-design-system";
9
- import { useAppDesignTokens } from "@umituz/react-native-design-system";
10
- import type { PaywallLegalFooterProps } from "./PaywallLegalFooterTypes";
11
- import { DEFAULT_TERMS } from "./PaywallLegalFooterTypes";
12
- import { styles } from "./PaywallLegalFooterStyles";
13
-
14
- export type { PaywallLegalFooterProps } from "./PaywallLegalFooterTypes";
15
-
16
- export const PaywallLegalFooter: React.FC<PaywallLegalFooterProps> = React.memo(
17
- ({
18
- termsText = DEFAULT_TERMS,
19
- privacyUrl,
20
- termsUrl,
21
- privacyText = "Privacy Policy",
22
- termsOfServiceText = "Terms of Service",
23
- showRestoreButton = false,
24
- restoreButtonText = "Restore Purchases",
25
- onRestore,
26
- isProcessing = false,
27
- }) => {
28
- const tokens = useAppDesignTokens();
29
-
30
- const handlePrivacyPress = () => {
31
- if (privacyUrl) {
32
- Linking.openURL(privacyUrl).catch(() => {});
33
- }
34
- };
35
-
36
- const handleTermsPress = () => {
37
- if (termsUrl) {
38
- Linking.openURL(termsUrl).catch(() => {});
39
- }
40
- };
41
-
42
- const hasLinks = privacyUrl || termsUrl || showRestoreButton;
43
-
44
- return (
45
- <View style={styles.container}>
46
- <AtomicText
47
- type="labelSmall"
48
- style={[styles.termsText, { color: tokens.colors.textTertiary }]}
49
- >
50
- {termsText}
51
- </AtomicText>
52
-
53
- {hasLinks && (
54
- <View style={styles.legalLinksWrapper}>
55
- <View
56
- style={[
57
- styles.legalLinksContainer,
58
- { borderColor: tokens.colors.border },
59
- ]}
60
- >
61
- {showRestoreButton && (
62
- <>
63
- <TouchableOpacity
64
- onPress={onRestore}
65
- activeOpacity={0.6}
66
- style={styles.linkItem}
67
- disabled={isProcessing}
68
- >
69
- <AtomicText
70
- type="labelSmall"
71
- style={[
72
- styles.linkText,
73
- {
74
- color: tokens.colors.textSecondary,
75
- },
76
- ]}
77
- >
78
- {restoreButtonText}
79
- </AtomicText>
80
- </TouchableOpacity>
81
- {(privacyUrl || termsUrl) && (
82
- <View
83
- style={[
84
- styles.dot,
85
- { backgroundColor: tokens.colors.border },
86
- ]}
87
- />
88
- )}
89
- </>
90
- )}
91
-
92
- {privacyUrl && (
93
- <>
94
- <TouchableOpacity
95
- onPress={handlePrivacyPress}
96
- activeOpacity={0.6}
97
- style={styles.linkItem}
98
- >
99
- <AtomicText
100
- type="labelSmall"
101
- style={[
102
- styles.linkText,
103
- { color: tokens.colors.textSecondary },
104
- ]}
105
- >
106
- {privacyText}
107
- </AtomicText>
108
- </TouchableOpacity>
109
- {termsUrl && (
110
- <View
111
- style={[
112
- styles.dot,
113
- { backgroundColor: tokens.colors.border },
114
- ]}
115
- />
116
- )}
117
- </>
118
- )}
119
-
120
- {termsUrl && (
121
- <TouchableOpacity
122
- onPress={handleTermsPress}
123
- activeOpacity={0.6}
124
- style={styles.linkItem}
125
- >
126
- <AtomicText
127
- type="labelSmall"
128
- style={[
129
- styles.linkText,
130
- { color: tokens.colors.textSecondary },
131
- ]}
132
- >
133
- {termsOfServiceText}
134
- </AtomicText>
135
- </TouchableOpacity>
136
- )}
137
- </View>
138
- </View>
139
- )}
140
- </View>
141
- );
142
- }
143
- );
144
-
145
- PaywallLegalFooter.displayName = "PaywallLegalFooter";
@@ -1,53 +0,0 @@
1
- /**
2
- * Paywall Legal Footer Styles
3
- * StyleSheet definitions for paywall legal footer
4
- */
5
-
6
- import { StyleSheet } from "react-native";
7
-
8
- export const styles = StyleSheet.create({
9
- container: {
10
- alignItems: "center",
11
- paddingHorizontal: 24,
12
- paddingBottom: 24,
13
- paddingTop: 8,
14
- width: "100%",
15
- },
16
- termsText: {
17
- textAlign: "center",
18
- fontSize: 10,
19
- lineHeight: 14,
20
- marginBottom: 16,
21
- opacity: 0.7,
22
- },
23
- legalLinksWrapper: {
24
- width: "100%",
25
- alignItems: "center",
26
- },
27
- legalLinksContainer: {
28
- flexDirection: "row",
29
- alignItems: "center",
30
- justifyContent: "center",
31
- paddingVertical: 8,
32
- paddingHorizontal: 16,
33
- borderRadius: 20,
34
- backgroundColor: "rgba(255, 255, 255, 0.03)",
35
- borderWidth: 1,
36
- borderColor: "rgba(255, 255, 255, 0.05)",
37
- },
38
- linkItem: {
39
- paddingVertical: 2,
40
- },
41
- linkText: {
42
- fontSize: 11,
43
- fontWeight: "500",
44
- letterSpacing: 0.3,
45
- },
46
- dot: {
47
- width: 3,
48
- height: 3,
49
- borderRadius: 1.5,
50
- marginHorizontal: 12,
51
- opacity: 0.3,
52
- },
53
- });
@@ -1,19 +0,0 @@
1
- /**
2
- * Paywall Legal Footer Types
3
- * Type definitions for paywall legal footer
4
- */
5
-
6
- export interface PaywallLegalFooterProps {
7
- termsText?: string;
8
- privacyUrl?: string;
9
- termsUrl?: string;
10
- privacyText?: string;
11
- termsOfServiceText?: string;
12
- showRestoreButton?: boolean;
13
- restoreButtonText?: string;
14
- onRestore?: () => void;
15
- isProcessing?: boolean;
16
- }
17
-
18
- export const DEFAULT_TERMS =
19
- "Payment will be charged to your account. Subscription automatically renews unless cancelled.";