@umituz/react-native-subscription 2.12.1 → 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 (45) hide show
  1. package/package.json +3 -3
  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/domains/paywall/components/PaywallHeader.tsx +115 -0
  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 +17 -21
  18. package/src/domain/entities/paywall/CreditsPackage.ts +0 -16
  19. package/src/domain/entities/paywall/PaywallTab.ts +0 -11
  20. package/src/domain/entities/paywall/SubscriptionPlan.ts +0 -27
  21. package/src/presentation/components/paywall/BestValueBadge.tsx +0 -59
  22. package/src/presentation/components/paywall/CreditsPackageCard.tsx +0 -129
  23. package/src/presentation/components/paywall/CreditsTabContent.tsx +0 -123
  24. package/src/presentation/components/paywall/PaywallFeatureItem.tsx +0 -65
  25. package/src/presentation/components/paywall/PaywallFeaturesList.tsx +0 -47
  26. package/src/presentation/components/paywall/PaywallHeader.tsx +0 -82
  27. package/src/presentation/components/paywall/PaywallLegalFooter.tsx +0 -145
  28. package/src/presentation/components/paywall/PaywallLegalFooterStyles.ts +0 -53
  29. package/src/presentation/components/paywall/PaywallLegalFooterTypes.ts +0 -19
  30. package/src/presentation/components/paywall/PaywallModal.tsx +0 -162
  31. package/src/presentation/components/paywall/PaywallTabBar.tsx +0 -96
  32. package/src/presentation/components/paywall/SubscriptionFooter.tsx +0 -116
  33. package/src/presentation/components/paywall/SubscriptionModal.tsx +0 -168
  34. package/src/presentation/components/paywall/SubscriptionModalHeader.tsx +0 -78
  35. package/src/presentation/components/paywall/SubscriptionPackageList.tsx +0 -171
  36. package/src/presentation/components/paywall/SubscriptionPlanCard.tsx +0 -213
  37. package/src/presentation/components/paywall/SubscriptionPlanCardStyles.ts +0 -61
  38. package/src/presentation/components/paywall/SubscriptionPlanCardTypes.ts +0 -15
  39. package/src/presentation/components/paywall/SubscriptionTabContent.tsx +0 -139
  40. package/src/presentation/components/paywall/accordion/AccordionPlanCard.tsx +0 -100
  41. package/src/presentation/components/paywall/accordion/AccordionPlanCardTypes.ts +0 -39
  42. package/src/presentation/components/paywall/accordion/PlanCardDetails.tsx +0 -107
  43. package/src/presentation/components/paywall/accordion/PlanCardHeader.tsx +0 -165
  44. package/src/presentation/components/paywall/accordion/index.ts +0 -12
  45. /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,65 +0,0 @@
1
- /**
2
- * Paywall Feature Item Component
3
- * Single Responsibility: Display a single feature in the features list
4
- */
5
-
6
- import React, { useMemo } from "react";
7
- import { View, StyleSheet } from "react-native";
8
- import { AtomicText, AtomicIcon, useAppDesignTokens, useResponsive } from "@umituz/react-native-design-system";
9
-
10
- interface PaywallFeatureItemProps {
11
- icon: string;
12
- text: string;
13
- }
14
-
15
- export const PaywallFeatureItem: React.FC<PaywallFeatureItemProps> = React.memo(
16
- ({ icon, text }) => {
17
- const tokens = useAppDesignTokens();
18
- const { spacingMultiplier, getFontSize } = useResponsive();
19
-
20
- const styles = useMemo(() => createStyles(spacingMultiplier), [spacingMultiplier]);
21
- const fontSize = getFontSize(15);
22
- const lineHeight = getFontSize(22);
23
- const iconSize = getFontSize(20);
24
-
25
- // Pass icon name directly to AtomicIcon (which uses Ionicons)
26
- // Do NOT capitalize, as Ionicons names are lowercase/kebab-case.
27
- const iconName = useMemo(() => {
28
- if (!icon) return "help-circle-outline";
29
- return icon;
30
- }, [icon]);
31
-
32
- return (
33
- <View style={styles.featureItem}>
34
- <AtomicIcon
35
- name={iconName}
36
- customSize={iconSize}
37
- customColor={tokens.colors.primary}
38
- style={styles.featureIcon}
39
- />
40
- <AtomicText
41
- type="bodyMedium"
42
- style={[styles.featureText, { color: tokens.colors.textPrimary, fontSize, lineHeight }]}
43
- >
44
- {text}
45
- </AtomicText>
46
- </View>
47
- );
48
- },
49
- );
50
-
51
- PaywallFeatureItem.displayName = "PaywallFeatureItem";
52
-
53
- const createStyles = (spacingMult: number) =>
54
- StyleSheet.create({
55
- featureItem: {
56
- flexDirection: "row",
57
- alignItems: "center",
58
- },
59
- featureIcon: {
60
- marginRight: 12 * spacingMult,
61
- },
62
- featureText: {
63
- flex: 1,
64
- },
65
- });
@@ -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.";
@@ -1,162 +0,0 @@
1
- /**
2
- * Paywall Modal Component
3
- * Displays paywall with Credits and Subscription tabs
4
- */
5
-
6
- import React from "react";
7
- import { View, StyleSheet } from "react-native";
8
- import { BaseModal } from "@umituz/react-native-design-system";
9
- import type { PurchasesPackage } from "react-native-purchases";
10
- import { usePaywall } from "../../hooks/usePaywall";
11
- import { PaywallHeader } from "./PaywallHeader";
12
- import { PaywallTabBar } from "./PaywallTabBar";
13
- import { CreditsTabContent } from "./CreditsTabContent";
14
- import { SubscriptionTabContent } from "./SubscriptionTabContent";
15
- import type { PaywallTabType } from "../../../domain/entities/paywall/PaywallTab";
16
- import type { CreditsPackage } from "../../../domain/entities/paywall/CreditsPackage";
17
-
18
- export interface PaywallModalStyles {
19
- headerTopPadding?: number;
20
- contentHorizontalPadding?: number;
21
- contentBottomPadding?: number;
22
- }
23
-
24
- export interface PaywallModalProps {
25
- visible: boolean;
26
- onClose: () => void;
27
- initialTab?: PaywallTabType;
28
- creditsPackages: CreditsPackage[];
29
- subscriptionPackages: PurchasesPackage[];
30
- currentCredits: number;
31
- requiredCredits?: number;
32
- onCreditsPurchase: (packageId: string) => Promise<void>;
33
- onSubscriptionPurchase: (pkg: PurchasesPackage) => Promise<void>;
34
- onRestore?: () => Promise<void>;
35
- subscriptionFeatures?: Array<{ icon: string; text: string }>;
36
- isLoading?: boolean;
37
- title: string;
38
- subtitle: string;
39
- creditsTabLabel: string;
40
- subscriptionTabLabel: string;
41
- purchaseButtonText: string;
42
- subscribeButtonText: string;
43
- restoreButtonText: string;
44
- loadingText: string;
45
- emptyText: string;
46
- processingText: string;
47
- privacyUrl?: string;
48
- termsUrl?: string;
49
- privacyText?: string;
50
- termsOfServiceText?: string;
51
- }
52
-
53
- export const PaywallModal: React.FC<PaywallModalProps> = React.memo((props) => {
54
- const {
55
- visible,
56
- onClose,
57
- initialTab = "credits",
58
- creditsPackages,
59
- subscriptionPackages,
60
- currentCredits,
61
- requiredCredits,
62
- onCreditsPurchase,
63
- onSubscriptionPurchase,
64
- onRestore,
65
- subscriptionFeatures = [],
66
- isLoading = false,
67
- title,
68
- subtitle,
69
- creditsTabLabel,
70
- subscriptionTabLabel,
71
- purchaseButtonText,
72
- subscribeButtonText,
73
- privacyUrl,
74
- termsUrl,
75
- privacyText,
76
- termsOfServiceText,
77
- restoreButtonText,
78
- loadingText,
79
- emptyText,
80
- processingText,
81
- } = props;
82
-
83
- const {
84
- activeTab,
85
- selectedCreditsPackageId,
86
- selectedSubscriptionPkg,
87
- handleTabChange,
88
- handleCreditsPackageSelect,
89
- handleSubscriptionPackageSelect,
90
- handleCreditsPurchase,
91
- handleSubscriptionPurchase,
92
- } = usePaywall({
93
- initialTab,
94
- onCreditsPurchase,
95
- onSubscriptionPurchase,
96
- });
97
-
98
- return (
99
- <BaseModal visible={visible} onClose={onClose}>
100
- <View style={styles.container}>
101
- <PaywallHeader
102
- title={title}
103
- subtitle={subtitle}
104
- onClose={onClose}
105
- />
106
-
107
- <PaywallTabBar
108
- activeTab={activeTab}
109
- onTabChange={handleTabChange}
110
- creditsLabel={creditsTabLabel}
111
- subscriptionLabel={subscriptionTabLabel}
112
- />
113
-
114
- <View style={styles.tabContent}>
115
- {activeTab === "credits" ? (
116
- <CreditsTabContent
117
- packages={creditsPackages}
118
- selectedPackageId={selectedCreditsPackageId}
119
- onSelectPackage={handleCreditsPackageSelect}
120
- onPurchase={handleCreditsPurchase}
121
- currentCredits={currentCredits}
122
- requiredCredits={requiredCredits}
123
- isLoading={isLoading}
124
- purchaseButtonText={purchaseButtonText}
125
- />
126
- ) : (
127
- <SubscriptionTabContent
128
- packages={subscriptionPackages}
129
- selectedPackage={selectedSubscriptionPkg}
130
- onSelectPackage={handleSubscriptionPackageSelect}
131
- onPurchase={handleSubscriptionPurchase}
132
- features={subscriptionFeatures}
133
- isLoading={isLoading}
134
- purchaseButtonText={subscribeButtonText}
135
- processingText={processingText}
136
- restoreButtonText={restoreButtonText}
137
- loadingText={loadingText}
138
- emptyText={emptyText}
139
- onRestore={onRestore}
140
- privacyUrl={privacyUrl}
141
- termsUrl={termsUrl}
142
- privacyText={privacyText}
143
- termsOfServiceText={termsOfServiceText}
144
- />
145
- )}
146
- </View>
147
- </View>
148
- </BaseModal>
149
- );
150
- });
151
-
152
- PaywallModal.displayName = "PaywallModal";
153
-
154
- const styles = StyleSheet.create({
155
- container: {
156
- flex: 1,
157
- width: "100%",
158
- },
159
- tabContent: {
160
- flex: 1,
161
- },
162
- });