@umituz/react-native-settings 1.7.0 → 1.8.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "Settings management for React Native apps - user preferences, theme, language, notifications",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -30,6 +30,8 @@ export interface SettingItemProps {
30
30
  iconColor?: string;
31
31
  /** Custom title color */
32
32
  titleColor?: string;
33
+ /** Test ID for E2E testing */
34
+ testID?: string;
33
35
  }
34
36
 
35
37
  export const SettingItem: React.FC<SettingItemProps> = ({
@@ -43,6 +45,7 @@ export const SettingItem: React.FC<SettingItemProps> = ({
43
45
  isLast = false,
44
46
  iconColor,
45
47
  titleColor,
48
+ testID,
46
49
  }) => {
47
50
  const tokens = useAppDesignTokens();
48
51
  const colors = tokens.colors;
@@ -57,6 +60,7 @@ export const SettingItem: React.FC<SettingItemProps> = ({
57
60
  onPress={onPress}
58
61
  disabled={showSwitch}
59
62
  activeOpacity={0.7}
63
+ testID={testID}
60
64
  >
61
65
  <View style={styles.content}>
62
66
  <View
@@ -1,82 +1,29 @@
1
1
  /**
2
2
  * Settings Screen
3
- * Modern settings screen with user profile header and organized sections
3
+ * Presentation layer - Composition only, no business logic
4
4
  */
5
5
 
6
- import React, { useMemo, useState } from "react";
7
- import {
8
- View,
9
- ScrollView,
10
- StatusBar,
11
- StyleSheet,
12
- Alert,
13
- DeviceEventEmitter,
14
- } from "react-native";
6
+ import React from "react";
7
+ import { View, ScrollView, StatusBar, StyleSheet } from "react-native";
15
8
  import { useSafeAreaInsets } from "react-native-safe-area-context";
16
- import { useNavigation, CommonActions } from "@react-navigation/native";
9
+ import { useNavigation } from "@react-navigation/native";
17
10
  import {
18
11
  useDesignSystemTheme,
19
12
  useAppDesignTokens,
20
13
  } from "@umituz/react-native-design-system-theme";
21
- import { Palette, Bell, Info, FileText } from "lucide-react-native";
22
14
  import { useLocalization } from "@umituz/react-native-localization";
23
- import { SettingItem } from "../components/SettingItem";
24
- import { SettingsSection } from "../components/SettingsSection";
25
15
  import { SettingsFooter } from "../components/SettingsFooter";
26
16
  import { UserProfileHeader } from "../components/UserProfileHeader";
27
- import { SettingsConfig, CustomSettingsSection } from "./types";
28
-
29
- // Optional notification service
30
- let notificationService: any = null;
31
- try {
32
- // eslint-disable-next-line @typescript-eslint/no-require-imports
33
- notificationService = require("@umituz/react-native-notifications")
34
- .notificationService;
35
- } catch {
36
- // Package not available
37
- }
38
-
39
- // Optional onboarding store
40
- let useOnboardingStore: any = null;
41
- try {
42
- // eslint-disable-next-line @typescript-eslint/no-require-imports
43
- const onboardingPackage = require("@umituz/react-native-onboarding");
44
- useOnboardingStore = onboardingPackage.useOnboardingStore;
45
- } catch {
46
- // Package not available
47
- }
48
-
49
- /**
50
- * Check if navigation screen exists
51
- */
52
- const hasNavigationScreen = (
53
- navigation: any,
54
- screenName: string,
55
- ): boolean => {
56
- try {
57
- const state = navigation.getState();
58
- if (!state) return false;
59
-
60
- const checkRoutes = (routes: any[]): boolean => {
61
- if (!routes || !Array.isArray(routes)) return false;
62
-
63
- for (const route of routes) {
64
- if (route.name === screenName) return true;
65
- if (route.state?.routes && checkRoutes(route.state.routes)) {
66
- return true;
67
- }
68
- }
69
- return false;
70
- };
71
-
72
- return checkRoutes(state.routes || []);
73
- } catch {
74
- return false;
75
- }
76
- };
17
+ import { SettingsSection } from "../components/SettingsSection";
18
+ import { AppearanceSection } from "./components/AppearanceSection";
19
+ import { NotificationsSection } from "./components/NotificationsSection";
20
+ import { AboutLegalSection } from "./components/AboutLegalSection";
21
+ import { normalizeSettingsConfig } from "./utils/normalizeConfig";
22
+ import { useFeatureDetection } from "./hooks/useFeatureDetection";
23
+ import type { CustomSettingsSection } from "./types";
77
24
 
78
25
  export interface SettingsScreenProps {
79
- config?: SettingsConfig;
26
+ config?: any;
80
27
  /** Show user profile header */
81
28
  showUserProfile?: boolean;
82
29
  /** User profile props */
@@ -109,61 +56,19 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
109
56
  const tokens = useAppDesignTokens();
110
57
  const insets = useSafeAreaInsets();
111
58
  const { t } = useLocalization();
112
- const [notificationsEnabled, setNotificationsEnabled] = useState(true);
113
59
 
114
60
  const isDark = themeMode === "dark";
115
61
  const colors = tokens.colors;
116
62
 
117
- const features = useMemo(() => {
118
- const appearanceConfig = config?.appearance ?? "auto";
119
- const notificationsConfig = config?.notifications ?? "auto";
120
- const aboutConfig = config?.about ?? "auto";
121
- const legalConfig = config?.legal ?? "auto";
122
-
123
- return {
124
- appearance:
125
- appearanceConfig === true ||
126
- (appearanceConfig === "auto" &&
127
- hasNavigationScreen(navigation, "Appearance")),
128
- notifications:
129
- notificationsConfig === true ||
130
- (notificationsConfig === "auto" &&
131
- notificationService !== null &&
132
- hasNavigationScreen(navigation, "Notifications")),
133
- about:
134
- aboutConfig === true ||
135
- (aboutConfig === "auto" && hasNavigationScreen(navigation, "About")),
136
- legal:
137
- legalConfig === true ||
138
- (legalConfig === "auto" && hasNavigationScreen(navigation, "Legal")),
139
- };
140
- }, [config, navigation]);
141
-
142
- const handleNotificationsToggle = async (value: boolean) => {
143
- if (notificationService && !value) {
144
- const hasPermissions = await notificationService.hasPermissions();
145
- if (!hasPermissions) {
146
- await notificationService.requestPermissions();
147
- }
148
- }
149
- setNotificationsEnabled(value);
150
- };
151
-
152
- const handleNotificationsPress = async () => {
153
- if (notificationService) {
154
- const hasPermissions = await notificationService.hasPermissions();
155
- if (!hasPermissions) {
156
- await notificationService.requestPermissions();
157
- }
158
- }
159
- navigation.navigate("Notifications" as never);
160
- };
63
+ const normalizedConfig = normalizeSettingsConfig(config);
64
+ const features = useFeatureDetection(normalizedConfig, navigation);
161
65
 
162
66
  const hasAnyFeatures =
163
67
  features.appearance ||
164
68
  features.notifications ||
165
69
  features.about ||
166
- features.legal;
70
+ features.legal ||
71
+ customSections.length > 0;
167
72
 
168
73
  return (
169
74
  <View style={[styles.container, { backgroundColor: colors.backgroundPrimary }]}>
@@ -192,61 +97,38 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
192
97
  )}
193
98
 
194
99
  {features.appearance && (
195
- <SettingsSection title={t("settings.sections.app.title")}>
196
- <SettingItem
197
- icon={Palette}
198
- title={t("settings.appearance.title")}
199
- value={t("settings.appearance.description")}
200
- onPress={() => navigation.navigate("Appearance" as never)}
201
- />
202
- </SettingsSection>
100
+ <AppearanceSection config={normalizedConfig.appearance.config} />
203
101
  )}
204
102
 
205
103
  {features.notifications && (
206
- <SettingsSection title={t("settings.sections.general")}>
207
- <SettingItem
208
- icon={Bell}
209
- title={t("settings.notifications.title")}
210
- showSwitch={true}
211
- switchValue={notificationsEnabled}
212
- onSwitchChange={handleNotificationsToggle}
213
- isLast={true}
214
- />
215
- </SettingsSection>
104
+ <NotificationsSection config={normalizedConfig.notifications.config} />
216
105
  )}
217
106
 
218
107
  {(features.about || features.legal) && (
219
- <SettingsSection title={t("settings.sections.about")}>
220
- {features.about && (
221
- <SettingItem
222
- icon={Info}
223
- title={t("settings.about.title")}
224
- value={t("settings.about.description")}
225
- onPress={() => navigation.navigate("About" as never)}
226
- />
227
- )}
228
- {features.legal && (
229
- <SettingItem
230
- icon={FileText}
231
- title={t("settings.legal.title")}
232
- value={t("settings.legal.description")}
233
- onPress={() => navigation.navigate("Legal" as never)}
234
- isLast={true}
235
- />
236
- )}
237
- </SettingsSection>
108
+ <AboutLegalSection
109
+ showAbout={features.about}
110
+ showLegal={features.legal}
111
+ aboutConfig={normalizedConfig.about.config}
112
+ legalConfig={normalizedConfig.legal.config}
113
+ />
238
114
  )}
239
115
 
240
- {/* Custom Sections */}
241
- {customSections
242
- .sort((a, b) => (a.order ?? 999) - (b.order ?? 999))
243
- .map((section, index) => (
244
- <SettingsSection key={`custom-${index}`} title={section.title}>
245
- {section.content}
246
- </SettingsSection>
247
- ))}
116
+ {customSections && customSections.length > 0 && (
117
+ <>
118
+ {customSections
119
+ .sort((a, b) => (a.order ?? 999) - (b.order ?? 999))
120
+ .map((section, index) => (
121
+ <SettingsSection
122
+ key={section.id || `custom-${index}`}
123
+ title={section.title}
124
+ >
125
+ {section.content}
126
+ </SettingsSection>
127
+ ))}
128
+ </>
129
+ )}
248
130
 
249
- {!hasAnyFeatures && customSections.length === 0 && (
131
+ {!hasAnyFeatures && (
250
132
  <View style={styles.emptyContainer}>
251
133
  <SettingsSection
252
134
  title={t("settings.noOptionsAvailable") || "No settings available"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * About & Legal Section Component
3
+ * Single Responsibility: Render about and legal settings section
4
+ */
5
+
6
+ import React from "react";
7
+ import { Info, FileText } from "lucide-react-native";
8
+ import { useNavigation } from "@react-navigation/native";
9
+ import { useLocalization } from "@umituz/react-native-localization";
10
+ import { SettingItem } from "../../components/SettingItem";
11
+ import { SettingsSection } from "../../components/SettingsSection";
12
+ import type { AboutConfig, LegalConfig } from "../types";
13
+
14
+ interface AboutLegalSectionProps {
15
+ showAbout: boolean;
16
+ showLegal: boolean;
17
+ aboutConfig?: AboutConfig;
18
+ legalConfig?: LegalConfig;
19
+ }
20
+
21
+ export const AboutLegalSection: React.FC<AboutLegalSectionProps> = ({
22
+ showAbout,
23
+ showLegal,
24
+ aboutConfig,
25
+ legalConfig,
26
+ }) => {
27
+ const navigation = useNavigation();
28
+ const { t } = useLocalization();
29
+
30
+ if (!showAbout && !showLegal) {
31
+ return null;
32
+ }
33
+
34
+ const aboutRoute = aboutConfig?.route || "About";
35
+ const aboutTitle = aboutConfig?.title || t("settings.about.title");
36
+ const aboutDescription =
37
+ aboutConfig?.description || t("settings.about.description");
38
+
39
+ const legalRoute = legalConfig?.route || "Legal";
40
+ const legalTitle = legalConfig?.title || t("settings.legal.title");
41
+ const legalDescription =
42
+ legalConfig?.description || t("settings.legal.description");
43
+
44
+ return (
45
+ <SettingsSection title={t("settings.sections.about")}>
46
+ {showAbout && (
47
+ <SettingItem
48
+ icon={Info}
49
+ title={aboutTitle}
50
+ value={aboutDescription}
51
+ onPress={() => navigation.navigate(aboutRoute as never)}
52
+ />
53
+ )}
54
+ {showLegal && (
55
+ <SettingItem
56
+ icon={FileText}
57
+ title={legalTitle}
58
+ value={legalDescription}
59
+ onPress={() => navigation.navigate(legalRoute as never)}
60
+ isLast={true}
61
+ />
62
+ )}
63
+ </SettingsSection>
64
+ );
65
+ };
66
+
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Appearance Section Component
3
+ * Single Responsibility: Render appearance settings section
4
+ */
5
+
6
+ import React from "react";
7
+ import { Palette } from "lucide-react-native";
8
+ import { useNavigation } from "@react-navigation/native";
9
+ import { useLocalization } from "@umituz/react-native-localization";
10
+ import { SettingItem } from "../../components/SettingItem";
11
+ import { SettingsSection } from "../../components/SettingsSection";
12
+ import type { AppearanceConfig } from "../types";
13
+
14
+ interface AppearanceSectionProps {
15
+ config?: AppearanceConfig;
16
+ }
17
+
18
+ export const AppearanceSection: React.FC<AppearanceSectionProps> = ({
19
+ config,
20
+ }) => {
21
+ const navigation = useNavigation();
22
+ const { t } = useLocalization();
23
+
24
+ const route = config?.route || "Appearance";
25
+ const title = config?.title || t("settings.appearance.title");
26
+ const description = config?.description || t("settings.appearance.description");
27
+
28
+ return (
29
+ <SettingsSection title={t("settings.sections.app.title")}>
30
+ <SettingItem
31
+ icon={Palette}
32
+ title={title}
33
+ value={description}
34
+ onPress={() => navigation.navigate(route as never)}
35
+ />
36
+ </SettingsSection>
37
+ );
38
+ };
39
+
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Notifications Section Component
3
+ * Single Responsibility: Render notifications settings section
4
+ */
5
+
6
+ import React, { useState, useEffect } from "react";
7
+ import { Bell } from "lucide-react-native";
8
+ import { useNavigation } from "@react-navigation/native";
9
+ import { useLocalization } from "@umituz/react-native-localization";
10
+ import { SettingItem } from "../../components/SettingItem";
11
+ import { SettingsSection } from "../../components/SettingsSection";
12
+ import type { NotificationsConfig } from "../types";
13
+
14
+ // Optional notification service
15
+ let notificationService: any = null;
16
+ try {
17
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
18
+ notificationService = require("@umituz/react-native-notifications")
19
+ .notificationService;
20
+ } catch {
21
+ // Package not available
22
+ }
23
+
24
+ interface NotificationsSectionProps {
25
+ config?: NotificationsConfig;
26
+ }
27
+
28
+ export const NotificationsSection: React.FC<NotificationsSectionProps> = ({
29
+ config,
30
+ }) => {
31
+ const navigation = useNavigation();
32
+ const { t } = useLocalization();
33
+ const [notificationsEnabled, setNotificationsEnabled] = useState(
34
+ config?.initialValue ?? true,
35
+ );
36
+
37
+ useEffect(() => {
38
+ if (config?.initialValue !== undefined) {
39
+ setNotificationsEnabled(config.initialValue);
40
+ }
41
+ }, [config?.initialValue]);
42
+
43
+ const handleToggle = async (value: boolean) => {
44
+ if (notificationService && !value) {
45
+ const hasPermissions = await notificationService.hasPermissions();
46
+ if (!hasPermissions) {
47
+ await notificationService.requestPermissions();
48
+ }
49
+ }
50
+
51
+ setNotificationsEnabled(value);
52
+ config?.onToggleChange?.(value);
53
+ };
54
+
55
+ const handlePress = async () => {
56
+ if (notificationService) {
57
+ const hasPermissions = await notificationService.hasPermissions();
58
+ if (!hasPermissions) {
59
+ await notificationService.requestPermissions();
60
+ }
61
+ }
62
+ navigation.navigate((config?.route || "Notifications") as never);
63
+ };
64
+
65
+ const title = config?.title || t("settings.notifications.title");
66
+ const description = config?.description || t("settings.notifications.description");
67
+ const showToggle = config?.showToggle ?? true;
68
+
69
+ return (
70
+ <SettingsSection title={t("settings.sections.general")}>
71
+ {showToggle ? (
72
+ <SettingItem
73
+ icon={Bell}
74
+ title={title}
75
+ showSwitch={true}
76
+ switchValue={notificationsEnabled}
77
+ onSwitchChange={handleToggle}
78
+ isLast={true}
79
+ />
80
+ ) : (
81
+ <SettingItem
82
+ icon={Bell}
83
+ title={title}
84
+ value={description}
85
+ onPress={handlePress}
86
+ isLast={true}
87
+ />
88
+ )}
89
+ </SettingsSection>
90
+ );
91
+ };
92
+
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Settings Screen Components
3
+ * Barrel export for all section components
4
+ */
5
+
6
+ export { AppearanceSection } from "./AppearanceSection";
7
+ export { NotificationsSection } from "./NotificationsSection";
8
+ export { AboutLegalSection } from "./AboutLegalSection";
9
+
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Feature Detection Hook
3
+ * Single Responsibility: Detect which features should be shown
4
+ */
5
+
6
+ import { useMemo } from "react";
7
+ import type { NormalizedConfig } from "../utils/normalizeConfig";
8
+
9
+ // Optional notification service
10
+ let notificationService: any = null;
11
+ try {
12
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
13
+ notificationService = require("@umituz/react-native-notifications")
14
+ .notificationService;
15
+ } catch {
16
+ // Package not available
17
+ }
18
+
19
+ /**
20
+ * Check if navigation screen exists
21
+ */
22
+ function hasNavigationScreen(
23
+ navigation: any,
24
+ screenName: string,
25
+ ): boolean {
26
+ try {
27
+ const state = navigation.getState();
28
+ if (!state) return false;
29
+
30
+ const checkRoutes = (routes: any[]): boolean => {
31
+ if (!routes || !Array.isArray(routes)) return false;
32
+
33
+ for (const route of routes) {
34
+ if (route.name === screenName) return true;
35
+ if (route.state?.routes && checkRoutes(route.state.routes)) {
36
+ return true;
37
+ }
38
+ }
39
+ return false;
40
+ };
41
+
42
+ return checkRoutes(state.routes || []);
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Hook to detect which features should be shown
50
+ */
51
+ export function useFeatureDetection(
52
+ normalizedConfig: NormalizedConfig,
53
+ navigation: any,
54
+ ) {
55
+ return useMemo(() => {
56
+ const { appearance, notifications, about, legal, account, support, developer } =
57
+ normalizedConfig;
58
+
59
+ return {
60
+ appearance:
61
+ appearance.enabled &&
62
+ (appearance.config?.enabled === true ||
63
+ (appearance.config?.enabled !== false &&
64
+ hasNavigationScreen(
65
+ navigation,
66
+ appearance.config?.route || "Appearance",
67
+ ))),
68
+ notifications:
69
+ notifications.enabled &&
70
+ (notifications.config?.enabled === true ||
71
+ (notifications.config?.enabled !== false &&
72
+ notificationService !== null &&
73
+ hasNavigationScreen(
74
+ navigation,
75
+ notifications.config?.route || "Notifications",
76
+ ))),
77
+ about:
78
+ about.enabled &&
79
+ (about.config?.enabled === true ||
80
+ (about.config?.enabled !== false &&
81
+ hasNavigationScreen(navigation, about.config?.route || "About"))),
82
+ legal:
83
+ legal.enabled &&
84
+ (legal.config?.enabled === true ||
85
+ (legal.config?.enabled !== false &&
86
+ hasNavigationScreen(navigation, legal.config?.route || "Legal"))),
87
+ account:
88
+ account.enabled &&
89
+ (account.config?.enabled === true ||
90
+ (account.config?.enabled !== false &&
91
+ hasNavigationScreen(
92
+ navigation,
93
+ account.config?.route || "AccountSettings",
94
+ ))),
95
+ support: support.enabled,
96
+ developer: developer.enabled && __DEV__,
97
+ };
98
+ }, [normalizedConfig, navigation]);
99
+ }
100
+
@@ -1,36 +1,232 @@
1
1
  /**
2
- * Settings Configuration
2
+ * Settings Configuration Types
3
3
  *
4
- * Controls which settings features are visible in the SettingsScreen.
5
- * Each feature can be:
4
+ * Comprehensive configuration system for SettingsScreen
5
+ * Supports built-in features and custom sections
6
+ */
7
+
8
+ import type { ComponentType, ReactNode } from "react";
9
+
10
+ /**
11
+ * Feature visibility configuration
6
12
  * - true: Always show (if navigation screen exists)
7
13
  * - false: Never show
8
14
  * - 'auto': Automatically detect (check if navigation screen exists and package is available)
9
15
  */
16
+ export type FeatureVisibility = boolean | "auto";
17
+
18
+ /**
19
+ * Appearance Settings Configuration
20
+ */
21
+ export interface AppearanceConfig {
22
+ /** Show appearance section */
23
+ enabled?: FeatureVisibility;
24
+ /** Custom navigation route for appearance screen */
25
+ route?: string;
26
+ /** Show language selection */
27
+ showLanguage?: boolean;
28
+ /** Show theme toggle */
29
+ showTheme?: boolean;
30
+ /** Custom appearance title */
31
+ title?: string;
32
+ /** Custom appearance description */
33
+ description?: string;
34
+ }
35
+
36
+ /**
37
+ * Notifications Settings Configuration
38
+ */
39
+ export interface NotificationsConfig {
40
+ /** Show notifications section */
41
+ enabled?: FeatureVisibility;
42
+ /** Custom navigation route for notifications screen */
43
+ route?: string;
44
+ /** Show notifications toggle switch */
45
+ showToggle?: boolean;
46
+ /** Initial notifications state */
47
+ initialValue?: boolean;
48
+ /** Custom toggle handler */
49
+ onToggleChange?: (value: boolean) => void;
50
+ /** Custom notifications title */
51
+ title?: string;
52
+ /** Custom notifications description */
53
+ description?: string;
54
+ }
55
+
56
+ /**
57
+ * About Settings Configuration
58
+ */
59
+ export interface AboutConfig {
60
+ /** Show about section */
61
+ enabled?: FeatureVisibility;
62
+ /** Custom navigation route for about screen */
63
+ route?: string;
64
+ /** Custom about title */
65
+ title?: string;
66
+ /** Custom about description */
67
+ description?: string;
68
+ }
69
+
70
+ /**
71
+ * Legal Settings Configuration
72
+ */
73
+ export interface LegalConfig {
74
+ /** Show legal section */
75
+ enabled?: FeatureVisibility;
76
+ /** Custom navigation route for legal screen */
77
+ route?: string;
78
+ /** Custom legal title */
79
+ title?: string;
80
+ /** Custom legal description */
81
+ description?: string;
82
+ }
83
+
84
+ /**
85
+ * Account Settings Configuration
86
+ */
87
+ export interface AccountConfig {
88
+ /** Show account section */
89
+ enabled?: FeatureVisibility;
90
+ /** Custom navigation route for account screen */
91
+ route?: string;
92
+ /** Custom account title */
93
+ title?: string;
94
+ /** Custom account description */
95
+ description?: string;
96
+ }
97
+
98
+ /**
99
+ * Support Settings Configuration
100
+ */
101
+ export interface SupportConfig {
102
+ /** Show support section */
103
+ enabled?: FeatureVisibility;
104
+ /** Support items configuration */
105
+ items?: {
106
+ /** Live support configuration */
107
+ liveSupport?: {
108
+ enabled?: boolean;
109
+ route?: string;
110
+ title?: string;
111
+ description?: string;
112
+ };
113
+ /** Help support configuration */
114
+ helpSupport?: {
115
+ enabled?: boolean;
116
+ route?: string;
117
+ title?: string;
118
+ description?: string;
119
+ };
120
+ };
121
+ /** Custom support section title */
122
+ title?: string;
123
+ }
124
+
125
+ /**
126
+ * Developer Settings Configuration
127
+ */
128
+ export interface DeveloperConfig {
129
+ /** Show developer section (only in __DEV__ mode) */
130
+ enabled?: boolean;
131
+ /** Developer settings items */
132
+ items?: Array<{
133
+ title: string;
134
+ description?: string;
135
+ route?: string;
136
+ onPress?: () => void;
137
+ icon?: ComponentType<{ size?: number; color?: string }>;
138
+ iconColor?: string;
139
+ titleColor?: string;
140
+ }>;
141
+ /** Custom developer section title */
142
+ title?: string;
143
+ }
144
+
145
+ /**
146
+ * Main Settings Configuration
147
+ *
148
+ * Controls which settings features are visible in the SettingsScreen.
149
+ * Each feature can be configured with:
150
+ * - Simple: boolean | 'auto' (quick setup)
151
+ * - Advanced: Detailed config object (full control)
152
+ *
153
+ * @example
154
+ * // Simple configuration
155
+ * const config: SettingsConfig = {
156
+ * appearance: true,
157
+ * notifications: 'auto',
158
+ * about: false,
159
+ * };
160
+ *
161
+ * @example
162
+ * // Advanced configuration
163
+ * const config: SettingsConfig = {
164
+ * appearance: {
165
+ * enabled: true,
166
+ * route: 'CustomAppearance',
167
+ * showLanguage: true,
168
+ * showTheme: true,
169
+ * },
170
+ * notifications: {
171
+ * enabled: 'auto',
172
+ * showToggle: true,
173
+ * initialValue: false,
174
+ * onToggleChange: (value) => console.log(value),
175
+ * },
176
+ * support: {
177
+ * enabled: true,
178
+ * items: {
179
+ * liveSupport: {
180
+ * enabled: true,
181
+ * route: 'ChatSupport',
182
+ * title: 'Live Chat',
183
+ * },
184
+ * },
185
+ * },
186
+ * };
187
+ */
10
188
  export interface SettingsConfig {
11
189
  /**
12
- * Show Appearance settings (Theme & Language)
190
+ * Appearance settings (Theme & Language)
13
191
  * @default 'auto'
14
192
  */
15
- appearance?: boolean | 'auto';
193
+ appearance?: FeatureVisibility | AppearanceConfig;
16
194
 
17
195
  /**
18
- * Show Notifications settings
196
+ * Notifications settings
19
197
  * @default 'auto'
20
198
  */
21
- notifications?: boolean | 'auto';
199
+ notifications?: FeatureVisibility | NotificationsConfig;
22
200
 
23
201
  /**
24
- * Show About settings
202
+ * About settings
25
203
  * @default 'auto'
26
204
  */
27
- about?: boolean | 'auto';
205
+ about?: FeatureVisibility | AboutConfig;
28
206
 
29
207
  /**
30
- * Show Legal settings (Terms, Privacy Policy)
208
+ * Legal settings (Terms, Privacy Policy)
31
209
  * @default 'auto'
32
210
  */
33
- legal?: boolean | 'auto';
211
+ legal?: FeatureVisibility | LegalConfig;
212
+
213
+ /**
214
+ * Account settings
215
+ * @default false
216
+ */
217
+ account?: FeatureVisibility | AccountConfig;
218
+
219
+ /**
220
+ * Support settings
221
+ * @default false
222
+ */
223
+ support?: FeatureVisibility | SupportConfig;
224
+
225
+ /**
226
+ * Developer settings (only shown in __DEV__ mode)
227
+ * @default false
228
+ */
229
+ developer?: boolean | DeveloperConfig;
34
230
  }
35
231
 
36
232
  /**
@@ -41,8 +237,9 @@ export interface CustomSettingsSection {
41
237
  /** Section title */
42
238
  title: string;
43
239
  /** Section content (React nodes) */
44
- content: React.ReactNode;
240
+ content: ReactNode;
45
241
  /** Section order (lower = higher in list) */
46
242
  order?: number;
243
+ /** Section ID for identification */
244
+ id?: string;
47
245
  }
48
-
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Config Normalization Utilities
3
+ * Single Responsibility: Normalize config values to consistent format
4
+ */
5
+
6
+ import type {
7
+ FeatureVisibility,
8
+ AppearanceConfig,
9
+ NotificationsConfig,
10
+ AboutConfig,
11
+ LegalConfig,
12
+ AccountConfig,
13
+ SupportConfig,
14
+ DeveloperConfig,
15
+ } from "../types";
16
+
17
+ export interface NormalizedConfig {
18
+ appearance: {
19
+ enabled: boolean;
20
+ config?: AppearanceConfig;
21
+ };
22
+ notifications: {
23
+ enabled: boolean;
24
+ config?: NotificationsConfig;
25
+ };
26
+ about: {
27
+ enabled: boolean;
28
+ config?: AboutConfig;
29
+ };
30
+ legal: {
31
+ enabled: boolean;
32
+ config?: LegalConfig;
33
+ };
34
+ account: {
35
+ enabled: boolean;
36
+ config?: AccountConfig;
37
+ };
38
+ support: {
39
+ enabled: boolean;
40
+ config?: SupportConfig;
41
+ };
42
+ developer: {
43
+ enabled: boolean;
44
+ config?: DeveloperConfig;
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Normalize a config value to enabled boolean and optional config object
50
+ */
51
+ function normalizeConfigValue<T>(
52
+ value: FeatureVisibility | T | undefined,
53
+ defaultValue: FeatureVisibility,
54
+ ): { enabled: boolean; config?: T } {
55
+ if (value === undefined) {
56
+ return { enabled: defaultValue === true };
57
+ }
58
+
59
+ if (typeof value === "boolean" || value === "auto") {
60
+ return { enabled: value === true };
61
+ }
62
+
63
+ // It's a config object
64
+ const config = value as T;
65
+ const enabled =
66
+ (config as { enabled?: FeatureVisibility })?.enabled ?? defaultValue;
67
+ return {
68
+ enabled: enabled === true,
69
+ config,
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Normalize entire SettingsConfig to consistent format
75
+ */
76
+ export function normalizeSettingsConfig(
77
+ config: any,
78
+ ): NormalizedConfig {
79
+ return {
80
+ appearance: normalizeConfigValue(config?.appearance, "auto"),
81
+ notifications: normalizeConfigValue(config?.notifications, "auto"),
82
+ about: normalizeConfigValue(config?.about, "auto"),
83
+ legal: normalizeConfigValue(config?.legal, "auto"),
84
+ account: normalizeConfigValue(config?.account, false),
85
+ support: normalizeConfigValue(config?.support, false),
86
+ developer: normalizeConfigValue(config?.developer, false),
87
+ };
88
+ }
89
+