@umituz/react-native-settings 4.17.26 → 4.17.30

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 (41) hide show
  1. package/package.json +15 -6
  2. package/src/domains/about/presentation/components/AboutSection.tsx +14 -71
  3. package/src/domains/appearance/application/ports/IAppearanceRepository.ts +8 -0
  4. package/src/domains/appearance/hooks/index.ts +1 -1
  5. package/src/domains/appearance/hooks/useAppearance.ts +18 -58
  6. package/src/domains/appearance/hooks/useAppearanceActions.ts +20 -128
  7. package/src/domains/appearance/infrastructure/repositories/AppearanceRepository.ts +34 -0
  8. package/src/domains/appearance/infrastructure/services/AppearanceService.ts +51 -0
  9. package/src/domains/appearance/presentation/components/AppearanceSection.tsx +2 -2
  10. package/src/domains/appearance/presentation/hooks/mutations/useAppearanceMutations.ts +36 -0
  11. package/src/domains/appearance/presentation/hooks/queries/useAppearanceQuery.ts +15 -0
  12. package/src/domains/disclaimer/presentation/components/DisclaimerModal.tsx +37 -40
  13. package/src/domains/faqs/presentation/components/FAQSection.tsx +1 -1
  14. package/src/domains/feedback/presentation/components/FeedbackModal.tsx +11 -15
  15. package/src/domains/feedback/presentation/components/SupportSection.tsx +2 -2
  16. package/src/domains/legal/presentation/components/LegalItem.tsx +13 -129
  17. package/src/index.ts +15 -9
  18. package/src/infrastructure/repositories/SettingsRepository.ts +105 -0
  19. package/src/infrastructure/services/SettingsService.ts +47 -0
  20. package/src/presentation/components/SettingItem.tsx +77 -129
  21. package/src/presentation/components/SettingsFooter.tsx +9 -25
  22. package/src/presentation/components/SettingsSection.tsx +9 -20
  23. package/src/presentation/hooks/mutations/useSettingsMutations.ts +58 -0
  24. package/src/presentation/hooks/queries/useSettingsQuery.ts +27 -0
  25. package/src/presentation/hooks/useSettings.ts +45 -0
  26. package/src/presentation/screens/components/SettingsContent.tsx +20 -247
  27. package/src/presentation/screens/components/sections/CustomSettingsList.tsx +31 -0
  28. package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +55 -0
  29. package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +43 -0
  30. package/src/presentation/screens/components/sections/ProfileSectionLoader.tsx +47 -0
  31. package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +84 -0
  32. package/src/presentation/screens/hooks/useFeatureDetection.ts +1 -16
  33. package/src/presentation/screens/types/FeatureConfig.ts +18 -0
  34. package/src/presentation/screens/types/SettingsConfig.ts +7 -0
  35. package/src/domains/appearance/infrastructure/services/appearanceService.ts +0 -301
  36. package/src/domains/appearance/infrastructure/storage/appearanceStorage.ts +0 -120
  37. package/src/domains/appearance/infrastructure/stores/appearanceStore.ts +0 -132
  38. package/src/infrastructure/storage/SettingsStore.ts +0 -189
  39. package/src/infrastructure/storage/__tests__/SettingsStore.test.tsx +0 -302
  40. package/src/presentation/components/CloudSyncSetting.tsx +0 -58
  41. /package/src/{domain/repositories → application/ports}/ISettingsRepository.ts +0 -0
@@ -1,47 +1,31 @@
1
- /**
2
- * Setting Item Component
3
- * Single Responsibility: Render a single settings item
4
- * Material Design 3 style with hover effects and modern spacing
5
- */
6
-
7
1
  import React from "react";
8
- import { View, Text, Pressable, StyleSheet, Switch } from "react-native";
9
- import { AtomicIcon, useResponsiveDesignTokens } from "@umituz/react-native-design-system";
2
+ import { View, StyleSheet, Switch } from "react-native";
3
+ import {
4
+ AtomicIcon,
5
+ AtomicText,
6
+ useResponsiveDesignTokens,
7
+ ListItem
8
+ } from "@umituz/react-native-design-system";
10
9
 
11
10
  export interface SettingItemProps {
12
- /** Icon name (Ionicons) */
13
11
  icon: string;
14
- /** Main title text */
15
12
  title: string;
16
- /** Optional description/value text */
17
13
  value?: string;
18
- /** Callback when pressed */
19
14
  onPress?: () => void;
20
- /** Show switch instead of chevron */
21
15
  showSwitch?: boolean;
22
- /** Switch value */
23
16
  switchValue?: boolean;
24
- /** Switch change handler */
25
17
  onSwitchChange?: (value: boolean) => void;
26
- /** Is last item in section (no divider) */
27
18
  isLast?: boolean;
28
- /** Custom icon color */
29
19
  iconColor?: string;
30
- /** Custom title color */
31
20
  titleColor?: string;
32
- /** Test ID for E2E testing */
33
21
  testID?: string;
34
- /** Disable the item */
35
22
  disabled?: boolean;
36
- /** Custom switch thumb color */
37
- switchThumbColor?: string;
38
- /** Custom switch track colors */
39
- switchTrackColors?: {
40
- false: string;
41
- true: string;
42
- };
43
23
  }
44
24
 
25
+ /**
26
+ * SettingItem - Enhanced ListItem for Settings
27
+ * Uses design system's ListItem molecule with custom switch support
28
+ */
45
29
  export const SettingItem: React.FC<SettingItemProps> = ({
46
30
  icon,
47
31
  title,
@@ -55,148 +39,112 @@ export const SettingItem: React.FC<SettingItemProps> = ({
55
39
  titleColor,
56
40
  testID,
57
41
  disabled = false,
58
- switchThumbColor,
59
- switchTrackColors,
60
42
  }) => {
61
43
  const tokens = useResponsiveDesignTokens();
62
- const colors = tokens.colors;
63
44
 
64
- return (
65
- <>
66
- <Pressable
67
- style={({ pressed }) => [
68
- styles.container,
45
+ // For switch items, we need custom rendering
46
+ if (showSwitch) {
47
+ return (
48
+ <View
49
+ style={[
50
+ styles.switchContainer,
69
51
  {
70
- backgroundColor: pressed && !disabled && !showSwitch
71
- ? `${colors.primary}08`
72
- : 'transparent',
73
- },
52
+ borderBottomWidth: isLast ? 0 : 1,
53
+ borderBottomColor: `${tokens.colors.onSurface}10`,
54
+ }
74
55
  ]}
75
- onPress={onPress}
76
- disabled={showSwitch || disabled}
77
- testID={testID}
78
56
  >
79
- <View style={styles.content}>
80
- <View
81
- style={[
82
- styles.iconContainer,
83
- {
84
- backgroundColor: iconColor
85
- ? `${iconColor}15`
86
- : `${colors.primary}15`,
87
- },
88
- ]}
89
- >
57
+ <View style={styles.switchContent}>
58
+ <View style={[
59
+ styles.iconWrapper,
60
+ { backgroundColor: iconColor ? `${iconColor}15` : `${tokens.colors.primary}15` }
61
+ ]}>
90
62
  <AtomicIcon
91
63
  name={icon}
92
- customSize={24}
93
- customColor={iconColor || colors.primary}
64
+ size="md"
65
+ customColor={iconColor || tokens.colors.primary}
94
66
  />
95
67
  </View>
96
68
  <View style={styles.textContainer}>
97
- <Text
69
+ <AtomicText
70
+ type="bodyLarge"
71
+ color={disabled ? "surfaceVariant" : "onSurface"}
98
72
  style={[
99
- styles.title,
100
- {
101
- color: disabled
102
- ? colors.textSecondary
103
- : titleColor || colors.textPrimary,
104
- opacity: disabled ? 0.5 : 1,
105
- },
73
+ titleColor ? { color: titleColor } : {},
74
+ { opacity: disabled ? 0.6 : 1 }
106
75
  ]}
107
76
  numberOfLines={1}
108
77
  >
109
78
  {title}
110
- </Text>
111
- {value && !showSwitch && (
112
- <Text
113
- style={[styles.value, { color: colors.textSecondary }]}
79
+ </AtomicText>
80
+ {value && (
81
+ <AtomicText
82
+ type="bodySmall"
83
+ color="secondary"
114
84
  numberOfLines={2}
85
+ style={{ marginTop: 2 }}
115
86
  >
116
87
  {value}
117
- </Text>
88
+ </AtomicText>
118
89
  )}
119
90
  </View>
120
91
  </View>
121
-
122
- <View style={styles.rightContainer}>
123
- {showSwitch ? (
124
- <Switch
125
- value={switchValue}
126
- onValueChange={onSwitchChange}
127
- trackColor={switchTrackColors || {
128
- false: `${colors.textSecondary}30`,
129
- true: colors.primary,
130
- }}
131
- thumbColor={switchThumbColor || "#FFFFFF"}
132
- ios_backgroundColor={`${colors.textSecondary}30`}
133
- />
134
- ) : (
135
- <AtomicIcon
136
- name="chevron-forward-outline"
137
- customSize={20}
138
- customColor={colors.textSecondary}
139
- />
140
- )}
141
- </View>
142
- </Pressable>
143
-
144
- {!isLast && (
145
- <View
146
- style={[
147
- styles.divider,
148
- { backgroundColor: `${colors.textSecondary}20` },
149
- ]}
92
+ <Switch
93
+ value={switchValue}
94
+ onValueChange={onSwitchChange}
95
+ trackColor={{
96
+ false: tokens.colors.surfaceVariant,
97
+ true: tokens.colors.primary
98
+ }}
99
+ thumbColor="#FFFFFF"
100
+ ios_backgroundColor={tokens.colors.surfaceVariant}
101
+ disabled={disabled}
150
102
  />
151
- )}
152
- </>
103
+ </View>
104
+ );
105
+ }
106
+
107
+ // Use design system's ListItem for regular items
108
+ return (
109
+ <ListItem
110
+ title={title}
111
+ subtitle={value}
112
+ leftIcon={icon}
113
+ rightIcon="chevron-forward"
114
+ onPress={onPress}
115
+ disabled={disabled}
116
+ style={{
117
+ borderBottomWidth: isLast ? 0 : 1,
118
+ borderBottomColor: `${tokens.colors.onSurface}10`,
119
+ }}
120
+ />
153
121
  );
154
122
  };
155
123
 
156
124
  const styles = StyleSheet.create({
157
- container: {
125
+ switchContainer: {
158
126
  flexDirection: "row",
159
127
  alignItems: "center",
160
128
  justifyContent: "space-between",
161
129
  paddingHorizontal: 16,
162
- paddingVertical: 16,
163
- minHeight: 72,
130
+ paddingVertical: 12,
131
+ minHeight: 64,
164
132
  },
165
- content: {
133
+ switchContent: {
166
134
  flexDirection: "row",
167
135
  alignItems: "center",
168
136
  flex: 1,
169
137
  },
170
- iconContainer: {
171
- width: 48,
172
- height: 48,
173
- borderRadius: 12,
138
+ iconWrapper: {
139
+ width: 40,
140
+ height: 40,
141
+ borderRadius: 10,
174
142
  justifyContent: "center",
175
143
  alignItems: "center",
176
- marginRight: 16,
144
+ marginRight: 12,
177
145
  },
178
146
  textContainer: {
179
147
  flex: 1,
180
- minWidth: 0,
181
- },
182
- title: {
183
- fontSize: 16,
184
- fontWeight: "500",
185
- lineHeight: 20,
186
- },
187
- value: {
188
- fontSize: 14,
189
- fontWeight: "400",
190
- marginTop: 4,
191
- lineHeight: 18,
192
- },
193
- rightContainer: {
194
- flexDirection: "row",
195
- alignItems: "center",
196
- gap: 8,
197
- },
198
- divider: {
199
- height: 1,
200
- marginLeft: 80,
148
+ paddingRight: 8,
201
149
  },
202
150
  });
@@ -1,45 +1,32 @@
1
- /**
2
- * Settings Footer Component
3
- * Single Responsibility: Display app version information
4
- */
5
-
6
1
  import React from "react";
7
- import { View, Text, StyleSheet } from "react-native";
8
- import { useResponsiveDesignTokens } from "@umituz/react-native-design-system";
2
+ import { View, StyleSheet } from "react-native";
3
+ import { useResponsiveDesignTokens, AtomicText } from "@umituz/react-native-design-system";
9
4
 
10
5
  export interface SettingsFooterProps {
11
- /** Custom version text (optional) - should include version number from app config */
12
6
  versionText?: string;
13
- /** App version number from app config (e.g., "1.0.0") */
14
7
  appVersion?: string;
15
- /** Label for version (e.g., "Version") */
16
8
  versionLabel?: string;
17
9
  }
18
10
 
19
11
  export const SettingsFooter: React.FC<SettingsFooterProps> = ({
20
12
  versionText,
21
13
  appVersion,
22
- versionLabel = "Version",
14
+ versionLabel,
23
15
  }) => {
24
16
  const tokens = useResponsiveDesignTokens();
25
17
  const colors = tokens.colors;
26
18
 
27
- // If versionText is provided, use it directly
28
- // Otherwise build from label + appVersion
29
- const displayText = versionText || (appVersion
19
+ const displayText = versionText || (appVersion && versionLabel
30
20
  ? `${versionLabel} ${appVersion}`
31
- : undefined);
21
+ : appVersion || versionText);
32
22
 
33
- // Don't render if no version info available
34
- if (!displayText) {
35
- return null;
36
- }
23
+ if (!displayText) return null;
37
24
 
38
25
  return (
39
26
  <View style={styles.container}>
40
- <Text style={[styles.text, { color: colors.textSecondary }]}>
27
+ <AtomicText type="labelSmall" style={{ color: colors.textSecondary }}>
41
28
  {displayText}
42
- </Text>
29
+ </AtomicText>
43
30
  </View>
44
31
  );
45
32
  };
@@ -49,9 +36,6 @@ const styles = StyleSheet.create({
49
36
  paddingVertical: 24,
50
37
  alignItems: "center",
51
38
  },
52
- text: {
53
- fontSize: 12,
54
- fontWeight: "500",
55
- },
56
39
  });
57
40
 
41
+
@@ -1,16 +1,9 @@
1
- /**
2
- * Settings Section Component
3
- * Single Responsibility: Render a settings section with title and container
4
- */
5
-
6
1
  import React from "react";
7
- import { View, Text, StyleSheet } from "react-native";
8
- import { useResponsiveDesignTokens } from "@umituz/react-native-design-system";
2
+ import { View, StyleSheet } from "react-native";
3
+ import { useResponsiveDesignTokens, AtomicText } from "@umituz/react-native-design-system";
9
4
 
10
5
  export interface SettingsSectionProps {
11
- /** Section title */
12
6
  title: string;
13
- /** Section content */
14
7
  children: React.ReactNode;
15
8
  }
16
9
 
@@ -23,12 +16,12 @@ export const SettingsSection: React.FC<SettingsSectionProps> = ({
23
16
 
24
17
  return (
25
18
  <View style={[styles.container, { backgroundColor: colors.surface }]}>
26
- <Text style={[styles.title, { color: colors.textPrimary }]}>
27
- {title}
28
- </Text>
29
- <View style={styles.content}>
30
- {children}
19
+ <View style={styles.titleContainer}>
20
+ <AtomicText type="titleLarge" color="primary">
21
+ {title}
22
+ </AtomicText>
31
23
  </View>
24
+ {children}
32
25
  </View>
33
26
  );
34
27
  };
@@ -39,15 +32,11 @@ const styles = StyleSheet.create({
39
32
  borderRadius: 12,
40
33
  overflow: "hidden",
41
34
  },
42
- title: {
43
- fontSize: 18,
44
- fontWeight: "600",
35
+ titleContainer: {
45
36
  paddingHorizontal: 16,
46
37
  paddingTop: 16,
47
38
  paddingBottom: 8,
48
39
  },
49
- content: {
50
- borderRadius: 0,
51
- },
52
40
  });
53
41
 
42
+
@@ -0,0 +1,58 @@
1
+ /**
2
+ * useSettingsMutations Hook
3
+ *
4
+ * Mutations for updating and resetting user settings
5
+ */
6
+
7
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
8
+ import { getSettingsService } from '../../../infrastructure/services/SettingsService';
9
+ import { SETTINGS_QUERY_KEY } from '../queries/useSettingsQuery';
10
+ import type { UserSettings } from '../../../application/ports/ISettingsRepository';
11
+
12
+ export const useUpdateSettingsMutation = (userId: string) => {
13
+ const queryClient = useQueryClient();
14
+ const settingsService = getSettingsService();
15
+
16
+ return useMutation({
17
+ mutationFn: async (updates: Partial<UserSettings>) => {
18
+ const currentResult = await settingsService.getSettings(userId);
19
+ if (!currentResult.success || !currentResult.data) {
20
+ throw new Error('Could not find existing settings to update');
21
+ }
22
+
23
+ const updatedSettings = {
24
+ ...currentResult.data,
25
+ ...updates,
26
+ updatedAt: new Date(),
27
+ };
28
+
29
+ const result = await settingsService.saveSettings(updatedSettings);
30
+ if (!result.success) {
31
+ throw new Error(result.error?.message || 'Failed to update settings');
32
+ }
33
+ return updatedSettings;
34
+ },
35
+ onSuccess: (data) => {
36
+ queryClient.setQueryData([...SETTINGS_QUERY_KEY, userId], data);
37
+ },
38
+ });
39
+ };
40
+
41
+ export const useResetSettingsMutation = (userId: string) => {
42
+ const queryClient = useQueryClient();
43
+ const settingsService = getSettingsService();
44
+
45
+ return useMutation({
46
+ mutationFn: async () => {
47
+ const result = await settingsService.resetSettings(userId);
48
+ // resetSettings returns the defaults as data
49
+ if (!result.success) {
50
+ throw new Error(result.error?.message || 'Failed to reset settings');
51
+ }
52
+ return (result as any).data as UserSettings;
53
+ },
54
+ onSuccess: (data) => {
55
+ queryClient.setQueryData([...SETTINGS_QUERY_KEY, userId], data);
56
+ },
57
+ });
58
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * useSettingsQuery Hook
3
+ *
4
+ * Fetches user settings using TanStack Query
5
+ */
6
+
7
+ import { useQuery } from '@tanstack/react-query';
8
+ import { getSettingsService } from '../../../infrastructure/services/SettingsService';
9
+ import type { UserSettings } from '../../../application/ports/ISettingsRepository';
10
+
11
+ export const SETTINGS_QUERY_KEY = ['settings'];
12
+
13
+ export const useSettingsQuery = (userId: string) => {
14
+ const settingsService = getSettingsService();
15
+
16
+ return useQuery({
17
+ queryKey: [...SETTINGS_QUERY_KEY, userId],
18
+ queryFn: async () => {
19
+ const result = await settingsService.getSettings(userId);
20
+ if (!result.success || !result.data) {
21
+ throw new Error(result.error?.message || 'Failed to load settings');
22
+ }
23
+ return result.data;
24
+ },
25
+ enabled: !!userId,
26
+ });
27
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * useSettings Hook
3
+ *
4
+ * Primary hook for accessing and managing user settings.
5
+ * Integrates with TanStack Query mutations and queries.
6
+ */
7
+
8
+ import { useSettingsQuery } from './queries/useSettingsQuery';
9
+ import { useUpdateSettingsMutation, useResetSettingsMutation } from './mutations/useSettingsMutations';
10
+ import type { UserSettings } from '../../application/ports/ISettingsRepository';
11
+
12
+ export const useSettings = (userId: string) => {
13
+ const {
14
+ data: settings,
15
+ isLoading: loading,
16
+ error: queryError
17
+ } = useSettingsQuery(userId);
18
+
19
+ const updateMutation = useUpdateSettingsMutation(userId);
20
+ const resetMutation = useResetSettingsMutation(userId);
21
+
22
+ const updateSettings = async (updates: Partial<UserSettings>) => {
23
+ return updateMutation.mutateAsync(updates);
24
+ };
25
+
26
+ const resetSettings = async () => {
27
+ return resetMutation.mutateAsync();
28
+ };
29
+
30
+ const error = queryError instanceof Error
31
+ ? queryError.message
32
+ : updateMutation.error instanceof Error
33
+ ? updateMutation.error.message
34
+ : resetMutation.error instanceof Error
35
+ ? resetMutation.error.message
36
+ : null;
37
+
38
+ return {
39
+ settings,
40
+ loading: loading || updateMutation.isPending || resetMutation.isPending,
41
+ error,
42
+ updateSettings,
43
+ resetSettings,
44
+ };
45
+ };