@umituz/react-native-settings 4.7.2 → 4.9.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 +2 -1
- package/src/index.ts +2 -0
- package/src/infrastructure/storage/SettingsStore.ts +6 -6
- package/src/presentation/components/DevSettingsSection.tsx +95 -0
- package/src/presentation/navigation/SettingsStackNavigator.tsx +9 -1
- package/src/presentation/screens/SettingsScreen.tsx +5 -0
- package/src/presentation/screens/components/SettingsContent.tsx +66 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-settings",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.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",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"@babel/plugin-transform-runtime": "^7.28.5",
|
|
52
52
|
"@expo/vector-icons": "^14.0.0",
|
|
53
53
|
"@react-native/babel-preset": "^0.83.0",
|
|
54
|
+
"@react-navigation/native": "^6.1.17",
|
|
54
55
|
"@react-navigation/stack": "^7.6.12",
|
|
55
56
|
"@testing-library/jest-native": "^5.4.3",
|
|
56
57
|
"@testing-library/react-hooks": "^8.0.1",
|
package/src/index.ts
CHANGED
|
@@ -76,3 +76,5 @@ export type { CloudSyncSettingProps } from './presentation/components/CloudSyncS
|
|
|
76
76
|
export { StorageClearSetting } from './presentation/components/StorageClearSetting';
|
|
77
77
|
export type { StorageClearSettingProps } from './presentation/components/StorageClearSetting';
|
|
78
78
|
|
|
79
|
+
export { DevSettingsSection } from './presentation/components/DevSettingsSection';
|
|
80
|
+
export type { DevSettingsProps } from './presentation/components/DevSettingsSection';
|
|
@@ -35,7 +35,7 @@ const getDefaultSettings = (userId: string): UserSettings => {
|
|
|
35
35
|
if (DEFAULT_SETTINGS_CACHE.has(userId)) {
|
|
36
36
|
return DEFAULT_SETTINGS_CACHE.get(userId)!;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
const settings = {
|
|
40
40
|
userId,
|
|
41
41
|
theme: 'auto' as const,
|
|
@@ -48,7 +48,7 @@ const getDefaultSettings = (userId: string): UserSettings => {
|
|
|
48
48
|
privacyMode: false,
|
|
49
49
|
updatedAt: new Date(),
|
|
50
50
|
};
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
DEFAULT_SETTINGS_CACHE.set(userId, settings);
|
|
53
53
|
return settings;
|
|
54
54
|
};
|
|
@@ -62,12 +62,12 @@ export const useSettingsStore = create<SettingsStore>((set, get) => ({
|
|
|
62
62
|
if (__DEV__) {
|
|
63
63
|
console.log('SettingsStore: Loading settings for user:', userId);
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
set({ loading: true, error: null });
|
|
67
67
|
|
|
68
68
|
try {
|
|
69
69
|
const defaultSettings = getDefaultSettings(userId);
|
|
70
|
-
const storageKey = createUserKey(StorageKey.
|
|
70
|
+
const storageKey = createUserKey(StorageKey.USER_PREFERENCES, userId);
|
|
71
71
|
|
|
72
72
|
// ✅ DRY: Storage domain handles JSON parse, error handling
|
|
73
73
|
const result = await storageRepository.getItem<UserSettings>(storageKey, defaultSettings);
|
|
@@ -127,7 +127,7 @@ export const useSettingsStore = create<SettingsStore>((set, get) => ({
|
|
|
127
127
|
updatedAt: new Date(),
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
-
const storageKey = createUserKey(StorageKey.
|
|
130
|
+
const storageKey = createUserKey(StorageKey.USER_PREFERENCES, currentSettings.userId);
|
|
131
131
|
|
|
132
132
|
// ✅ DRY: Storage domain replaces JSON.stringify + AsyncStorage + try/catch
|
|
133
133
|
const result = await storageRepository.setItem(storageKey, updatedSettings);
|
|
@@ -153,7 +153,7 @@ export const useSettingsStore = create<SettingsStore>((set, get) => ({
|
|
|
153
153
|
set({ loading: true, error: null });
|
|
154
154
|
|
|
155
155
|
const defaultSettings = getDefaultSettings(userId);
|
|
156
|
-
const storageKey = createUserKey(StorageKey.
|
|
156
|
+
const storageKey = createUserKey(StorageKey.USER_PREFERENCES, userId);
|
|
157
157
|
|
|
158
158
|
// ✅ DRY: Storage domain replaces JSON.stringify + AsyncStorage + try/catch
|
|
159
159
|
const result = await storageRepository.setItem(storageKey, defaultSettings);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev Settings Section Component
|
|
3
|
+
* Only visible in __DEV__ mode
|
|
4
|
+
* Provides development utilities like clearing storage
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { Alert } from "react-native";
|
|
9
|
+
import { Feather } from "@expo/vector-icons";
|
|
10
|
+
import { useLocalization } from "@umituz/react-native-localization";
|
|
11
|
+
import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
|
|
12
|
+
import { SettingsSection } from "./SettingsSection";
|
|
13
|
+
import { SettingItem } from "./SettingItem";
|
|
14
|
+
|
|
15
|
+
// Icon wrapper for SettingItem compatibility
|
|
16
|
+
const TrashIcon: React.FC<{ size?: number; color?: string }> = ({ size = 24, color }) => (
|
|
17
|
+
<Feather name="trash-2" size={size} color={color} />
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export interface DevSettingsProps {
|
|
21
|
+
/** Callback to clear all storage and reset app */
|
|
22
|
+
onClearAllData?: () => Promise<void>;
|
|
23
|
+
/** Custom title for the section */
|
|
24
|
+
sectionTitle?: string;
|
|
25
|
+
/** Custom title for clear data button */
|
|
26
|
+
clearDataTitle?: string;
|
|
27
|
+
/** Custom description for clear data button */
|
|
28
|
+
clearDataDescription?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const DevSettingsSection: React.FC<DevSettingsProps> = ({
|
|
32
|
+
onClearAllData,
|
|
33
|
+
sectionTitle,
|
|
34
|
+
clearDataTitle,
|
|
35
|
+
clearDataDescription,
|
|
36
|
+
}) => {
|
|
37
|
+
const { t } = useLocalization();
|
|
38
|
+
const tokens = useAppDesignTokens();
|
|
39
|
+
|
|
40
|
+
// Only render in development mode
|
|
41
|
+
if (!__DEV__) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Don't render if no handler provided
|
|
46
|
+
if (!onClearAllData) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const handleClearData = () => {
|
|
51
|
+
Alert.alert(
|
|
52
|
+
t("settings.devSettings.clearData.confirmTitle") || "Clear All Data?",
|
|
53
|
+
t("settings.devSettings.clearData.confirmMessage") || "This will clear all app data and reset to initial state. This action cannot be undone.",
|
|
54
|
+
[
|
|
55
|
+
{
|
|
56
|
+
text: t("common.cancel") || "Cancel",
|
|
57
|
+
style: "cancel",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
text: t("common.confirm") || "Confirm",
|
|
61
|
+
style: "destructive",
|
|
62
|
+
onPress: async () => {
|
|
63
|
+
try {
|
|
64
|
+
await onClearAllData();
|
|
65
|
+
} catch {
|
|
66
|
+
Alert.alert(
|
|
67
|
+
t("common.error") || "Error",
|
|
68
|
+
t("settings.devSettings.clearData.error") || "Failed to clear data"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
]
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<SettingsSection
|
|
79
|
+
title={sectionTitle || t("settings.devSettings.title") || "Developer"}
|
|
80
|
+
>
|
|
81
|
+
<SettingItem
|
|
82
|
+
icon={TrashIcon}
|
|
83
|
+
title={clearDataTitle || t("settings.devSettings.clearData.title") || "Clear All Data"}
|
|
84
|
+
value={
|
|
85
|
+
clearDataDescription ||
|
|
86
|
+
t("settings.devSettings.clearData.description") || "Reset app to initial state (DEV only)"
|
|
87
|
+
}
|
|
88
|
+
onPress={handleClearData}
|
|
89
|
+
iconColor={tokens.colors.error}
|
|
90
|
+
titleColor={tokens.colors.error}
|
|
91
|
+
isLast={true}
|
|
92
|
+
/>
|
|
93
|
+
</SettingsSection>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
@@ -11,6 +11,7 @@ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
|
|
|
11
11
|
import { SettingsScreen } from "../screens/SettingsScreen";
|
|
12
12
|
import { AppearanceScreen } from "../screens/AppearanceScreen";
|
|
13
13
|
import type { SettingsConfig } from "../screens/types";
|
|
14
|
+
import type { DevSettingsProps } from "../components/DevSettingsSection";
|
|
14
15
|
|
|
15
16
|
// Default param list - can be extended by apps
|
|
16
17
|
export type SettingsStackParamList = {
|
|
@@ -58,6 +59,11 @@ export interface SettingsStackNavigatorProps {
|
|
|
58
59
|
component: React.ComponentType<any>;
|
|
59
60
|
options?: any;
|
|
60
61
|
}>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Dev settings (only shown in __DEV__ mode)
|
|
65
|
+
*/
|
|
66
|
+
devSettings?: DevSettingsProps;
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
const Stack = createStackNavigator<SettingsStackParamList>();
|
|
@@ -68,6 +74,7 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
|
|
|
68
74
|
showUserProfile = false,
|
|
69
75
|
userProfile,
|
|
70
76
|
additionalScreens = [],
|
|
77
|
+
devSettings,
|
|
71
78
|
}) => {
|
|
72
79
|
const tokens = useAppDesignTokens();
|
|
73
80
|
|
|
@@ -93,11 +100,12 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
|
|
|
93
100
|
appVersion={appVersion}
|
|
94
101
|
showUserProfile={showUserProfile}
|
|
95
102
|
userProfile={userProfile}
|
|
103
|
+
devSettings={devSettings}
|
|
96
104
|
/>
|
|
97
105
|
);
|
|
98
106
|
Wrapper.displayName = "SettingsScreenWrapper";
|
|
99
107
|
return Wrapper;
|
|
100
|
-
}, [config, appVersion, showUserProfile, userProfile]);
|
|
108
|
+
}, [config, appVersion, showUserProfile, userProfile, devSettings]);
|
|
101
109
|
|
|
102
110
|
return (
|
|
103
111
|
<Stack.Navigator screenOptions={screenOptions}>
|
|
@@ -16,6 +16,7 @@ import { SettingsErrorBoundary } from "../components/SettingsErrorBoundary";
|
|
|
16
16
|
import { normalizeSettingsConfig } from "./utils/normalizeConfig";
|
|
17
17
|
import { useFeatureDetection } from "./hooks/useFeatureDetection";
|
|
18
18
|
import type { SettingsConfig, CustomSettingsSection } from "./types";
|
|
19
|
+
import type { DevSettingsProps } from "../components/DevSettingsSection";
|
|
19
20
|
|
|
20
21
|
export interface SettingsScreenProps {
|
|
21
22
|
config?: SettingsConfig;
|
|
@@ -48,6 +49,8 @@ export interface SettingsScreenProps {
|
|
|
48
49
|
featureOptions?: {
|
|
49
50
|
notificationServiceAvailable?: boolean;
|
|
50
51
|
};
|
|
52
|
+
/** Dev settings (only shown in __DEV__ mode) */
|
|
53
|
+
devSettings?: DevSettingsProps;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
|
@@ -61,6 +64,7 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
|
|
61
64
|
showCloseButton = false,
|
|
62
65
|
onClose,
|
|
63
66
|
featureOptions,
|
|
67
|
+
devSettings,
|
|
64
68
|
}) => {
|
|
65
69
|
const navigation = useNavigation();
|
|
66
70
|
const { themeMode } = useDesignSystemTheme();
|
|
@@ -92,6 +96,7 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
|
|
92
96
|
appVersion={appVersion}
|
|
93
97
|
customSections={customSections}
|
|
94
98
|
showCloseButton={showCloseButton}
|
|
99
|
+
devSettings={devSettings}
|
|
95
100
|
/>
|
|
96
101
|
</SettingsErrorBoundary>
|
|
97
102
|
</View>
|
|
@@ -11,6 +11,7 @@ import { useLocalization } from "@umituz/react-native-localization";
|
|
|
11
11
|
import { SettingsFooter } from "../../components/SettingsFooter";
|
|
12
12
|
import { UserProfileHeader } from "../../components/UserProfileHeader";
|
|
13
13
|
import { SettingsSection } from "../../components/SettingsSection";
|
|
14
|
+
import { DevSettingsSection, DevSettingsProps } from "../../components/DevSettingsSection";
|
|
14
15
|
import { NotificationsSection } from "@umituz/react-native-notifications";
|
|
15
16
|
import { AboutSection } from "@umituz/react-native-about";
|
|
16
17
|
import { LegalSection } from "@umituz/react-native-legal";
|
|
@@ -59,6 +60,8 @@ interface SettingsContentProps {
|
|
|
59
60
|
appVersion?: string;
|
|
60
61
|
customSections?: CustomSettingsSection[];
|
|
61
62
|
showCloseButton?: boolean;
|
|
63
|
+
/** Dev settings (only shown in __DEV__ mode) */
|
|
64
|
+
devSettings?: DevSettingsProps;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
@@ -72,6 +75,7 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
|
72
75
|
appVersion,
|
|
73
76
|
customSections = [],
|
|
74
77
|
showCloseButton = false,
|
|
78
|
+
devSettings,
|
|
75
79
|
}) => {
|
|
76
80
|
const tokens = useAppDesignTokens();
|
|
77
81
|
const insets = useSafeAreaInsets();
|
|
@@ -122,23 +126,71 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
|
122
126
|
)}
|
|
123
127
|
|
|
124
128
|
{features.appearance && (
|
|
125
|
-
<AppearanceSection
|
|
129
|
+
<AppearanceSection
|
|
130
|
+
config={{
|
|
131
|
+
...normalizedConfig.appearance.config,
|
|
132
|
+
title:
|
|
133
|
+
normalizedConfig.appearance.config?.title ||
|
|
134
|
+
t("settings.appearance.title"),
|
|
135
|
+
description:
|
|
136
|
+
normalizedConfig.appearance.config?.description ||
|
|
137
|
+
t("settings.appearance.description"),
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
126
140
|
)}
|
|
127
141
|
|
|
128
142
|
{features.language && (
|
|
129
|
-
<LanguageSection
|
|
143
|
+
<LanguageSection
|
|
144
|
+
config={{
|
|
145
|
+
...normalizedConfig.language.config,
|
|
146
|
+
title: t("settings.languageSelection.title"),
|
|
147
|
+
description:
|
|
148
|
+
normalizedConfig.language.config?.description ||
|
|
149
|
+
t("settings.languageSelection.description"),
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
130
152
|
)}
|
|
131
153
|
|
|
132
154
|
{features.notifications && (
|
|
133
|
-
<NotificationsSection
|
|
155
|
+
<NotificationsSection
|
|
156
|
+
config={{
|
|
157
|
+
...normalizedConfig.notifications.config,
|
|
158
|
+
title:
|
|
159
|
+
normalizedConfig.notifications.config?.title ||
|
|
160
|
+
t("settings.notifications.title"),
|
|
161
|
+
description:
|
|
162
|
+
normalizedConfig.notifications.config?.description ||
|
|
163
|
+
t("settings.notifications.description"),
|
|
164
|
+
}}
|
|
165
|
+
/>
|
|
134
166
|
)}
|
|
135
167
|
|
|
136
168
|
{features.about && (
|
|
137
|
-
<AboutSection
|
|
169
|
+
<AboutSection
|
|
170
|
+
config={{
|
|
171
|
+
...normalizedConfig.about.config,
|
|
172
|
+
title:
|
|
173
|
+
normalizedConfig.about.config?.title ||
|
|
174
|
+
t("settings.about.title"),
|
|
175
|
+
description:
|
|
176
|
+
normalizedConfig.about.config?.description ||
|
|
177
|
+
t("settings.about.description"),
|
|
178
|
+
}}
|
|
179
|
+
/>
|
|
138
180
|
)}
|
|
139
181
|
|
|
140
182
|
{features.legal && (
|
|
141
|
-
<LegalSection
|
|
183
|
+
<LegalSection
|
|
184
|
+
config={{
|
|
185
|
+
...normalizedConfig.legal.config,
|
|
186
|
+
title:
|
|
187
|
+
normalizedConfig.legal.config?.title ||
|
|
188
|
+
t("settings.legal.title"),
|
|
189
|
+
description:
|
|
190
|
+
normalizedConfig.legal.config?.description ||
|
|
191
|
+
t("settings.legal.description"),
|
|
192
|
+
}}
|
|
193
|
+
/>
|
|
142
194
|
)}
|
|
143
195
|
|
|
144
196
|
{features.disclaimer && DisclaimerSetting && (
|
|
@@ -168,6 +220,15 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
|
168
220
|
</View>
|
|
169
221
|
)}
|
|
170
222
|
|
|
223
|
+
{devSettings && (
|
|
224
|
+
<DevSettingsSection
|
|
225
|
+
onClearAllData={devSettings.onClearAllData}
|
|
226
|
+
sectionTitle={devSettings.sectionTitle}
|
|
227
|
+
clearDataTitle={devSettings.clearDataTitle}
|
|
228
|
+
clearDataDescription={devSettings.clearDataDescription}
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
231
|
+
|
|
171
232
|
{showFooter && <SettingsFooter versionText={footerText} appVersion={appVersion} />}
|
|
172
233
|
</ScrollView>
|
|
173
234
|
);
|