@umituz/react-native-subscription 2.12.2 → 2.12.4

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 (47) hide show
  1. package/package.json +2 -2
  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/presentation/components/details/PremiumDetailsCard.tsx +35 -11
  19. package/src/domain/entities/paywall/CreditsPackage.ts +0 -16
  20. package/src/domain/entities/paywall/PaywallMode.ts +0 -6
  21. package/src/domain/entities/paywall/PaywallTab.ts +0 -11
  22. package/src/domain/entities/paywall/SubscriptionPlan.ts +0 -27
  23. package/src/presentation/components/paywall/BestValueBadge.tsx +0 -59
  24. package/src/presentation/components/paywall/CreditsPackageCard.tsx +0 -161
  25. package/src/presentation/components/paywall/CreditsTabContent.tsx +0 -123
  26. package/src/presentation/components/paywall/PaywallFeatureItem.tsx +0 -79
  27. package/src/presentation/components/paywall/PaywallFeaturesList.tsx +0 -47
  28. package/src/presentation/components/paywall/PaywallHeader.tsx +0 -82
  29. package/src/presentation/components/paywall/PaywallLegalFooter.tsx +0 -145
  30. package/src/presentation/components/paywall/PaywallLegalFooterStyles.ts +0 -53
  31. package/src/presentation/components/paywall/PaywallLegalFooterTypes.ts +0 -19
  32. package/src/presentation/components/paywall/PaywallModal.tsx +0 -162
  33. package/src/presentation/components/paywall/PaywallTabBar.tsx +0 -120
  34. package/src/presentation/components/paywall/SubscriptionFooter.tsx +0 -116
  35. package/src/presentation/components/paywall/SubscriptionModal.tsx +0 -168
  36. package/src/presentation/components/paywall/SubscriptionModalHeader.tsx +0 -78
  37. package/src/presentation/components/paywall/SubscriptionPackageList.tsx +0 -171
  38. package/src/presentation/components/paywall/SubscriptionPlanCard.tsx +0 -213
  39. package/src/presentation/components/paywall/SubscriptionPlanCardStyles.ts +0 -61
  40. package/src/presentation/components/paywall/SubscriptionPlanCardTypes.ts +0 -15
  41. package/src/presentation/components/paywall/SubscriptionTabContent.tsx +0 -139
  42. package/src/presentation/components/paywall/accordion/AccordionPlanCard.tsx +0 -98
  43. package/src/presentation/components/paywall/accordion/AccordionPlanCardTypes.ts +0 -39
  44. package/src/presentation/components/paywall/accordion/PlanCardDetails.tsx +0 -107
  45. package/src/presentation/components/paywall/accordion/PlanCardHeader.tsx +0 -155
  46. package/src/presentation/components/paywall/accordion/index.ts +0 -12
  47. /package/src/{presentation → domains/paywall}/hooks/useSubscriptionModal.ts +0 -0
@@ -1,161 +0,0 @@
1
- /**
2
- * Credits Package Card Component
3
- * Selectable card for credit packages
4
- */
5
-
6
- import React from "react";
7
- import { View, StyleSheet, TouchableOpacity } from "react-native";
8
- import {
9
- AtomicText,
10
- AtomicIcon,
11
- AtomicBadge,
12
- useAppDesignTokens,
13
- } from "@umituz/react-native-design-system";
14
- import type { CreditsPackage } from "../../../domain/entities/paywall/CreditsPackage";
15
-
16
- interface CreditsPackageCardProps {
17
- package: CreditsPackage;
18
- isSelected: boolean;
19
- onSelect: () => void;
20
- }
21
-
22
- export const CreditsPackageCard: React.FC<CreditsPackageCardProps> = React.memo(
23
- ({ package: pkg, isSelected, onSelect }) => {
24
- const tokens = useAppDesignTokens();
25
- const totalCredits = pkg.credits + (pkg.bonus || 0);
26
-
27
- return (
28
- <TouchableOpacity
29
- onPress={onSelect}
30
- activeOpacity={0.7}
31
- style={styles.touchable}
32
- >
33
- <View
34
- style={[
35
- styles.container,
36
- {
37
- backgroundColor: tokens.colors.surface,
38
- borderColor: isSelected ? tokens.colors.primary : tokens.colors.border,
39
- borderWidth: isSelected ? 2 : 1,
40
- },
41
- ]}
42
- >
43
- {pkg.badge && (
44
- <View style={styles.badgeContainer}>
45
- <AtomicBadge text={pkg.badge} variant="warning" size="sm" />
46
- </View>
47
- )}
48
-
49
- <View style={styles.content}>
50
- <View style={styles.leftSection}>
51
- <View style={styles.creditsRow}>
52
- <AtomicIcon
53
- name="flash"
54
- size="md"
55
- color={isSelected ? "primary" : "secondary"}
56
- />
57
- <AtomicText
58
- type="headlineSmall"
59
- style={[
60
- styles.credits,
61
- { color: isSelected ? tokens.colors.primary : tokens.colors.textPrimary },
62
- ]}
63
- >
64
- {totalCredits.toLocaleString()}
65
- </AtomicText>
66
- </View>
67
-
68
- {(pkg.bonus ?? 0) > 0 && (
69
- <View style={styles.bonusContainer}>
70
- <AtomicIcon name="gift-outline" size="sm" color="success" />
71
- <AtomicText
72
- type="bodySmall"
73
- style={[styles.bonus, { color: tokens.colors.success }]}
74
- >
75
- +{pkg.bonus}
76
- </AtomicText>
77
- </View>
78
- )}
79
-
80
- {pkg.description && (
81
- <AtomicText
82
- type="bodySmall"
83
- style={{ color: tokens.colors.textSecondary }}
84
- >
85
- {pkg.description}
86
- </AtomicText>
87
- )}
88
- </View>
89
-
90
- <View style={styles.rightSection}>
91
- <AtomicText
92
- type="titleLarge"
93
- style={[
94
- styles.price,
95
- { color: isSelected ? tokens.colors.primary : tokens.colors.textPrimary },
96
- ]}
97
- >
98
- {pkg.currency}{pkg.price.toFixed(2)}
99
- </AtomicText>
100
- {isSelected && (
101
- <AtomicIcon name="checkmark-circle" size="md" color="primary" />
102
- )}
103
- </View>
104
- </View>
105
- </View>
106
- </TouchableOpacity>
107
- );
108
- }
109
- );
110
-
111
- CreditsPackageCard.displayName = "CreditsPackageCard";
112
-
113
- const styles = StyleSheet.create({
114
- touchable: {
115
- marginBottom: 12,
116
- },
117
- container: {
118
- borderRadius: 16,
119
- padding: 16,
120
- position: "relative",
121
- },
122
- badgeContainer: {
123
- position: "absolute",
124
- top: -10,
125
- right: 16,
126
- },
127
- content: {
128
- flexDirection: "row",
129
- justifyContent: "space-between",
130
- alignItems: "center",
131
- },
132
- leftSection: {
133
- flex: 1,
134
- marginRight: 16,
135
- },
136
- creditsRow: {
137
- flexDirection: "row",
138
- alignItems: "center",
139
- marginBottom: 4,
140
- },
141
- credits: {
142
- fontWeight: "700",
143
- marginLeft: 8,
144
- },
145
- bonusContainer: {
146
- flexDirection: "row",
147
- alignItems: "center",
148
- marginBottom: 4,
149
- },
150
- bonus: {
151
- fontWeight: "600",
152
- marginLeft: 4,
153
- },
154
- rightSection: {
155
- alignItems: "flex-end",
156
- },
157
- price: {
158
- fontWeight: "700",
159
- marginBottom: 4,
160
- },
161
- });
@@ -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
- });