@umituz/react-native-settings 1.11.4 → 2.4.0

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 (167) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +129 -3
  3. package/lib/__tests__/setup.d.ts +5 -0
  4. package/lib/__tests__/setup.d.ts.map +1 -0
  5. package/lib/__tests__/setup.js +143 -0
  6. package/lib/__tests__/setup.js.map +1 -0
  7. package/lib/domain/repositories/ISettingsRepository.d.ts +51 -0
  8. package/lib/domain/repositories/ISettingsRepository.d.ts.map +1 -0
  9. package/lib/domain/repositories/ISettingsRepository.js +8 -0
  10. package/lib/domain/repositories/ISettingsRepository.js.map +1 -0
  11. package/lib/index.d.ts +35 -0
  12. package/lib/index.d.ts.map +1 -0
  13. package/lib/index.js +32 -0
  14. package/lib/index.js.map +1 -0
  15. package/lib/infrastructure/storage/SettingsStore.d.ts +36 -0
  16. package/lib/infrastructure/storage/SettingsStore.d.ts.map +1 -0
  17. package/lib/infrastructure/storage/SettingsStore.js +144 -0
  18. package/lib/infrastructure/storage/SettingsStore.js.map +1 -0
  19. package/lib/presentation/components/CloudSyncSetting.d.ts +16 -0
  20. package/lib/presentation/components/CloudSyncSetting.d.ts.map +1 -0
  21. package/lib/presentation/components/CloudSyncSetting.js +30 -0
  22. package/lib/presentation/components/CloudSyncSetting.js.map +1 -0
  23. package/lib/presentation/components/DisclaimerCard.d.ts +15 -0
  24. package/lib/presentation/components/DisclaimerCard.d.ts.map +1 -0
  25. package/lib/presentation/components/DisclaimerCard.js +73 -0
  26. package/lib/presentation/components/DisclaimerCard.js.map +1 -0
  27. package/lib/presentation/components/DisclaimerModal.d.ts +13 -0
  28. package/lib/presentation/components/DisclaimerModal.d.ts.map +1 -0
  29. package/lib/presentation/components/DisclaimerModal.js +62 -0
  30. package/lib/presentation/components/DisclaimerModal.js.map +1 -0
  31. package/lib/presentation/components/DisclaimerSetting.d.ts +39 -0
  32. package/lib/presentation/components/DisclaimerSetting.d.ts.map +1 -0
  33. package/lib/presentation/components/DisclaimerSetting.js +59 -0
  34. package/lib/presentation/components/DisclaimerSetting.js.map +1 -0
  35. package/lib/presentation/components/SettingItem.d.ts +45 -0
  36. package/lib/presentation/components/SettingItem.d.ts.map +1 -0
  37. package/lib/presentation/components/SettingItem.js +113 -0
  38. package/lib/presentation/components/SettingItem.js.map +1 -0
  39. package/lib/presentation/components/SettingsErrorBoundary.d.ts +23 -0
  40. package/lib/presentation/components/SettingsErrorBoundary.d.ts.map +1 -0
  41. package/lib/presentation/components/SettingsErrorBoundary.js +73 -0
  42. package/lib/presentation/components/SettingsErrorBoundary.js.map +1 -0
  43. package/lib/presentation/components/SettingsFooter.d.ts +11 -0
  44. package/lib/presentation/components/SettingsFooter.d.ts.map +1 -0
  45. package/lib/presentation/components/SettingsFooter.js +31 -0
  46. package/lib/presentation/components/SettingsFooter.js.map +1 -0
  47. package/lib/presentation/components/SettingsSection.d.ts +13 -0
  48. package/lib/presentation/components/SettingsSection.d.ts.map +1 -0
  49. package/lib/presentation/components/SettingsSection.js +37 -0
  50. package/lib/presentation/components/SettingsSection.js.map +1 -0
  51. package/lib/presentation/components/StorageClearSetting.d.ts +16 -0
  52. package/lib/presentation/components/StorageClearSetting.d.ts.map +1 -0
  53. package/lib/presentation/components/StorageClearSetting.js +21 -0
  54. package/lib/presentation/components/StorageClearSetting.js.map +1 -0
  55. package/lib/presentation/components/UserProfileHeader.d.ts +30 -0
  56. package/lib/presentation/components/UserProfileHeader.d.ts.map +1 -0
  57. package/lib/presentation/components/UserProfileHeader.js +119 -0
  58. package/lib/presentation/components/UserProfileHeader.js.map +1 -0
  59. package/lib/presentation/screens/AppearanceScreen.d.ts +8 -0
  60. package/lib/presentation/screens/AppearanceScreen.d.ts.map +1 -0
  61. package/lib/presentation/screens/AppearanceScreen.js +8 -0
  62. package/lib/presentation/screens/AppearanceScreen.js.map +1 -0
  63. package/lib/presentation/screens/SettingsScreen.d.ts +38 -0
  64. package/lib/presentation/screens/SettingsScreen.d.ts.map +1 -0
  65. package/lib/presentation/screens/SettingsScreen.js +37 -0
  66. package/lib/presentation/screens/SettingsScreen.js.map +1 -0
  67. package/lib/presentation/screens/components/AboutLegalSection.d.ts +15 -0
  68. package/lib/presentation/screens/components/AboutLegalSection.d.ts.map +1 -0
  69. package/lib/presentation/screens/components/AboutLegalSection.js +28 -0
  70. package/lib/presentation/screens/components/AboutLegalSection.js.map +1 -0
  71. package/lib/presentation/screens/components/AppearanceSection.d.ts +12 -0
  72. package/lib/presentation/screens/components/AppearanceSection.d.ts.map +1 -0
  73. package/lib/presentation/screens/components/AppearanceSection.js +21 -0
  74. package/lib/presentation/screens/components/AppearanceSection.js.map +1 -0
  75. package/lib/presentation/screens/components/LanguageSection.d.ts +12 -0
  76. package/lib/presentation/screens/components/LanguageSection.d.ts.map +1 -0
  77. package/lib/presentation/screens/components/LanguageSection.js +26 -0
  78. package/lib/presentation/screens/components/LanguageSection.js.map +1 -0
  79. package/lib/presentation/screens/components/NotificationsSection.d.ts +12 -0
  80. package/lib/presentation/screens/components/NotificationsSection.d.ts.map +1 -0
  81. package/lib/presentation/screens/components/NotificationsSection.js +58 -0
  82. package/lib/presentation/screens/components/NotificationsSection.js.map +1 -0
  83. package/lib/presentation/screens/components/SettingsContent.d.ts +36 -0
  84. package/lib/presentation/screens/components/SettingsContent.d.ts.map +1 -0
  85. package/lib/presentation/screens/components/SettingsContent.js +81 -0
  86. package/lib/presentation/screens/components/SettingsContent.js.map +1 -0
  87. package/lib/presentation/screens/components/SettingsHeader.d.ts +12 -0
  88. package/lib/presentation/screens/components/SettingsHeader.d.ts.map +1 -0
  89. package/lib/presentation/screens/components/SettingsHeader.js +59 -0
  90. package/lib/presentation/screens/components/SettingsHeader.js.map +1 -0
  91. package/lib/presentation/screens/components/index.d.ts +9 -0
  92. package/lib/presentation/screens/components/index.d.ts.map +1 -0
  93. package/lib/presentation/screens/components/index.js +9 -0
  94. package/lib/presentation/screens/components/index.js.map +1 -0
  95. package/lib/presentation/screens/hooks/useFeatureDetection.d.ts +21 -0
  96. package/lib/presentation/screens/hooks/useFeatureDetection.d.ts.map +1 -0
  97. package/lib/presentation/screens/hooks/useFeatureDetection.js +82 -0
  98. package/lib/presentation/screens/hooks/useFeatureDetection.js.map +1 -0
  99. package/lib/presentation/screens/types/CustomSection.d.ts +19 -0
  100. package/lib/presentation/screens/types/CustomSection.d.ts.map +1 -0
  101. package/lib/presentation/screens/types/CustomSection.js +6 -0
  102. package/lib/presentation/screens/types/CustomSection.js.map +1 -0
  103. package/lib/presentation/screens/types/ExtendedConfig.d.ts +68 -0
  104. package/lib/presentation/screens/types/ExtendedConfig.d.ts.map +1 -0
  105. package/lib/presentation/screens/types/ExtendedConfig.js +6 -0
  106. package/lib/presentation/screens/types/ExtendedConfig.js.map +1 -0
  107. package/lib/presentation/screens/types/FeatureConfig.d.ts +95 -0
  108. package/lib/presentation/screens/types/FeatureConfig.d.ts.map +1 -0
  109. package/lib/presentation/screens/types/FeatureConfig.js +6 -0
  110. package/lib/presentation/screens/types/FeatureConfig.js.map +1 -0
  111. package/lib/presentation/screens/types/SettingsConfig.d.ts +97 -0
  112. package/lib/presentation/screens/types/SettingsConfig.d.ts.map +1 -0
  113. package/lib/presentation/screens/types/SettingsConfig.js +6 -0
  114. package/lib/presentation/screens/types/SettingsConfig.js.map +1 -0
  115. package/lib/presentation/screens/types/index.d.ts +10 -0
  116. package/lib/presentation/screens/types/index.d.ts.map +1 -0
  117. package/lib/presentation/screens/types/index.js +6 -0
  118. package/lib/presentation/screens/types/index.js.map +1 -0
  119. package/lib/presentation/screens/utils/normalizeConfig.d.ts +44 -0
  120. package/lib/presentation/screens/utils/normalizeConfig.d.ts.map +1 -0
  121. package/lib/presentation/screens/utils/normalizeConfig.js +38 -0
  122. package/lib/presentation/screens/utils/normalizeConfig.js.map +1 -0
  123. package/package.json +46 -11
  124. package/src/__tests__/integration.test.tsx +371 -0
  125. package/src/__tests__/performance.test.tsx +369 -0
  126. package/src/__tests__/setup.test.tsx +20 -0
  127. package/src/__tests__/setup.ts +157 -0
  128. package/src/domain/repositories/ISettingsRepository.ts +0 -0
  129. package/src/index.ts +9 -1
  130. package/src/infrastructure/storage/SettingsStore.ts +90 -45
  131. package/src/infrastructure/storage/__tests__/SettingsStore.test.tsx +302 -0
  132. package/src/presentation/components/CloudSyncSetting.tsx +11 -17
  133. package/src/presentation/components/DisclaimerCard.tsx +115 -0
  134. package/src/presentation/components/DisclaimerModal.tsx +104 -0
  135. package/src/presentation/components/DisclaimerSetting.tsx +77 -159
  136. package/src/presentation/components/SettingItem.tsx +11 -2
  137. package/src/presentation/components/SettingsErrorBoundary.tsx +126 -0
  138. package/src/presentation/components/SettingsFooter.tsx +0 -0
  139. package/src/presentation/components/SettingsSection.tsx +0 -0
  140. package/src/presentation/components/StorageClearSetting.tsx +13 -8
  141. package/src/presentation/components/UserProfileHeader.tsx +48 -11
  142. package/src/presentation/components/__tests__/CloudSyncSetting.test.tsx +78 -0
  143. package/src/presentation/components/__tests__/DisclaimerCard.test.tsx +208 -0
  144. package/src/presentation/components/__tests__/DisclaimerModal.test.tsx +236 -0
  145. package/src/presentation/components/__tests__/DisclaimerSetting.test.tsx +74 -0
  146. package/src/presentation/components/__tests__/SettingItem.test.tsx +189 -0
  147. package/src/presentation/components/__tests__/SettingsErrorBoundary.test.tsx +186 -0
  148. package/src/presentation/screens/AppearanceScreen.tsx +0 -0
  149. package/src/presentation/screens/SettingsScreen.tsx +29 -159
  150. package/src/presentation/screens/__tests__/SettingsScreen.test.tsx +322 -0
  151. package/src/presentation/screens/components/AboutLegalSection.tsx +14 -5
  152. package/src/presentation/screens/components/AppearanceSection.tsx +1 -1
  153. package/src/presentation/screens/components/LanguageSection.tsx +2 -1
  154. package/src/presentation/screens/components/NotificationsSection.tsx +19 -14
  155. package/src/presentation/screens/components/SettingsContent.tsx +167 -0
  156. package/src/presentation/screens/components/SettingsHeader.tsx +79 -0
  157. package/src/presentation/screens/components/index.ts +0 -0
  158. package/src/presentation/screens/hooks/__tests__/useFeatureDetection.test.tsx +261 -0
  159. package/src/presentation/screens/hooks/useFeatureDetection.ts +15 -5
  160. package/src/presentation/screens/types/CustomSection.ts +20 -0
  161. package/src/presentation/screens/types/ExtendedConfig.ts +68 -0
  162. package/src/presentation/screens/types/FeatureConfig.ts +102 -0
  163. package/src/presentation/screens/types/SettingsConfig.ts +116 -0
  164. package/src/presentation/screens/types/index.ts +20 -0
  165. package/src/presentation/screens/utils/normalizeConfig.ts +2 -1
  166. package/src/presentation/screens/LanguageSelectionScreen.tsx +0 -204
  167. package/src/presentation/screens/types.ts +0 -263
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Tests for SettingsScreen Component
3
+ */
4
+
5
+ import React from 'react';
6
+ import { render, fireEvent } from '@testing-library/react-native';
7
+ import { SettingsScreen } from '../SettingsScreen';
8
+ import { NavigationContainer } from '@react-navigation/native';
9
+
10
+ // Mock dependencies
11
+ jest.mock('@umituz/react-native-design-system-theme', () => ({
12
+ useDesignSystemTheme: () => ({
13
+ themeMode: 'light',
14
+ }),
15
+ useAppDesignTokens: () => ({
16
+ colors: {
17
+ backgroundPrimary: '#ffffff',
18
+ },
19
+ }),
20
+ }));
21
+
22
+ jest.mock('../components/SettingsHeader', () => 'SettingsHeader');
23
+ jest.mock('../components/SettingsContent', () => 'SettingsContent');
24
+ jest.mock('../components/SettingsErrorBoundary', () => 'SettingsErrorBoundary');
25
+ jest.mock('../utils/normalizeConfig', () => ({
26
+ normalizeSettingsConfig: jest.fn((config) => ({
27
+ appearance: { enabled: true, config: { enabled: true } },
28
+ language: { enabled: true, config: { enabled: true } },
29
+ notifications: { enabled: true, config: { enabled: true } },
30
+ about: { enabled: true, config: { enabled: true } },
31
+ legal: { enabled: true, config: { enabled: true } },
32
+ account: { enabled: true, config: { enabled: true } },
33
+ support: { enabled: true, config: { enabled: true } },
34
+ developer: { enabled: true, config: { enabled: true } },
35
+ })),
36
+ }));
37
+
38
+ jest.mock('../hooks/useFeatureDetection', () => ({
39
+ useFeatureDetection: jest.fn(() => ({
40
+ appearance: true,
41
+ language: true,
42
+ notifications: true,
43
+ about: true,
44
+ legal: true,
45
+ account: true,
46
+ support: true,
47
+ developer: false,
48
+ })),
49
+ }));
50
+
51
+ // Mock navigation
52
+ const mockNavigation = {
53
+ navigate: jest.fn(),
54
+ goBack: jest.fn(),
55
+ getState: jest.fn(() => ({
56
+ routes: [{ name: 'Settings' }],
57
+ })),
58
+ };
59
+
60
+ jest.mock('@react-navigation/native', () => ({
61
+ useNavigation: () => mockNavigation,
62
+ }));
63
+
64
+ // Wrapper component for navigation context
65
+ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
66
+ <NavigationContainer>
67
+ {children}
68
+ </NavigationContainer>
69
+ );
70
+
71
+ describe('SettingsScreen', () => {
72
+ beforeEach(() => {
73
+ jest.clearAllMocks();
74
+ });
75
+
76
+ it('renders correctly with default props', () => {
77
+ const { getByTestId } = render(
78
+ <TestWrapper>
79
+ <SettingsScreen />
80
+ </TestWrapper>
81
+ );
82
+
83
+ // Should render the main container
84
+ expect(getByTestId('settings-screen')).toBeTruthy();
85
+ });
86
+
87
+ it('renders with custom config', () => {
88
+ const config = {
89
+ appearance: true,
90
+ notifications: false,
91
+ about: true,
92
+ };
93
+
94
+ const { getByTestId } = render(
95
+ <TestWrapper>
96
+ <SettingsScreen config={config} />
97
+ </TestWrapper>
98
+ );
99
+
100
+ expect(getByTestId('settings-screen')).toBeTruthy();
101
+ });
102
+
103
+ it('renders with user profile header', () => {
104
+ const userProfile = {
105
+ displayName: 'John Doe',
106
+ userId: 'user123',
107
+ isGuest: false,
108
+ };
109
+
110
+ const { getByTestId } = render(
111
+ <TestWrapper>
112
+ <SettingsScreen
113
+ showUserProfile={true}
114
+ userProfile={userProfile}
115
+ />
116
+ </TestWrapper>
117
+ );
118
+
119
+ expect(getByTestId('settings-screen')).toBeTruthy();
120
+ });
121
+
122
+ it('renders with custom footer text', () => {
123
+ const { getByTestId } = render(
124
+ <TestWrapper>
125
+ <SettingsScreen
126
+ footerText="Custom Footer Text"
127
+ />
128
+ </TestWrapper>
129
+ );
130
+
131
+ expect(getByTestId('settings-screen')).toBeTruthy();
132
+ });
133
+
134
+ it('renders with custom sections', () => {
135
+ const customSections = [
136
+ {
137
+ title: 'Custom Section',
138
+ data: [
139
+ {
140
+ id: 'custom-item',
141
+ title: 'Custom Item',
142
+ icon: 'Settings',
143
+ },
144
+ ],
145
+ },
146
+ ];
147
+
148
+ const { getByTestId } = render(
149
+ <TestWrapper>
150
+ <SettingsScreen
151
+ customSections={customSections}
152
+ />
153
+ </TestWrapper>
154
+ );
155
+
156
+ expect(getByTestId('settings-screen')).toBeTruthy();
157
+ });
158
+
159
+ it('renders with close button', () => {
160
+ const mockOnClose = jest.fn();
161
+
162
+ const { getByTestId } = render(
163
+ <TestWrapper>
164
+ <SettingsScreen
165
+ showCloseButton={true}
166
+ onClose={mockOnClose}
167
+ />
168
+ </TestWrapper>
169
+ );
170
+
171
+ expect(getByTestId('settings-screen')).toBeTruthy();
172
+ });
173
+
174
+ it('applies correct theme styling', () => {
175
+ const { getByTestId } = render(
176
+ <TestWrapper>
177
+ <SettingsScreen />
178
+ </TestWrapper>
179
+ );
180
+
181
+ const container = getByTestId('settings-screen');
182
+ expect(container.props.style).toContainEqual({
183
+ backgroundColor: '#ffffff',
184
+ });
185
+ });
186
+
187
+ it('handles feature detection options', () => {
188
+ const featureOptions = {
189
+ notificationServiceAvailable: false,
190
+ };
191
+
192
+ const { getByTestId } = render(
193
+ <TestWrapper>
194
+ <SettingsScreen
195
+ featureOptions={featureOptions}
196
+ />
197
+ </TestWrapper>
198
+ );
199
+
200
+ expect(getByTestId('settings-screen')).toBeTruthy();
201
+ });
202
+
203
+ it('wraps content in error boundary', () => {
204
+ const { getByTestId } = render(
205
+ <TestWrapper>
206
+ <SettingsScreen />
207
+ </TestWrapper>
208
+ );
209
+
210
+ expect(getByTestId('settings-screen')).toBeTruthy();
211
+ });
212
+
213
+ it('renders without footer when showFooter is false', () => {
214
+ const { getByTestId } = render(
215
+ <TestWrapper>
216
+ <SettingsScreen
217
+ showFooter={false}
218
+ />
219
+ </TestWrapper>
220
+ );
221
+
222
+ expect(getByTestId('settings-screen')).toBeTruthy();
223
+ });
224
+
225
+ it('handles guest user profile', () => {
226
+ const guestProfile = {
227
+ displayName: 'Guest User',
228
+ userId: 'guest123',
229
+ isGuest: true,
230
+ guestDisplayName: 'Guest',
231
+ };
232
+
233
+ const { getByTestId } = render(
234
+ <TestWrapper>
235
+ <SettingsScreen
236
+ showUserProfile={true}
237
+ userProfile={guestProfile}
238
+ />
239
+ </TestWrapper>
240
+ );
241
+
242
+ expect(getByTestId('settings-screen')).toBeTruthy();
243
+ });
244
+
245
+ it('handles user profile with avatar', () => {
246
+ const profileWithAvatar = {
247
+ displayName: 'John Doe',
248
+ userId: 'user123',
249
+ isGuest: false,
250
+ avatarUrl: 'https://example.com/avatar.jpg',
251
+ };
252
+
253
+ const { getByTestId } = render(
254
+ <TestWrapper>
255
+ <SettingsScreen
256
+ showUserProfile={true}
257
+ userProfile={profileWithAvatar}
258
+ />
259
+ </TestWrapper>
260
+ );
261
+
262
+ expect(getByTestId('settings-screen')).toBeTruthy();
263
+ });
264
+
265
+ it('handles empty config', () => {
266
+ const { getByTestId } = render(
267
+ <TestWrapper>
268
+ <SettingsScreen config={{}} />
269
+ </TestWrapper>
270
+ );
271
+
272
+ expect(getByTestId('settings-screen')).toBeTruthy();
273
+ });
274
+
275
+ it('handles missing user profile props', () => {
276
+ const { getByTestId } = render(
277
+ <TestWrapper>
278
+ <SettingsScreen
279
+ showUserProfile={true}
280
+ userProfile={{}}
281
+ />
282
+ </TestWrapper>
283
+ );
284
+
285
+ expect(getByTestId('settings-screen')).toBeTruthy();
286
+ });
287
+
288
+ it('handles null custom sections', () => {
289
+ const { getByTestId } = render(
290
+ <TestWrapper>
291
+ <SettingsScreen
292
+ customSections={null as any}
293
+ />
294
+ </TestWrapper>
295
+ );
296
+
297
+ expect(getByTestId('settings-screen')).toBeTruthy();
298
+ });
299
+
300
+ it('handles undefined feature options', () => {
301
+ const { getByTestId } = render(
302
+ <TestWrapper>
303
+ <SettingsScreen
304
+ featureOptions={undefined as any}
305
+ />
306
+ </TestWrapper>
307
+ );
308
+
309
+ expect(getByTestId('settings-screen')).toBeTruthy();
310
+ });
311
+
312
+ it('maintains correct component structure', () => {
313
+ const { getByTestId } = render(
314
+ <TestWrapper>
315
+ <SettingsScreen />
316
+ </TestWrapper>
317
+ );
318
+
319
+ // Should have main container
320
+ expect(getByTestId('settings-screen')).toBeTruthy();
321
+ });
322
+ });
@@ -7,6 +7,7 @@ import React from "react";
7
7
  import { Info, FileText } from "lucide-react-native";
8
8
  import { useNavigation } from "@react-navigation/native";
9
9
  import { useLocalization } from "@umituz/react-native-localization";
10
+ import type { NavigationProp } from "@react-navigation/native";
10
11
  import { SettingItem } from "../../components/SettingItem";
11
12
  import { SettingsSection } from "../../components/SettingsSection";
12
13
  import type { AboutConfig, LegalConfig } from "../types";
@@ -18,25 +19,33 @@ interface AboutLegalSectionProps {
18
19
  legalConfig?: LegalConfig;
19
20
  }
20
21
 
22
+ type RootStackParamList = {
23
+ About: undefined;
24
+ Legal: undefined;
25
+ [key: string]: undefined;
26
+ };
27
+
28
+ type NavigationType = NavigationProp<RootStackParamList>;
29
+
21
30
  export const AboutLegalSection: React.FC<AboutLegalSectionProps> = ({
22
31
  showAbout,
23
32
  showLegal,
24
33
  aboutConfig,
25
34
  legalConfig,
26
35
  }) => {
27
- const navigation = useNavigation();
36
+ const navigation = useNavigation<NavigationType>();
28
37
  const { t } = useLocalization();
29
38
 
30
39
  if (!showAbout && !showLegal) {
31
40
  return null;
32
41
  }
33
42
 
34
- const aboutRoute = aboutConfig?.route || "About";
43
+ const aboutRoute = aboutConfig?.route || aboutConfig?.defaultRoute || "About";
35
44
  const aboutTitle = aboutConfig?.title || t("settings.about.title");
36
45
  const aboutDescription =
37
46
  aboutConfig?.description || t("settings.about.description");
38
47
 
39
- const legalRoute = legalConfig?.route || "Legal";
48
+ const legalRoute = legalConfig?.route || legalConfig?.defaultRoute || "Legal";
40
49
  const legalTitle = legalConfig?.title || t("settings.legal.title");
41
50
  const legalDescription =
42
51
  legalConfig?.description || t("settings.legal.description");
@@ -48,7 +57,7 @@ export const AboutLegalSection: React.FC<AboutLegalSectionProps> = ({
48
57
  icon={Info}
49
58
  title={aboutTitle}
50
59
  value={aboutDescription}
51
- onPress={() => navigation.navigate(aboutRoute as never)}
60
+ onPress={() => navigation.navigate(aboutRoute as string)}
52
61
  />
53
62
  )}
54
63
  {showLegal && (
@@ -56,7 +65,7 @@ export const AboutLegalSection: React.FC<AboutLegalSectionProps> = ({
56
65
  icon={FileText}
57
66
  title={legalTitle}
58
67
  value={legalDescription}
59
- onPress={() => navigation.navigate(legalRoute as never)}
68
+ onPress={() => navigation.navigate(legalRoute as string)}
60
69
  isLast={true}
61
70
  />
62
71
  )}
@@ -21,7 +21,7 @@ export const AppearanceSection: React.FC<AppearanceSectionProps> = ({
21
21
  const navigation = useNavigation();
22
22
  const { t } = useLocalization();
23
23
 
24
- const route = config?.route || "Appearance";
24
+ const route = config?.route || config?.defaultRoute || "Appearance";
25
25
  const title = config?.title || t("settings.appearance.title");
26
26
  const description = config?.description || t("settings.appearance.description");
27
27
 
@@ -26,9 +26,10 @@ export const LanguageSection: React.FC<LanguageSectionProps> = ({
26
26
  const description = config?.description || "";
27
27
 
28
28
  const currentLang = getLanguageByCode(currentLanguage);
29
+ const defaultLanguageDisplay = config?.defaultLanguageDisplay || "English";
29
30
  const languageDisplay = currentLang
30
31
  ? `${currentLang.flag} ${currentLang.nativeName}`
31
- : "English";
32
+ : defaultLanguageDisplay;
32
33
 
33
34
  return (
34
35
  <SettingsSection title={t("settings.sections.app.title")}>
@@ -3,7 +3,7 @@
3
3
  * Single Responsibility: Render notifications settings section
4
4
  */
5
5
 
6
- import React, { useState, useEffect } from "react";
6
+ import React, { useState, useEffect, useCallback } from "react";
7
7
  import { Bell } from "lucide-react-native";
8
8
  import { useNavigation } from "@react-navigation/native";
9
9
  import { useLocalization } from "@umituz/react-native-localization";
@@ -12,11 +12,16 @@ import { SettingsSection } from "../../components/SettingsSection";
12
12
  import type { NotificationsConfig } from "../types";
13
13
 
14
14
  // Optional notification service
15
- let notificationService: any = null;
15
+ let notificationService: {
16
+ hasPermissions?: () => Promise<boolean>;
17
+ requestPermissions?: () => Promise<void>;
18
+ } | null = null;
16
19
  try {
17
20
  // eslint-disable-next-line @typescript-eslint/no-require-imports
18
- notificationService = require("@umituz/react-native-notifications")
19
- .notificationService;
21
+ const module = require("@umituz/react-native-notifications");
22
+ if (module?.notificationService && typeof module.notificationService === 'object') {
23
+ notificationService = module.notificationService;
24
+ }
20
25
  } catch {
21
26
  // Package not available
22
27
  }
@@ -28,7 +33,7 @@ interface NotificationsSectionProps {
28
33
  export const NotificationsSection: React.FC<NotificationsSectionProps> = ({
29
34
  config,
30
35
  }) => {
31
- const navigation = useNavigation();
36
+ const navigation = useNavigation<any>();
32
37
  const { t } = useLocalization();
33
38
  const [notificationsEnabled, setNotificationsEnabled] = useState(
34
39
  config?.initialValue ?? true,
@@ -40,27 +45,27 @@ export const NotificationsSection: React.FC<NotificationsSectionProps> = ({
40
45
  }
41
46
  }, [config?.initialValue]);
42
47
 
43
- const handleToggle = async (value: boolean) => {
48
+ const handleToggle = useCallback(async (value: boolean) => {
44
49
  if (notificationService && !value) {
45
- const hasPermissions = await notificationService.hasPermissions();
50
+ const hasPermissions = await notificationService.hasPermissions?.();
46
51
  if (!hasPermissions) {
47
- await notificationService.requestPermissions();
52
+ await notificationService.requestPermissions?.();
48
53
  }
49
54
  }
50
55
 
51
56
  setNotificationsEnabled(value);
52
57
  config?.onToggleChange?.(value);
53
- };
58
+ }, [config?.onToggleChange]);
54
59
 
55
- const handlePress = async () => {
60
+ const handlePress = useCallback(async () => {
56
61
  if (notificationService) {
57
- const hasPermissions = await notificationService.hasPermissions();
62
+ const hasPermissions = await notificationService.hasPermissions?.();
58
63
  if (!hasPermissions) {
59
- await notificationService.requestPermissions();
64
+ await notificationService.requestPermissions?.();
60
65
  }
61
66
  }
62
- navigation.navigate((config?.route || "Notifications") as never);
63
- };
67
+ navigation.navigate(config?.route || config?.defaultRoute || "Notifications" as any);
68
+ }, [navigation, config?.route, config?.defaultRoute]);
64
69
 
65
70
  const title = config?.title || t("settings.notifications.title");
66
71
  const description = config?.description || t("settings.notifications.description");
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Settings Content Component
3
+ * Renders all settings sections and custom content
4
+ */
5
+
6
+ import React, { useMemo } from "react";
7
+ import { View, ScrollView, StyleSheet } from "react-native";
8
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
9
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
10
+ import { useLocalization } from "@umituz/react-native-localization";
11
+ import { SettingsFooter } from "../../components/SettingsFooter";
12
+ import { UserProfileHeader } from "../../components/UserProfileHeader";
13
+ import { SettingsSection } from "../../components/SettingsSection";
14
+ import { AppearanceSection } from "./AppearanceSection";
15
+ import { LanguageSection } from "./LanguageSection";
16
+ import { NotificationsSection } from "./NotificationsSection";
17
+ import { AboutLegalSection } from "./AboutLegalSection";
18
+ import type { NormalizedConfig } from "../utils/normalizeConfig";
19
+ import type { CustomSettingsSection } from "../types";
20
+
21
+ interface SettingsContentProps {
22
+ normalizedConfig: NormalizedConfig;
23
+ config?: any; // Original config for emptyStateText
24
+ features: {
25
+ appearance: boolean;
26
+ language: boolean;
27
+ notifications: boolean;
28
+ about: boolean;
29
+ legal: boolean;
30
+ };
31
+ showUserProfile?: boolean;
32
+ userProfile?: {
33
+ displayName?: string;
34
+ userId?: string;
35
+ isGuest?: boolean;
36
+ avatarUrl?: string;
37
+ accountSettingsRoute?: string;
38
+ onPress?: () => void;
39
+ guestDisplayName?: string;
40
+ avatarServiceUrl?: string;
41
+ };
42
+ showFooter?: boolean;
43
+ footerText?: string;
44
+ customSections?: CustomSettingsSection[];
45
+ showCloseButton?: boolean;
46
+ }
47
+
48
+ export const SettingsContent: React.FC<SettingsContentProps> = ({
49
+ normalizedConfig,
50
+ config,
51
+ features,
52
+ showUserProfile = false,
53
+ userProfile,
54
+ showFooter = true,
55
+ footerText,
56
+ customSections = [],
57
+ showCloseButton = false,
58
+ }) => {
59
+ const tokens = useAppDesignTokens();
60
+ const insets = useSafeAreaInsets();
61
+ const { t } = useLocalization();
62
+
63
+ const hasAnyFeatures = useMemo(() =>
64
+ features.appearance ||
65
+ features.language ||
66
+ features.notifications ||
67
+ features.about ||
68
+ features.legal ||
69
+ customSections.length > 0,
70
+ [features, customSections.length]
71
+ );
72
+
73
+ const sortedSections = useMemo(() => {
74
+ return Array.from(customSections)
75
+ .sort((a, b) => (a.order ?? 999) - (b.order ?? 999));
76
+ }, [customSections]);
77
+
78
+ return (
79
+ <ScrollView
80
+ style={styles.scrollView}
81
+ contentContainerStyle={[
82
+ styles.scrollContent,
83
+ {
84
+ paddingTop: showCloseButton ? tokens.spacing.md : insets.top + tokens.spacing.md,
85
+ paddingBottom: tokens.spacing.xxxl + tokens.spacing.xl,
86
+ paddingHorizontal: 0,
87
+ },
88
+ ]}
89
+ showsVerticalScrollIndicator={false}
90
+ >
91
+ {showUserProfile && (
92
+ <View style={styles.profileContainer}>
93
+ <UserProfileHeader
94
+ displayName={userProfile?.displayName}
95
+ userId={userProfile?.userId}
96
+ isGuest={userProfile?.isGuest}
97
+ avatarUrl={userProfile?.avatarUrl}
98
+ accountSettingsRoute={userProfile?.accountSettingsRoute}
99
+ onPress={userProfile?.onPress}
100
+ />
101
+ </View>
102
+ )}
103
+
104
+ {features.appearance && (
105
+ <AppearanceSection config={normalizedConfig.appearance.config} />
106
+ )}
107
+
108
+ {features.language && (
109
+ <LanguageSection config={normalizedConfig.language.config} />
110
+ )}
111
+
112
+ {features.notifications && (
113
+ <NotificationsSection config={normalizedConfig.notifications.config} />
114
+ )}
115
+
116
+ {(features.about || features.legal) && (
117
+ <AboutLegalSection
118
+ showAbout={features.about}
119
+ showLegal={features.legal}
120
+ aboutConfig={normalizedConfig.about.config}
121
+ legalConfig={normalizedConfig.legal.config}
122
+ />
123
+ )}
124
+
125
+ {customSections && customSections.length > 0 && (
126
+ <>
127
+ {sortedSections.map((section, index) => (
128
+ <SettingsSection
129
+ key={section.id || `custom-${index}`}
130
+ title={section.title}
131
+ >
132
+ {section.content}
133
+ </SettingsSection>
134
+ ))}
135
+ </>
136
+ )}
137
+
138
+ {!hasAnyFeatures && (
139
+ <View style={styles.emptyContainer}>
140
+ <SettingsSection
141
+ title={config?.emptyStateText || t("settings.noOptionsAvailable") || "No settings available"}
142
+ >
143
+ <View />
144
+ </SettingsSection>
145
+ </View>
146
+ )}
147
+
148
+ {showFooter && <SettingsFooter versionText={footerText} />}
149
+ </ScrollView>
150
+ );
151
+ };
152
+
153
+ const styles = StyleSheet.create({
154
+ scrollView: {
155
+ flex: 1,
156
+ },
157
+ scrollContent: {
158
+ flexGrow: 1,
159
+ },
160
+ profileContainer: {
161
+ marginBottom: 32,
162
+ paddingHorizontal: 0,
163
+ },
164
+ emptyContainer: {
165
+ paddingVertical: 24,
166
+ },
167
+ });