@umituz/react-native-settings 1.6.2 → 1.8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
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",
package/src/index.ts CHANGED
@@ -41,7 +41,7 @@ export { LanguageSelectionScreen } from './presentation/screens/LanguageSelectio
41
41
  // PRESENTATION LAYER - Types
42
42
  // =============================================================================
43
43
 
44
- export type { SettingsConfig } from './presentation/screens/types';
44
+ export type { SettingsConfig, CustomSettingsSection } from './presentation/screens/types';
45
45
 
46
46
  // =============================================================================
47
47
  // PRESENTATION LAYER - Components
@@ -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 } 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 */
@@ -92,6 +39,8 @@ export interface SettingsScreenProps {
92
39
  showFooter?: boolean;
93
40
  /** Custom footer text */
94
41
  footerText?: string;
42
+ /** Custom sections to render */
43
+ customSections?: CustomSettingsSection[];
95
44
  }
96
45
 
97
46
  export const SettingsScreen: React.FC<SettingsScreenProps> = ({
@@ -100,67 +49,26 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
100
49
  userProfile,
101
50
  showFooter = true,
102
51
  footerText,
52
+ customSections = [],
103
53
  }) => {
104
54
  const navigation = useNavigation();
105
55
  const { themeMode } = useDesignSystemTheme();
106
56
  const tokens = useAppDesignTokens();
107
57
  const insets = useSafeAreaInsets();
108
58
  const { t } = useLocalization();
109
- const [notificationsEnabled, setNotificationsEnabled] = useState(true);
110
59
 
111
60
  const isDark = themeMode === "dark";
112
61
  const colors = tokens.colors;
113
62
 
114
- const features = useMemo(() => {
115
- const appearanceConfig = config?.appearance ?? "auto";
116
- const notificationsConfig = config?.notifications ?? "auto";
117
- const aboutConfig = config?.about ?? "auto";
118
- const legalConfig = config?.legal ?? "auto";
119
-
120
- return {
121
- appearance:
122
- appearanceConfig === true ||
123
- (appearanceConfig === "auto" &&
124
- hasNavigationScreen(navigation, "Appearance")),
125
- notifications:
126
- notificationsConfig === true ||
127
- (notificationsConfig === "auto" &&
128
- notificationService !== null &&
129
- hasNavigationScreen(navigation, "Notifications")),
130
- about:
131
- aboutConfig === true ||
132
- (aboutConfig === "auto" && hasNavigationScreen(navigation, "About")),
133
- legal:
134
- legalConfig === true ||
135
- (legalConfig === "auto" && hasNavigationScreen(navigation, "Legal")),
136
- };
137
- }, [config, navigation]);
138
-
139
- const handleNotificationsToggle = async (value: boolean) => {
140
- if (notificationService && !value) {
141
- const hasPermissions = await notificationService.hasPermissions();
142
- if (!hasPermissions) {
143
- await notificationService.requestPermissions();
144
- }
145
- }
146
- setNotificationsEnabled(value);
147
- };
148
-
149
- const handleNotificationsPress = async () => {
150
- if (notificationService) {
151
- const hasPermissions = await notificationService.hasPermissions();
152
- if (!hasPermissions) {
153
- await notificationService.requestPermissions();
154
- }
155
- }
156
- navigation.navigate("Notifications" as never);
157
- };
63
+ const normalizedConfig = normalizeSettingsConfig(config);
64
+ const features = useFeatureDetection(normalizedConfig, navigation);
158
65
 
159
66
  const hasAnyFeatures =
160
67
  features.appearance ||
161
68
  features.notifications ||
162
69
  features.about ||
163
- features.legal;
70
+ features.legal ||
71
+ customSections.length > 0;
164
72
 
165
73
  return (
166
74
  <View style={[styles.container, { backgroundColor: colors.backgroundPrimary }]}>
@@ -189,49 +97,35 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
189
97
  )}
190
98
 
191
99
  {features.appearance && (
192
- <SettingsSection title={t("settings.sections.app.title")}>
193
- <SettingItem
194
- icon={Palette}
195
- title={t("settings.appearance.title")}
196
- value={t("settings.appearance.description")}
197
- onPress={() => navigation.navigate("Appearance" as never)}
198
- />
199
- </SettingsSection>
100
+ <AppearanceSection config={normalizedConfig.appearance.config} />
200
101
  )}
201
102
 
202
103
  {features.notifications && (
203
- <SettingsSection title={t("settings.sections.general")}>
204
- <SettingItem
205
- icon={Bell}
206
- title={t("settings.notifications.title")}
207
- showSwitch={true}
208
- switchValue={notificationsEnabled}
209
- onSwitchChange={handleNotificationsToggle}
210
- isLast={true}
211
- />
212
- </SettingsSection>
104
+ <NotificationsSection config={normalizedConfig.notifications.config} />
213
105
  )}
214
106
 
215
107
  {(features.about || features.legal) && (
216
- <SettingsSection title={t("settings.sections.about")}>
217
- {features.about && (
218
- <SettingItem
219
- icon={Info}
220
- title={t("settings.about.title")}
221
- value={t("settings.about.description")}
222
- onPress={() => navigation.navigate("About" as never)}
223
- />
224
- )}
225
- {features.legal && (
226
- <SettingItem
227
- icon={FileText}
228
- title={t("settings.legal.title")}
229
- value={t("settings.legal.description")}
230
- onPress={() => navigation.navigate("Legal" as never)}
231
- isLast={true}
232
- />
233
- )}
234
- </SettingsSection>
108
+ <AboutLegalSection
109
+ showAbout={features.about}
110
+ showLegal={features.legal}
111
+ aboutConfig={normalizedConfig.about.config}
112
+ legalConfig={normalizedConfig.legal.config}
113
+ />
114
+ )}
115
+
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
+ </>
235
129
  )}
236
130
 
237
131
  {!hasAnyFeatures && (
@@ -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,35 +1,245 @@
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
 
232
+ /**
233
+ * Custom Settings Section
234
+ * Allows apps to add custom sections to the settings screen
235
+ */
236
+ export interface CustomSettingsSection {
237
+ /** Section title */
238
+ title: string;
239
+ /** Section content (React nodes) */
240
+ content: ReactNode;
241
+ /** Section order (lower = higher in list) */
242
+ order?: number;
243
+ /** Section ID for identification */
244
+ id?: string;
245
+ }
@@ -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
+