@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,162 +0,0 @@
1
- /**
2
- * Paywall Modal Component
3
- * Mode-based paywall: subscription, credits, or hybrid
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 { PaywallHeroHeader } from "./PaywallHeroHeader";
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 { PaywallMode } from "../../../domain/entities/paywall/PaywallMode";
17
- import type { CreditsPackage } from "../../../domain/entities/paywall/CreditsPackage";
18
-
19
- export interface PaywallModalProps {
20
- visible: boolean;
21
- onClose: () => void;
22
- mode: PaywallMode;
23
- initialTab?: PaywallTabType;
24
- creditsPackages?: CreditsPackage[];
25
- subscriptionPackages?: PurchasesPackage[];
26
- currentCredits?: number;
27
- requiredCredits?: number;
28
- onCreditsPurchase?: (packageId: string) => Promise<void>;
29
- onSubscriptionPurchase?: (pkg: PurchasesPackage) => Promise<void>;
30
- onRestore?: () => Promise<void>;
31
- subscriptionFeatures?: Array<{ icon: string; text: string }>;
32
- isLoading?: boolean;
33
- translations: PaywallTranslations;
34
- legalUrls?: PaywallLegalUrls;
35
- }
36
-
37
- export interface PaywallTranslations {
38
- title: string;
39
- subtitle: string;
40
- creditsTabLabel?: string;
41
- subscriptionTabLabel?: string;
42
- purchaseButtonText: string;
43
- subscribeButtonText?: string;
44
- restoreButtonText: string;
45
- loadingText: string;
46
- emptyText: string;
47
- processingText: string;
48
- privacyText?: string;
49
- termsOfServiceText?: string;
50
- }
51
-
52
- export interface PaywallLegalUrls {
53
- privacyUrl?: string;
54
- termsUrl?: string;
55
- }
56
-
57
- export const PaywallModal: React.FC<PaywallModalProps> = React.memo((props) => {
58
- const {
59
- visible,
60
- onClose,
61
- mode,
62
- initialTab = mode === "credits" ? "credits" : "subscription",
63
- creditsPackages = [],
64
- subscriptionPackages = [],
65
- currentCredits = 0,
66
- requiredCredits,
67
- onCreditsPurchase,
68
- onSubscriptionPurchase,
69
- onRestore,
70
- subscriptionFeatures = [],
71
- isLoading = false,
72
- translations,
73
- legalUrls = {},
74
- } = props;
75
-
76
- const {
77
- activeTab,
78
- selectedCreditsPackageId,
79
- selectedSubscriptionPkg,
80
- handleTabChange,
81
- handleCreditsPackageSelect,
82
- handleSubscriptionPackageSelect,
83
- handleCreditsPurchase,
84
- handleSubscriptionPurchase,
85
- } = usePaywall({
86
- initialTab,
87
- onCreditsPurchase: onCreditsPurchase ?? (() => Promise.resolve()),
88
- onSubscriptionPurchase: onSubscriptionPurchase ?? (() => Promise.resolve()),
89
- });
90
-
91
- const showTabs = mode === "hybrid";
92
- const showCredits = mode === "credits" || (mode === "hybrid" && activeTab === "credits");
93
- const showSubscription = mode === "subscription" || (mode === "hybrid" && activeTab === "subscription");
94
-
95
- return (
96
- <BaseModal visible={visible} onClose={onClose}>
97
- <View style={styles.container}>
98
- <PaywallHeroHeader
99
- title={translations.title}
100
- subtitle={translations.subtitle}
101
- onClose={onClose}
102
- />
103
-
104
- {showTabs && (
105
- <PaywallTabBar
106
- activeTab={activeTab}
107
- onTabChange={handleTabChange}
108
- creditsLabel={translations.creditsTabLabel}
109
- subscriptionLabel={translations.subscriptionTabLabel}
110
- />
111
- )}
112
-
113
- <View style={styles.tabContent}>
114
- {showCredits && (
115
- <CreditsTabContent
116
- packages={creditsPackages}
117
- selectedPackageId={selectedCreditsPackageId}
118
- onSelectPackage={handleCreditsPackageSelect}
119
- onPurchase={handleCreditsPurchase}
120
- currentCredits={currentCredits}
121
- requiredCredits={requiredCredits}
122
- isLoading={isLoading}
123
- purchaseButtonText={translations.purchaseButtonText}
124
- />
125
- )}
126
- {showSubscription && (
127
- <SubscriptionTabContent
128
- packages={subscriptionPackages}
129
- selectedPackage={selectedSubscriptionPkg}
130
- onSelectPackage={handleSubscriptionPackageSelect}
131
- onPurchase={handleSubscriptionPurchase}
132
- features={subscriptionFeatures}
133
- isLoading={isLoading}
134
- purchaseButtonText={translations.subscribeButtonText ?? translations.purchaseButtonText}
135
- processingText={translations.processingText}
136
- restoreButtonText={translations.restoreButtonText}
137
- loadingText={translations.loadingText}
138
- emptyText={translations.emptyText}
139
- onRestore={onRestore}
140
- privacyUrl={legalUrls.privacyUrl}
141
- termsUrl={legalUrls.termsUrl}
142
- privacyText={translations.privacyText}
143
- termsOfServiceText={translations.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
- });
@@ -1,120 +0,0 @@
1
- /**
2
- * Paywall Tab Bar Component
3
- * Segmented control for paywall tabs
4
- */
5
-
6
- import React from "react";
7
- import { View, TouchableOpacity, StyleSheet, Animated } from "react-native";
8
- import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
9
- import type { PaywallTabType } from "../../../domain/entities/paywall/PaywallTab";
10
-
11
- interface PaywallTabBarProps {
12
- activeTab: PaywallTabType;
13
- onTabChange: (tab: PaywallTabType) => void;
14
- creditsLabel?: string;
15
- subscriptionLabel?: string;
16
- }
17
-
18
- export const PaywallTabBar: React.FC<PaywallTabBarProps> = React.memo(
19
- ({
20
- activeTab,
21
- onTabChange,
22
- creditsLabel = "Credits",
23
- subscriptionLabel = "Subscription",
24
- }) => {
25
- const tokens = useAppDesignTokens();
26
- const animatedValue = React.useRef(
27
- new Animated.Value(activeTab === "credits" ? 0 : 1)
28
- ).current;
29
-
30
- React.useEffect(() => {
31
- Animated.spring(animatedValue, {
32
- toValue: activeTab === "credits" ? 0 : 1,
33
- useNativeDriver: false,
34
- tension: 68,
35
- friction: 12,
36
- }).start();
37
- }, [activeTab, animatedValue]);
38
-
39
- const renderTab = (tab: PaywallTabType, label: string) => {
40
- const isActive = activeTab === tab;
41
-
42
- return (
43
- <TouchableOpacity
44
- key={tab}
45
- style={styles.tab}
46
- onPress={() => onTabChange(tab)}
47
- activeOpacity={0.7}
48
- >
49
- <AtomicText
50
- type="labelLarge"
51
- style={[
52
- styles.tabText,
53
- {
54
- color: isActive ? tokens.colors.primary : tokens.colors.textSecondary,
55
- },
56
- ]}
57
- >
58
- {label}
59
- </AtomicText>
60
- </TouchableOpacity>
61
- );
62
- };
63
-
64
- const indicatorTranslateX = animatedValue.interpolate({
65
- inputRange: [0, 1],
66
- outputRange: ["0%", "50%"],
67
- });
68
-
69
- return (
70
- <View
71
- style={[
72
- styles.container,
73
- { backgroundColor: tokens.colors.surfaceSecondary },
74
- ]}
75
- >
76
- <Animated.View
77
- style={[
78
- styles.indicator,
79
- {
80
- backgroundColor: tokens.colors.surface,
81
- left: indicatorTranslateX,
82
- },
83
- ]}
84
- />
85
- {renderTab("credits", creditsLabel)}
86
- {renderTab("subscription", subscriptionLabel)}
87
- </View>
88
- );
89
- }
90
- );
91
-
92
- PaywallTabBar.displayName = "PaywallTabBar";
93
-
94
- const styles = StyleSheet.create({
95
- container: {
96
- flexDirection: "row",
97
- borderRadius: 12,
98
- padding: 4,
99
- marginHorizontal: 24,
100
- marginBottom: 16,
101
- position: "relative",
102
- height: 44,
103
- },
104
- indicator: {
105
- position: "absolute",
106
- top: 4,
107
- bottom: 4,
108
- width: "48%",
109
- borderRadius: 8,
110
- },
111
- tab: {
112
- flex: 1,
113
- alignItems: "center",
114
- justifyContent: "center",
115
- zIndex: 1,
116
- },
117
- tabText: {
118
- fontWeight: "600",
119
- },
120
- });
@@ -1,116 +0,0 @@
1
- import React, { useMemo } from "react";
2
- import { View, StyleSheet, TouchableOpacity } from "react-native";
3
- import type { PurchasesPackage } from "react-native-purchases";
4
- import { AtomicText, useAppDesignTokens, useResponsive } from "@umituz/react-native-design-system";
5
- import { PaywallLegalFooter } from "./PaywallLegalFooter";
6
-
7
- interface SubscriptionFooterProps {
8
- isProcessing: boolean;
9
- isLoading: boolean;
10
- processingText: string;
11
- purchaseButtonText: string;
12
- hasPackages: boolean;
13
- selectedPkg: PurchasesPackage | null;
14
- restoreButtonText: string;
15
- showRestoreButton: boolean;
16
- privacyUrl?: string;
17
- termsUrl?: string;
18
- privacyText?: string;
19
- termsOfServiceText?: string;
20
- onPurchase: () => void;
21
- onRestore: () => void;
22
- }
23
-
24
- import { LinearGradient } from "expo-linear-gradient";
25
-
26
- export const SubscriptionFooter: React.FC<SubscriptionFooterProps> = React.memo(
27
- ({
28
- isProcessing,
29
- isLoading,
30
- processingText,
31
- purchaseButtonText,
32
- hasPackages,
33
- selectedPkg,
34
- restoreButtonText,
35
- showRestoreButton,
36
- privacyUrl,
37
- termsUrl,
38
- privacyText,
39
- termsOfServiceText,
40
- onPurchase,
41
- onRestore,
42
- }) => {
43
- const tokens = useAppDesignTokens();
44
- const { spacingMultiplier, getFontSize } = useResponsive();
45
-
46
- const styles = useMemo(() => createStyles(spacingMultiplier), [spacingMultiplier]);
47
- const buttonFontSize = getFontSize(16);
48
-
49
- const isDisabled = !selectedPkg || isProcessing || isLoading;
50
-
51
- return (
52
- <View style={styles.container}>
53
- <View style={styles.actions}>
54
- {hasPackages && (
55
- <TouchableOpacity
56
- onPress={onPurchase}
57
- disabled={isDisabled}
58
- activeOpacity={0.8}
59
- >
60
- <LinearGradient
61
- colors={[tokens.colors.primary, tokens.colors.secondary]}
62
- start={{ x: 0, y: 0 }}
63
- end={{ x: 1, y: 0 }}
64
- style={[styles.gradientButton, isDisabled && { opacity: 0.5 }]}
65
- >
66
- <AtomicText
67
- type="titleSmall"
68
- style={{
69
- color: tokens.colors.onPrimary,
70
- fontWeight: "800",
71
- fontSize: buttonFontSize,
72
- }}
73
- >
74
- {isProcessing ? processingText : purchaseButtonText}
75
- </AtomicText>
76
- </LinearGradient>
77
- </TouchableOpacity>
78
- )}
79
- </View>
80
-
81
- <PaywallLegalFooter
82
- privacyUrl={privacyUrl}
83
- termsUrl={termsUrl}
84
- privacyText={privacyText}
85
- termsOfServiceText={termsOfServiceText}
86
- showRestoreButton={showRestoreButton}
87
- restoreButtonText={restoreButtonText}
88
- onRestore={onRestore}
89
- isProcessing={isProcessing || isLoading}
90
- />
91
- </View>
92
- );
93
- }
94
- );
95
-
96
- SubscriptionFooter.displayName = "SubscriptionFooter";
97
-
98
- const createStyles = (spacingMult: number) =>
99
- StyleSheet.create({
100
- container: {},
101
- actions: {
102
- paddingHorizontal: 24 * spacingMult,
103
- paddingVertical: 16 * spacingMult,
104
- gap: 12 * spacingMult,
105
- },
106
- gradientButton: {
107
- paddingVertical: 16 * spacingMult,
108
- borderRadius: 16 * spacingMult,
109
- alignItems: "center",
110
- justifyContent: "center",
111
- },
112
- restoreButton: {
113
- alignItems: "center",
114
- paddingVertical: 8 * spacingMult,
115
- },
116
- });
@@ -1,168 +0,0 @@
1
- /**
2
- * Subscription Modal Component
3
- * Fullscreen subscription flow using BaseModal from design system
4
- */
5
-
6
- import React, { useMemo } from "react";
7
- import { View, StyleSheet, ScrollView } from "react-native";
8
- import { BaseModal, useResponsive } from "@umituz/react-native-design-system";
9
- import type { PurchasesPackage } from "react-native-purchases";
10
-
11
- import { SubscriptionModalHeader } from "./SubscriptionModalHeader";
12
- import { SubscriptionPackageList } from "./SubscriptionPackageList";
13
- import { SubscriptionFooter } from "./SubscriptionFooter";
14
- import { PaywallFeaturesList } from "./PaywallFeaturesList";
15
- import { useSubscriptionModal } from "../../hooks/useSubscriptionModal";
16
-
17
- export interface SubscriptionModalProps {
18
- visible: boolean;
19
- onClose: () => void;
20
- packages: PurchasesPackage[];
21
- onPurchase: (pkg: PurchasesPackage) => Promise<boolean>;
22
- onRestore: () => Promise<boolean>;
23
- title: string;
24
- subtitle?: string;
25
- isLoading?: boolean;
26
- purchaseButtonText: string;
27
- restoreButtonText: string;
28
- loadingText: string;
29
- emptyText: string;
30
- processingText: string;
31
- privacyUrl?: string;
32
- termsUrl?: string;
33
- privacyText?: string;
34
- termsOfServiceText?: string;
35
- showRestoreButton?: boolean;
36
- /** Optional: Map of product identifier to credit amount */
37
- creditAmounts?: Record<string, number>;
38
- /** Optional: Manually specify which package should show "Best Value" badge */
39
- bestValueIdentifier?: string;
40
- /** Optional: Text labels for accordion details */
41
- billingPeriodLabel?: string;
42
- totalPriceLabel?: string;
43
- perMonthLabel?: string;
44
- /** Optional: List of premium features to display */
45
- features?: Array<{ icon: string; text: string }>;
46
- }
47
-
48
- export const SubscriptionModal: React.FC<SubscriptionModalProps> = React.memo((props) => {
49
- const {
50
- visible,
51
- onClose,
52
- packages,
53
- onPurchase,
54
- onRestore,
55
- title,
56
- subtitle,
57
- isLoading = false,
58
- purchaseButtonText,
59
- restoreButtonText,
60
- loadingText,
61
- emptyText,
62
- processingText,
63
- privacyUrl,
64
- termsUrl,
65
- privacyText,
66
- termsOfServiceText,
67
- showRestoreButton = true,
68
- creditAmounts,
69
- bestValueIdentifier,
70
- billingPeriodLabel,
71
- totalPriceLabel,
72
- perMonthLabel,
73
- features = [],
74
- } = props;
75
-
76
- const {
77
- selectedPkg,
78
- setSelectedPkg,
79
- isProcessing,
80
- handlePurchase,
81
- handleRestore,
82
- } = useSubscriptionModal({
83
- onPurchase,
84
- onRestore,
85
- onClose,
86
- });
87
-
88
- const { spacingMultiplier } = useResponsive();
89
- const styles = useMemo(() => createStyles(spacingMultiplier), [spacingMultiplier]);
90
-
91
- return (
92
- <BaseModal visible={visible} onClose={onClose}>
93
- <View style={styles.container}>
94
- <SubscriptionModalHeader
95
- title={title}
96
- subtitle={subtitle}
97
- onClose={onClose}
98
- />
99
-
100
- <ScrollView
101
- style={styles.scrollView}
102
- contentContainerStyle={styles.scrollContent}
103
- showsVerticalScrollIndicator={false}
104
- bounces={false}
105
- >
106
- {features.length > 0 && (
107
- <PaywallFeaturesList
108
- features={features}
109
- containerStyle={styles.featuresList}
110
- />
111
- )}
112
-
113
- <SubscriptionPackageList
114
- packages={packages}
115
- isLoading={isLoading}
116
- selectedPkg={selectedPkg}
117
- onSelect={setSelectedPkg}
118
- loadingText={loadingText}
119
- emptyText={emptyText}
120
- creditAmounts={creditAmounts}
121
- bestValueIdentifier={bestValueIdentifier}
122
- billingPeriodLabel={billingPeriodLabel}
123
- totalPriceLabel={totalPriceLabel}
124
- perMonthLabel={perMonthLabel}
125
- />
126
- </ScrollView>
127
-
128
- <SubscriptionFooter
129
- isProcessing={isProcessing}
130
- isLoading={isLoading}
131
- processingText={processingText}
132
- purchaseButtonText={purchaseButtonText}
133
- hasPackages={packages.length > 0}
134
- selectedPkg={selectedPkg}
135
- restoreButtonText={restoreButtonText}
136
- showRestoreButton={showRestoreButton}
137
- privacyUrl={privacyUrl}
138
- termsUrl={termsUrl}
139
- privacyText={privacyText}
140
- termsOfServiceText={termsOfServiceText}
141
- onPurchase={handlePurchase}
142
- onRestore={handleRestore}
143
- />
144
- </View>
145
- </BaseModal>
146
- );
147
- });
148
-
149
- SubscriptionModal.displayName = "SubscriptionModal";
150
-
151
- const createStyles = (spacingMult: number) =>
152
- StyleSheet.create({
153
- container: {
154
- flex: 1,
155
- width: "100%",
156
- },
157
- scrollView: {
158
- flex: 1,
159
- },
160
- scrollContent: {
161
- flexGrow: 1,
162
- paddingBottom: 32 * spacingMult,
163
- },
164
- featuresList: {
165
- paddingHorizontal: 24 * spacingMult,
166
- marginBottom: 24 * spacingMult,
167
- },
168
- });
@@ -1,78 +0,0 @@
1
- /**
2
- * Subscription Modal Header Component
3
- */
4
-
5
- import React from "react";
6
- import { View, StyleSheet, TouchableOpacity } from "react-native";
7
- import { AtomicText } from "@umituz/react-native-design-system";
8
- import { useAppDesignTokens } from "@umituz/react-native-design-system";
9
-
10
- interface SubscriptionModalHeaderProps {
11
- title: string;
12
- subtitle?: string;
13
- onClose: () => void;
14
- }
15
-
16
- export const SubscriptionModalHeader: React.FC<SubscriptionModalHeaderProps> = ({
17
- title,
18
- subtitle,
19
- onClose,
20
- }) => {
21
- const tokens = useAppDesignTokens();
22
-
23
- return (
24
- <View style={styles.header}>
25
- <TouchableOpacity
26
- style={styles.closeButton}
27
- onPress={onClose}
28
- testID="subscription-modal-close-button"
29
- >
30
- <AtomicText style={[styles.closeIcon, { color: tokens.colors.textSecondary }]}>
31
- ×
32
- </AtomicText>
33
- </TouchableOpacity>
34
- <AtomicText
35
- type="headlineMedium"
36
- style={[styles.title, { color: tokens.colors.textPrimary }]}
37
- >
38
- {title}
39
- </AtomicText>
40
- {subtitle && (
41
- <AtomicText
42
- type="bodyMedium"
43
- style={[styles.subtitle, { color: tokens.colors.textSecondary }]}
44
- >
45
- {subtitle}
46
- </AtomicText>
47
- )}
48
- </View>
49
- );
50
- };
51
-
52
- const styles = StyleSheet.create({
53
- header: {
54
- alignItems: "center",
55
- paddingHorizontal: 24,
56
- paddingTop: 16,
57
- paddingBottom: 16,
58
- },
59
- closeButton: {
60
- position: "absolute",
61
- top: 8,
62
- right: 16,
63
- padding: 8,
64
- zIndex: 1,
65
- },
66
- closeIcon: {
67
- fontSize: 28,
68
- fontWeight: "300",
69
- },
70
- title: {
71
- marginBottom: 8,
72
- textAlign: "center",
73
- },
74
- subtitle: {
75
- textAlign: "center",
76
- paddingHorizontal: 20,
77
- },
78
- });