@umituz/react-native-settings 4.17.14 → 4.17.16

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 (103) hide show
  1. package/package.json +16 -15
  2. package/src/domains/about/__tests__/integration.test.tsx +328 -0
  3. package/src/domains/about/__tests__/types.d.ts +5 -0
  4. package/src/domains/about/domain/entities/AppInfo.ts +74 -0
  5. package/src/domains/about/domain/entities/__tests__/AppInfo.test.ts +93 -0
  6. package/src/domains/about/domain/repositories/IAboutRepository.ts +22 -0
  7. package/src/domains/about/index.ts +10 -0
  8. package/src/domains/about/infrastructure/repositories/AboutRepository.ts +68 -0
  9. package/src/domains/about/infrastructure/repositories/__tests__/AboutRepository.test.ts +153 -0
  10. package/src/domains/about/presentation/components/AboutContent.tsx +104 -0
  11. package/src/domains/about/presentation/components/AboutHeader.tsx +79 -0
  12. package/src/domains/about/presentation/components/AboutSection.tsx +134 -0
  13. package/src/domains/about/presentation/components/AboutSettingItem.tsx +208 -0
  14. package/src/domains/about/presentation/components/__tests__/AboutContent.simple.test.tsx +178 -0
  15. package/src/domains/about/presentation/components/__tests__/AboutContent.test.tsx +293 -0
  16. package/src/domains/about/presentation/components/__tests__/AboutHeader.test.tsx +201 -0
  17. package/src/domains/about/presentation/components/__tests__/AboutSettingItem.test.tsx +71 -0
  18. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.simple.test.tsx +229 -0
  19. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.test.tsx +240 -0
  20. package/src/domains/about/presentation/hooks/useAboutInfo.ts +262 -0
  21. package/src/domains/about/presentation/screens/AboutScreen.tsx +195 -0
  22. package/src/domains/about/presentation/screens/__tests__/AboutScreen.simple.test.tsx +199 -0
  23. package/src/domains/about/presentation/screens/__tests__/AboutScreen.test.tsx +366 -0
  24. package/src/domains/about/types/global.d.ts +15 -0
  25. package/src/domains/about/utils/__tests__/index.test.ts +408 -0
  26. package/src/domains/about/utils/index.ts +160 -0
  27. package/src/domains/appearance/__tests__/components/AppearanceScreen.test.tsx +195 -0
  28. package/src/domains/appearance/__tests__/hooks/index.test.tsx +232 -0
  29. package/src/domains/appearance/__tests__/integration/index.test.tsx +207 -0
  30. package/src/domains/appearance/__tests__/services/appearanceService.test.ts +299 -0
  31. package/src/domains/appearance/__tests__/setup.ts +96 -0
  32. package/src/domains/appearance/__tests__/stores/appearanceStore.test.tsx +175 -0
  33. package/src/domains/appearance/data/colorPalettes.ts +94 -0
  34. package/src/domains/appearance/hooks/index.ts +6 -0
  35. package/src/domains/appearance/hooks/useAppearance.ts +61 -0
  36. package/src/domains/appearance/hooks/useAppearanceActions.ts +144 -0
  37. package/src/domains/appearance/index.ts +7 -0
  38. package/src/domains/appearance/infrastructure/services/appearanceService.ts +301 -0
  39. package/src/domains/appearance/infrastructure/services/systemThemeDetection.ts +79 -0
  40. package/src/domains/appearance/infrastructure/services/validation.ts +91 -0
  41. package/src/domains/appearance/infrastructure/storage/appearanceStorage.ts +120 -0
  42. package/src/domains/appearance/infrastructure/stores/appearanceStore.ts +132 -0
  43. package/src/domains/appearance/presentation/components/AppearanceHeader.tsx +67 -0
  44. package/src/domains/appearance/presentation/components/AppearancePreview.tsx +141 -0
  45. package/src/domains/appearance/presentation/components/AppearanceSection.tsx +139 -0
  46. package/src/domains/appearance/presentation/components/ColorPicker.tsx +113 -0
  47. package/src/domains/appearance/presentation/components/CustomColorsSection.tsx +186 -0
  48. package/src/domains/appearance/presentation/components/ThemeModeSection.tsx +110 -0
  49. package/src/domains/appearance/presentation/components/ThemeOption.tsx +138 -0
  50. package/src/domains/appearance/presentation/components/index.ts +6 -0
  51. package/src/domains/appearance/presentation/screens/AppearanceScreen.tsx +226 -0
  52. package/src/domains/appearance/presentation/screens/index.ts +2 -0
  53. package/src/domains/appearance/types/index.ts +54 -0
  54. package/src/domains/faqs/domain/entities/FAQEntity.ts +16 -0
  55. package/src/domains/faqs/domain/services/FAQSearchService.ts +36 -0
  56. package/src/domains/faqs/domain/services/index.ts +1 -0
  57. package/src/domains/faqs/index.ts +7 -0
  58. package/src/domains/faqs/presentation/components/FAQCategory.tsx +71 -0
  59. package/src/domains/faqs/presentation/components/FAQEmptyState.tsx +75 -0
  60. package/src/domains/faqs/presentation/components/FAQItem.tsx +103 -0
  61. package/src/domains/faqs/presentation/components/FAQSearchBar.tsx +70 -0
  62. package/src/domains/faqs/presentation/components/FAQSection.tsx +50 -0
  63. package/src/domains/faqs/presentation/components/index.ts +18 -0
  64. package/src/domains/faqs/presentation/hooks/index.ts +6 -0
  65. package/src/domains/faqs/presentation/hooks/useFAQExpansion.ts +51 -0
  66. package/src/domains/faqs/presentation/hooks/useFAQSearch.ts +33 -0
  67. package/src/domains/faqs/presentation/screens/FAQScreen.tsx +129 -0
  68. package/src/domains/faqs/presentation/screens/index.ts +2 -0
  69. package/src/domains/feedback/domain/entities/FeedbackEntity.ts +92 -0
  70. package/src/domains/feedback/domain/repositories/IFeedbackRepository.ts +28 -0
  71. package/src/domains/feedback/index.ts +6 -0
  72. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +189 -0
  73. package/src/domains/feedback/presentation/components/FeedbackModal.tsx +111 -0
  74. package/src/domains/feedback/presentation/components/SupportSection.tsx +160 -0
  75. package/src/domains/feedback/presentation/hooks/useDeleteFeedback.ts +25 -0
  76. package/src/domains/feedback/presentation/hooks/useFeedbackForm.ts +59 -0
  77. package/src/domains/feedback/presentation/hooks/useSubmitFeedback.ts +55 -0
  78. package/src/domains/feedback/presentation/hooks/useUserFeedback.ts +29 -0
  79. package/src/domains/legal/__tests__/ContentValidationService.test.ts +195 -0
  80. package/src/domains/legal/__tests__/StyleCacheService.test.ts +110 -0
  81. package/src/domains/legal/__tests__/UrlHandlerService.test.ts +71 -0
  82. package/src/domains/legal/__tests__/setup.ts +82 -0
  83. package/src/domains/legal/domain/entities/LegalConfig.ts +26 -0
  84. package/src/domains/legal/domain/services/ContentValidationService.ts +89 -0
  85. package/src/domains/legal/domain/services/StyleCacheService.ts +97 -0
  86. package/src/domains/legal/domain/services/UrlHandlerService.ts +128 -0
  87. package/src/domains/legal/index.ts +8 -0
  88. package/src/domains/legal/presentation/components/LegalItem.tsx +177 -0
  89. package/src/domains/legal/presentation/components/LegalLinks.tsx +154 -0
  90. package/src/domains/legal/presentation/components/LegalSection.tsx +134 -0
  91. package/src/domains/legal/presentation/screens/LegalScreen.tsx +237 -0
  92. package/src/domains/legal/presentation/screens/PrivacyPolicyScreen.tsx +214 -0
  93. package/src/domains/legal/presentation/screens/TermsOfServiceScreen.tsx +214 -0
  94. package/src/index.ts +19 -0
  95. package/src/presentation/components/DevSettingsSection.tsx +2 -2
  96. package/src/presentation/components/SettingItem.tsx +2 -2
  97. package/src/presentation/components/SettingsErrorBoundary.tsx +2 -2
  98. package/src/presentation/components/SettingsFooter.tsx +2 -2
  99. package/src/presentation/components/SettingsSection.tsx +2 -2
  100. package/src/presentation/navigation/SettingsStackNavigator.tsx +2 -2
  101. package/src/presentation/screens/SettingsScreen.tsx +2 -2
  102. package/src/presentation/screens/components/SettingsContent.tsx +2 -2
  103. package/src/presentation/screens/components/SettingsHeader.tsx +2 -2
@@ -0,0 +1,154 @@
1
+ /**
2
+ * LegalLinks Component
3
+ * Single Responsibility: Display Privacy Policy and Terms of Service links
4
+ * Required for App Store compliance in paywall screens
5
+ */
6
+
7
+ import React from "react";
8
+ import { View, TouchableOpacity, StyleSheet } from "react-native";
9
+ import { AtomicText } from "@umituz/react-native-design-system";
10
+ import { ContentValidationService } from "../../domain/services/ContentValidationService";
11
+ import { UrlHandlerService } from "../../domain/services/UrlHandlerService";
12
+
13
+ export interface LegalLinksProps {
14
+ /**
15
+ * Privacy Policy URL
16
+ */
17
+ privacyPolicyUrl?: string;
18
+ /**
19
+ * Terms of Service URL
20
+ */
21
+ termsOfServiceUrl?: string;
22
+ /**
23
+ * Privacy Policy link text (required when privacyPolicyUrl is provided)
24
+ */
25
+ privacyText?: string;
26
+ /**
27
+ * Terms of Service link text (required when termsOfServiceUrl is provided)
28
+ */
29
+ termsText?: string;
30
+ /**
31
+ * Callback when Privacy Policy is pressed
32
+ */
33
+ onPrivacyPress?: () => void;
34
+ /**
35
+ * Callback when Terms of Service is pressed
36
+ */
37
+ onTermsPress?: () => void;
38
+ /**
39
+ * Additional styles
40
+ */
41
+ style?: object;
42
+ }
43
+
44
+ export const LegalLinks: React.FC<LegalLinksProps> = React.memo(
45
+ ({
46
+ privacyPolicyUrl,
47
+ termsOfServiceUrl,
48
+ privacyText,
49
+ termsText,
50
+ onPrivacyPress,
51
+ onTermsPress,
52
+ style,
53
+ }) => {
54
+ // Validate required props
55
+ React.useEffect(() => {
56
+ ContentValidationService.validateLegalLinks(
57
+ privacyPolicyUrl,
58
+ termsOfServiceUrl,
59
+ privacyText,
60
+ termsText,
61
+ onPrivacyPress,
62
+ onTermsPress
63
+ );
64
+ }, [privacyPolicyUrl, termsOfServiceUrl, privacyText, termsText, onPrivacyPress, onTermsPress]);
65
+
66
+ // Memoize press handlers to prevent child re-renders
67
+ const handlePrivacyPress = React.useCallback(async () => {
68
+ if (onPrivacyPress) {
69
+ onPrivacyPress();
70
+ } else if (privacyPolicyUrl) {
71
+ try {
72
+ await UrlHandlerService.openUrl(privacyPolicyUrl);
73
+ } catch (error) {
74
+ if (__DEV__) {
75
+ console.error('LegalLinks: Error opening privacy policy URL', error);
76
+ }
77
+ }
78
+ }
79
+ }, [onPrivacyPress, privacyPolicyUrl]);
80
+
81
+ const handleTermsPress = React.useCallback(async () => {
82
+ if (onTermsPress) {
83
+ onTermsPress();
84
+ } else if (termsOfServiceUrl) {
85
+ try {
86
+ await UrlHandlerService.openUrl(termsOfServiceUrl);
87
+ } catch (error) {
88
+ if (__DEV__) {
89
+ console.error('LegalLinks: Error opening terms of service URL', error);
90
+ }
91
+ }
92
+ }
93
+ }, [onTermsPress, termsOfServiceUrl]);
94
+
95
+ // Memoize conditional rendering to prevent unnecessary re-renders
96
+ const showPrivacy = React.useMemo(() => !!(onPrivacyPress || privacyPolicyUrl), [onPrivacyPress, privacyPolicyUrl]);
97
+ const showTerms = React.useMemo(() => !!(onTermsPress || termsOfServiceUrl), [onTermsPress, termsOfServiceUrl]);
98
+ const showSeparator = React.useMemo(() => showPrivacy && showTerms, [showPrivacy, showTerms]);
99
+
100
+ return (
101
+ <View style={[styles.container, style]}>
102
+ {showPrivacy && (
103
+ <TouchableOpacity onPress={handlePrivacyPress} hitSlop={styles.hitSlop}>
104
+ <AtomicText
105
+ type="labelSmall"
106
+ color="primary"
107
+ style={styles.link}
108
+ >
109
+ {privacyText}
110
+ </AtomicText>
111
+ </TouchableOpacity>
112
+ )}
113
+ {showSeparator && (
114
+ <AtomicText
115
+ type="labelSmall"
116
+ color="textTertiary"
117
+ >
118
+ {" • "}
119
+ </AtomicText>
120
+ )}
121
+ {showTerms && (
122
+ <TouchableOpacity onPress={handleTermsPress} hitSlop={styles.hitSlop}>
123
+ <AtomicText
124
+ type="labelSmall"
125
+ color="primary"
126
+ style={styles.link}
127
+ >
128
+ {termsText}
129
+ </AtomicText>
130
+ </TouchableOpacity>
131
+ )}
132
+ </View>
133
+ );
134
+ },
135
+ );
136
+
137
+ LegalLinks.displayName = "LegalLinks";
138
+
139
+ const styles = StyleSheet.create({
140
+ container: {
141
+ flexDirection: "row",
142
+ alignItems: "center",
143
+ justifyContent: "center",
144
+ },
145
+ link: {
146
+ textDecorationLine: "underline",
147
+ },
148
+ hitSlop: {
149
+ top: 10,
150
+ bottom: 10,
151
+ left: 10,
152
+ right: 10,
153
+ },
154
+ });
@@ -0,0 +1,134 @@
1
+ import React from 'react';
2
+ import { View, Pressable, StyleSheet, ViewStyle } from 'react-native';
3
+ import { useNavigation } from '@react-navigation/native';
4
+ import { useResponsiveDesignTokens, AtomicIcon, AtomicText } from '@umituz/react-native-design-system';
5
+ import { LegalConfig } from '../../domain/entities/LegalConfig';
6
+
7
+ export interface LegalSectionProps {
8
+ config?: LegalConfig;
9
+ onPress?: () => void;
10
+ containerStyle?: ViewStyle;
11
+ title?: string;
12
+ description?: string;
13
+ sectionTitle?: string;
14
+ }
15
+
16
+ export const LegalSection: React.FC<LegalSectionProps> = ({
17
+ config,
18
+ onPress,
19
+ containerStyle,
20
+ title: propsTitle,
21
+ description: propsDescription,
22
+ sectionTitle: propsSectionTitle,
23
+ }) => {
24
+ const navigation = useNavigation();
25
+ const tokens = useResponsiveDesignTokens();
26
+ const colors = tokens.colors;
27
+
28
+ const route = config?.route || config?.defaultRoute || 'Legal';
29
+ const title = propsTitle || config?.title;
30
+ const description = propsDescription || config?.description;
31
+ const sectionTitle = propsSectionTitle || title;
32
+
33
+ const handlePress = () => {
34
+ if (onPress) {
35
+ onPress();
36
+ } else {
37
+ navigation.navigate(route as never);
38
+ }
39
+ };
40
+
41
+ if (!title) return null;
42
+
43
+ return (
44
+ <View style={[styles.sectionContainer, { backgroundColor: colors.surface }, containerStyle]}>
45
+ {!!sectionTitle && (
46
+ <View style={styles.headerContainer}>
47
+ <AtomicText
48
+ type="titleMedium"
49
+ color="primary"
50
+ >
51
+ {sectionTitle}
52
+ </AtomicText>
53
+ </View>
54
+ )}
55
+ <Pressable
56
+ style={({ pressed }) => [
57
+ styles.itemContainer,
58
+ {
59
+ backgroundColor: pressed ? `${colors.primary}08` : 'transparent',
60
+ },
61
+ ]}
62
+ onPress={handlePress}
63
+ >
64
+ <View style={styles.content}>
65
+ <View
66
+ style={[
67
+ styles.iconContainer,
68
+ { backgroundColor: `${colors.primary}15` },
69
+ ]}
70
+ >
71
+ <AtomicIcon name="document-text" size="lg" color="primary" />
72
+ </View>
73
+ <View style={styles.textContainer}>
74
+ <AtomicText
75
+ type="bodyLarge"
76
+ color="primary"
77
+ numberOfLines={1}
78
+ style={{ marginBottom: 4 }}
79
+ >
80
+ {title}
81
+ </AtomicText>
82
+ {!!description && (
83
+ <AtomicText
84
+ type="bodyMedium"
85
+ color="secondary"
86
+ numberOfLines={2}
87
+ >
88
+ {description}
89
+ </AtomicText>
90
+ )}
91
+ </View>
92
+ <AtomicIcon name="chevron-right" size="md" color="secondary" />
93
+ </View>
94
+ </Pressable>
95
+ </View>
96
+ );
97
+ };
98
+
99
+ const styles = StyleSheet.create({
100
+ sectionContainer: {
101
+ marginBottom: 16,
102
+ borderRadius: 12,
103
+ overflow: 'hidden',
104
+ },
105
+ headerContainer: {
106
+ paddingHorizontal: 16,
107
+ paddingTop: 16,
108
+ paddingBottom: 8,
109
+ },
110
+ itemContainer: {
111
+ flexDirection: 'row',
112
+ alignItems: 'center',
113
+ paddingHorizontal: 16,
114
+ paddingVertical: 16,
115
+ minHeight: 72,
116
+ },
117
+ content: {
118
+ flex: 1,
119
+ flexDirection: 'row',
120
+ alignItems: 'center',
121
+ },
122
+ iconContainer: {
123
+ width: 48,
124
+ height: 48,
125
+ borderRadius: 12,
126
+ justifyContent: 'center',
127
+ alignItems: 'center',
128
+ marginRight: 16,
129
+ },
130
+ textContainer: {
131
+ flex: 1,
132
+ marginRight: 8,
133
+ },
134
+ });
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Legal Screen Component
3
+ *
4
+ * Single Responsibility: Layout and orchestration of legal documents list
5
+ * Delegates item rendering to LegalItem component
6
+ *
7
+ * All text content is passed as props (no localization dependency).
8
+ */
9
+
10
+ import React from "react";
11
+ import { View, StyleSheet } from "react-native";
12
+ import { useResponsiveDesignTokens, type DesignTokens } from "@umituz/react-native-design-system";
13
+ import { AtomicText } from "@umituz/react-native-design-system";
14
+ import { ScreenLayout } from "@umituz/react-native-design-system";
15
+ import { LegalItem } from "../components/LegalItem";
16
+ import { UrlHandlerService } from "../../domain/services/UrlHandlerService";
17
+ import { ContentValidationService } from "../../domain/services/ContentValidationService";
18
+ import { StyleCacheService } from "../../domain/services/StyleCacheService";
19
+
20
+ export interface LegalScreenProps {
21
+ /**
22
+ * Title of the screen
23
+ */
24
+ title?: string;
25
+ /**
26
+ * Description/subtitle text
27
+ */
28
+ description?: string;
29
+ /**
30
+ * Header text for documents section
31
+ */
32
+ documentsHeader?: string;
33
+ /**
34
+ * Privacy Policy button text
35
+ */
36
+ privacyTitle?: string;
37
+ /**
38
+ * Privacy Policy description
39
+ */
40
+ privacyDescription?: string;
41
+ /**
42
+ * Terms of Service button text
43
+ */
44
+ termsTitle?: string;
45
+ /**
46
+ * Terms of Service description
47
+ */
48
+ termsDescription?: string;
49
+ /**
50
+ * EULA button text
51
+ */
52
+ eulaTitle?: string;
53
+ /**
54
+ * EULA description
55
+ */
56
+ eulaDescription?: string;
57
+ /**
58
+ * Callback when Privacy Policy is pressed
59
+ */
60
+ onPrivacyPress?: () => void;
61
+ /**
62
+ * Callback when Terms of Service is pressed
63
+ */
64
+ onTermsPress?: () => void;
65
+ /**
66
+ * Callback when EULA is pressed
67
+ * Icon name from Feather library (e.g., "shield", "file-text", "file") EULA URL
68
+ */
69
+ onEulaPress?: () => void;
70
+ /**
71
+ * EULA URL (defaults to Apple's standard EULA)
72
+ */
73
+ eulaUrl?: string;
74
+ /**
75
+ * Test ID for E2E testing
76
+ */
77
+ testID?: string;
78
+ }
79
+
80
+ export const LegalScreen: React.FC<LegalScreenProps> = React.memo(({
81
+ title,
82
+ description,
83
+ documentsHeader,
84
+ privacyTitle,
85
+ privacyDescription,
86
+ termsTitle,
87
+ termsDescription,
88
+ eulaTitle,
89
+ eulaDescription,
90
+ onPrivacyPress,
91
+ onTermsPress,
92
+ onEulaPress,
93
+ eulaUrl,
94
+ testID = "legal-screen",
95
+ }) => {
96
+ const tokens = useResponsiveDesignTokens();
97
+
98
+ // Memoize styles to prevent recreation on every render
99
+ const styles = React.useMemo(() => {
100
+ const cacheKey = StyleCacheService.createTokenCacheKey(tokens);
101
+ return StyleCacheService.getCachedStyles(
102
+ 'LegalScreen',
103
+ cacheKey,
104
+ () => createLegalScreenStyles(tokens)
105
+ );
106
+ }, [tokens]);
107
+
108
+ // Memoize EULA press handler to prevent child re-renders
109
+ const handleEulaPress = React.useCallback(async () => {
110
+ if (__DEV__) {
111
+ console.log('LegalScreen: EULA pressed', { eulaUrl });
112
+ }
113
+
114
+ if (onEulaPress) {
115
+ onEulaPress();
116
+ } else if (eulaUrl) {
117
+ try {
118
+ await UrlHandlerService.openUrl(eulaUrl);
119
+ } catch (error) {
120
+ if (__DEV__) {
121
+ console.error('LegalScreen: Error opening EULA URL', error);
122
+ }
123
+ }
124
+ }
125
+ }, [onEulaPress, eulaUrl]);
126
+
127
+ // Memoize conditional rendering to prevent unnecessary re-renders
128
+ const showHeader = React.useMemo(() => !!(title), [title]);
129
+ const showDescription = React.useMemo(() => !!(description), [description]);
130
+ const showSectionHeader = React.useMemo(() => !!(documentsHeader), [documentsHeader]);
131
+ const showPrivacy = React.useMemo(() =>
132
+ ContentValidationService.shouldShowLegalItem(onPrivacyPress, privacyTitle),
133
+ [onPrivacyPress, privacyTitle]
134
+ );
135
+ const showTerms = React.useMemo(() =>
136
+ ContentValidationService.shouldShowLegalItem(onTermsPress, termsTitle),
137
+ [onTermsPress, termsTitle]
138
+ );
139
+ const showEula = React.useMemo(() =>
140
+ !!((onEulaPress || eulaUrl) && eulaTitle),
141
+ [onEulaPress, eulaUrl, eulaTitle]
142
+ );
143
+
144
+ // Memoize header content
145
+ const headerContent = React.useMemo(() => {
146
+ if (!showHeader) return null;
147
+
148
+ return (
149
+ <View style={styles.header}>
150
+ <AtomicText type="headlineLarge" color="textPrimary">
151
+ {title}
152
+ </AtomicText>
153
+ {showDescription && (
154
+ <AtomicText
155
+ type="bodyMedium"
156
+ color="textSecondary"
157
+ style={styles.headerSubtitle}
158
+ >
159
+ {description}
160
+ </AtomicText>
161
+ )}
162
+ </View>
163
+ );
164
+ }, [showHeader, showDescription, styles.header, styles.headerSubtitle, title, description]);
165
+
166
+ return (
167
+ <ScreenLayout testID={testID} hideScrollIndicator>
168
+ {/* Header */}
169
+ {headerContent}
170
+
171
+ {/* Legal Documents Section */}
172
+ <View style={styles.section}>
173
+ {showSectionHeader && (
174
+ <AtomicText
175
+ type="labelLarge"
176
+ color="textSecondary"
177
+ style={styles.sectionHeader}
178
+ >
179
+ {documentsHeader}
180
+ </AtomicText>
181
+ )}
182
+
183
+ {/* Privacy Policy */}
184
+ {showPrivacy && (
185
+ <LegalItem
186
+ iconName="shield"
187
+ title={privacyTitle!}
188
+ description={privacyDescription}
189
+ onPress={onPrivacyPress}
190
+ testID="privacy-policy-item"
191
+ />
192
+ )}
193
+
194
+ {/* Terms of Service */}
195
+ {showTerms && (
196
+ <LegalItem
197
+ iconName="document-text"
198
+ title={termsTitle!}
199
+ description={termsDescription}
200
+ onPress={onTermsPress}
201
+ testID="terms-of-service-item"
202
+ />
203
+ )}
204
+
205
+ {/* EULA */}
206
+ {showEula && (
207
+ <LegalItem
208
+ iconName="document"
209
+ title={eulaTitle!}
210
+ description={eulaDescription}
211
+ onPress={handleEulaPress}
212
+ testID="eula-item"
213
+ />
214
+ )}
215
+ </View>
216
+ </ScreenLayout>
217
+ );
218
+ });
219
+
220
+ const createLegalScreenStyles = (tokens: DesignTokens) => {
221
+ return StyleSheet.create({
222
+ header: {
223
+ paddingBottom: tokens.spacing.lg,
224
+ paddingTop: tokens.spacing.md,
225
+ },
226
+ headerSubtitle: {
227
+ marginTop: tokens.spacing.xs,
228
+ },
229
+ section: {
230
+ marginTop: tokens.spacing.md,
231
+ },
232
+ sectionHeader: {
233
+ marginBottom: tokens.spacing.sm,
234
+ paddingHorizontal: tokens.spacing.md,
235
+ },
236
+ });
237
+ };