@umituz/react-native-settings 4.20.53 → 4.20.55

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 (27) hide show
  1. package/package.json +1 -1
  2. package/src/domains/about/presentation/components/AboutSettingItem.tsx +39 -86
  3. package/src/domains/about/presentation/hooks/useAboutInfo.ts +80 -200
  4. package/src/domains/about/presentation/hooks/useAboutInfo.types.ts +32 -0
  5. package/src/domains/about/presentation/hooks/useAboutInfo.utils.ts +167 -0
  6. package/src/domains/about/utils/AppInfoFactory.ts +19 -0
  7. package/src/domains/about/utils/index.ts +2 -0
  8. package/src/domains/legal/index.ts +1 -0
  9. package/src/domains/legal/presentation/screens/LegalContentScreen.tsx +140 -0
  10. package/src/domains/legal/presentation/screens/PrivacyPolicyScreen.tsx +17 -155
  11. package/src/domains/legal/presentation/screens/TermsOfServiceScreen.tsx +17 -155
  12. package/src/presentation/components/SettingsItemCard.tsx +2 -2
  13. package/src/presentation/navigation/SettingsStackNavigator.tsx +50 -129
  14. package/src/presentation/navigation/components/wrappers/AboutScreenWrapper.tsx +13 -0
  15. package/src/presentation/navigation/components/wrappers/LegalScreenWrapper.tsx +50 -0
  16. package/src/presentation/navigation/components/wrappers/SettingsScreenWrapper.tsx +32 -0
  17. package/src/presentation/navigation/components/wrappers/index.ts +9 -0
  18. package/src/presentation/navigation/utils/index.ts +5 -0
  19. package/src/presentation/navigation/utils/navigationScreenOptions.ts +56 -0
  20. package/src/presentation/navigation/utils/navigationTranslations.ts +46 -0
  21. package/src/presentation/screens/components/SettingsHeader.tsx +1 -1
  22. package/src/presentation/screens/types/BaseTypes.ts +12 -0
  23. package/src/presentation/screens/types/ContentConfig.ts +82 -0
  24. package/src/presentation/screens/types/SettingsConfig.ts +6 -4
  25. package/src/presentation/screens/types/UserFeatureConfig.ts +137 -0
  26. package/src/presentation/screens/types/index.ts +10 -8
  27. package/src/presentation/screens/types/FeatureConfig.ts +0 -263
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "4.20.53",
3
+ "version": "4.20.55",
4
4
  "description": "Complete settings hub for React Native apps - consolidated package with settings, about, legal, appearance, feedback, FAQs, and rating",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -4,7 +4,7 @@
4
4
  * Fully configurable and generic
5
5
  * Optimized for performance and memory safety
6
6
  */
7
- import React, { useMemo, useCallback } from 'react';
7
+ import React, { memo, useCallback } from 'react';
8
8
  import {
9
9
  TouchableOpacity,
10
10
  View,
@@ -13,6 +13,7 @@ import {
13
13
  ViewStyle,
14
14
  TextStyle,
15
15
  } from 'react-native';
16
+ import { useAppDesignTokens } from '@umituz/react-native-design-system';
16
17
 
17
18
  export interface AboutSettingItemProps {
18
19
  /** Icon component (any React component) */
@@ -45,9 +46,7 @@ export interface AboutSettingItemProps {
45
46
  chevronColor?: string;
46
47
  }
47
48
 
48
- import { useAppDesignTokens } from '@umituz/react-native-design-system';
49
-
50
- export const AboutSettingItem: React.FC<AboutSettingItemProps> = ({
49
+ export const AboutSettingItem: React.FC<AboutSettingItemProps> = memo(({
51
50
  icon,
52
51
  title,
53
52
  description,
@@ -67,107 +66,61 @@ export const AboutSettingItem: React.FC<AboutSettingItemProps> = ({
67
66
  const styles = getStyles(tokens);
68
67
  const colors = tokens.colors;
69
68
 
70
- // Memoize container type to prevent unnecessary re-renders
71
- const Container = useMemo(() => {
72
- return onPress ? TouchableOpacity : View;
73
- }, [onPress]) as React.ComponentType<React.ComponentProps<typeof TouchableOpacity | typeof View>>;
74
-
75
- // Memoize container styles
76
- const containerStyles = useMemo(() => {
77
- return [
78
- styles.container,
79
- { backgroundColor: colors.surface },
80
- disabled && styles.disabled,
81
- containerStyle
82
- ];
83
- }, [disabled, containerStyle, colors.surface, styles]);
69
+ const Container = onPress ? TouchableOpacity : View;
84
70
 
85
- // Memoize icon container styles
86
- const iconContainerStyles = useMemo(() => {
87
- return [
88
- styles.iconContainer,
89
- iconContainerStyle
90
- ];
91
- }, [iconContainerStyle, styles]);
92
-
93
- // Memoize chevron styles
94
- const chevronStyles = useMemo(() => {
95
- return [
96
- styles.chevron,
97
- { color: chevronColor || colors.textSecondary }
98
- ];
99
- }, [chevronColor, colors.textSecondary, styles]);
100
-
101
- // Memoize press handler to prevent unnecessary re-renders
102
71
  const handlePress = useCallback(() => {
103
72
  if (onPress && !disabled) {
104
73
  onPress();
105
74
  }
106
75
  }, [onPress, disabled]);
107
76
 
108
- // Memoize icon rendering
109
- const renderIcon = useMemo(() => {
110
- if (!icon) {
111
- return null;
112
- }
77
+ const containerStyles = [
78
+ styles.container,
79
+ { backgroundColor: colors.surface },
80
+ disabled && styles.disabled,
81
+ containerStyle,
82
+ ];
113
83
 
114
- return (
115
- <View style={iconContainerStyles}>
116
- {icon}
117
- </View>
118
- );
119
- }, [icon, iconContainerStyles]);
84
+ const iconContainerStyles = [
85
+ styles.iconContainer,
86
+ iconContainerStyle,
87
+ ];
88
+
89
+ const chevronStyles = [
90
+ styles.chevron,
91
+ { color: chevronColor || colors.textSecondary },
92
+ ];
93
+
94
+ return (
95
+ <Container
96
+ style={containerStyles}
97
+ onPress={handlePress}
98
+ disabled={disabled}
99
+ testID={testID}
100
+ >
101
+ {icon && <View style={iconContainerStyles}>{icon}</View>}
120
102
 
121
- // Memoize content rendering
122
- const renderContent = useMemo(() => {
123
- return (
124
103
  <View style={styles.content}>
125
- <Text style={[styles.title, { color: colors.textPrimary }, titleStyle]}>{title}</Text>
104
+ <Text style={[styles.title, { color: colors.textPrimary }, titleStyle]}>
105
+ {title}
106
+ </Text>
126
107
  {description && (
127
108
  <Text style={[styles.description, { color: colors.textSecondary }, descriptionStyle]}>
128
109
  {description}
129
110
  </Text>
130
111
  )}
131
112
  </View>
132
- );
133
- }, [title, description, titleStyle, descriptionStyle, colors.textPrimary, colors.textSecondary, styles]);
134
-
135
- // Memoize value rendering
136
- const renderValue = useMemo(() => {
137
- if (!value) {
138
- return null;
139
- }
140
-
141
- return (
142
- <Text style={[styles.value, { color: colors.textSecondary }, valueStyle]}>{value}</Text>
143
- );
144
- }, [value, valueStyle, colors.textSecondary, styles]);
145
-
146
- // Memoize chevron rendering
147
- const renderChevron = useMemo(() => {
148
- if (!showChevron) {
149
- return null;
150
- }
151
113
 
152
- return (
153
- <Text style={chevronStyles}>›</Text>
154
- );
155
- }, [showChevron, chevronStyles]);
114
+ {value && (
115
+ <Text style={[styles.value, { color: colors.textSecondary }, valueStyle]}>
116
+ {value}
117
+ </Text>
118
+ )}
156
119
 
157
- return (
158
- <Container
159
- style={containerStyles}
160
- onPress={handlePress}
161
- disabled={disabled}
162
- testID={testID}
163
- >
164
- {renderIcon}
165
- {renderContent}
166
- {renderValue}
167
- {renderChevron}
120
+ {showChevron && <Text style={chevronStyles}>›</Text>}
168
121
  </Container>
169
122
  );
170
- };
123
+ });
171
124
 
172
125
  const getStyles = (tokens: any) => StyleSheet.create({
173
126
  container: {
@@ -206,4 +159,4 @@ const getStyles = (tokens: any) => StyleSheet.create({
206
159
  fontSize: 20,
207
160
  fontWeight: '300',
208
161
  },
209
- });
162
+ });
@@ -3,35 +3,18 @@
3
3
  * Provides reactive state management for About data
4
4
  * Optimized for performance and memory safety
5
5
  */
6
- import { useState, useEffect, useCallback, useRef } from 'react';
7
- import { AppInfo, AboutConfig } from '../../domain/entities/AppInfo';
6
+ import { useState, useCallback, useRef, useEffect } from 'react';
7
+ import type { AppInfo, AboutConfig } from '../../domain/entities/AppInfo';
8
8
  import { AboutRepository } from '../../infrastructure/repositories/AboutRepository';
9
-
10
- export interface UseAboutInfoOptions {
11
- /** Initial configuration */
12
- initialConfig?: AboutConfig;
13
- /** Auto-initialize on mount */
14
- autoInit?: boolean;
15
- }
16
-
17
- export interface UseAboutInfoReturn {
18
- /** Current app info */
19
- appInfo: AppInfo | null;
20
- /** Loading state */
21
- loading: boolean;
22
- /** Error state */
23
- error: string | null;
24
- /** Initialize with config */
25
- initialize: (config: AboutConfig) => Promise<void>;
26
- /** Update with new config */
27
- update: (config: AboutConfig) => Promise<void>;
28
- /** Update app info */
29
- updateAppInfo: (updates: Partial<AppInfo>) => Promise<void>;
30
- /** Refresh current app info */
31
- refresh: () => Promise<void>;
32
- /** Reset to initial state */
33
- reset: () => void;
34
- }
9
+ import type { UseAboutInfoOptions, UseAboutInfoReturn } from './useAboutInfo.types';
10
+ import {
11
+ setErrorIfMounted,
12
+ setLoadingIfMounted,
13
+ initializeAppInfo,
14
+ updateAppInfoConfig,
15
+ updateAppInfoPartial,
16
+ refreshAppInfo,
17
+ } from './useAboutInfo.utils';
35
18
 
36
19
  export const useAboutInfo = (
37
20
  options: UseAboutInfoOptions = {}
@@ -42,161 +25,62 @@ export const useAboutInfo = (
42
25
  const [loading, setLoading] = useState(false);
43
26
  const [error, setError] = useState<string | null>(null);
44
27
 
45
- // Prevent infinite loops and memory leaks
46
28
  const isInitializedRef = useRef(false);
47
29
  const isMountedRef = useRef(true);
48
30
 
49
- const initialize = useCallback(async (config: AboutConfig, force = false) => {
50
- // Prevent multiple initializations unless forced
51
- if (isInitializedRef.current && !force) {
52
- return;
53
- }
54
-
55
- // Check if component is still mounted
56
- if (!isMountedRef.current) {
57
- return;
58
- }
59
-
60
- setLoading(true);
61
- setError(null);
62
-
63
- try {
64
- const defaultAppInfo: AppInfo = {
65
- name: config.appInfo?.name || '',
66
- version: config.appInfo?.version || '1.0.0',
67
- description: config.appInfo?.description,
68
- developer: config.appInfo?.developer,
69
- contactEmail: config.appInfo?.contactEmail,
70
- websiteUrl: config.appInfo?.websiteUrl,
71
- websiteDisplay: config.appInfo?.websiteDisplay,
72
- moreAppsUrl: config.appInfo?.moreAppsUrl,
73
- };
74
-
75
- await repository.saveAppInfo(defaultAppInfo);
76
-
77
- // Only update state if component is still mounted
78
- if (isMountedRef.current) {
79
- setAppInfo(defaultAppInfo);
80
- isInitializedRef.current = true;
81
- }
82
- } catch (err) {
83
- if (!isMountedRef.current) {
84
- return;
85
- }
86
-
87
- const errorMessage = err instanceof Error ? err.message : 'Unknown error';
88
- setError(errorMessage);
89
- } finally {
90
- // Only update loading state if component is still mounted
91
- if (isMountedRef.current) {
92
- setLoading(false);
93
- }
94
- }
95
- }, [repository]);
96
-
97
- const update = useCallback(async (config: AboutConfig) => {
98
- if (!isMountedRef.current) {
99
- return;
100
- }
101
-
102
- setLoading(true);
103
- setError(null);
104
-
105
- try {
106
- const updatedAppInfo: AppInfo = {
107
- name: config.appInfo?.name || '',
108
- version: config.appInfo?.version || '1.0.0',
109
- description: config.appInfo?.description,
110
- developer: config.appInfo?.developer,
111
- contactEmail: config.appInfo?.contactEmail,
112
- websiteUrl: config.appInfo?.websiteUrl,
113
- websiteDisplay: config.appInfo?.websiteDisplay,
114
- moreAppsUrl: config.appInfo?.moreAppsUrl,
115
- };
116
-
117
- await repository.saveAppInfo(updatedAppInfo);
118
-
119
- // Only update state if component is still mounted
120
- if (isMountedRef.current) {
121
- setAppInfo(updatedAppInfo);
122
- }
123
- } catch (err) {
124
- if (!isMountedRef.current) {
125
- return;
126
- }
127
-
128
- const errorMessage = err instanceof Error ? err.message : 'Unknown error';
129
- setError(errorMessage);
130
- } finally {
131
- // Only update loading state if component is still mounted
132
- if (isMountedRef.current) {
133
- setLoading(false);
31
+ const initialize = useCallback(
32
+ (config: AboutConfig) => initializeAppInfo(
33
+ config,
34
+ repository,
35
+ isMountedRef,
36
+ isInitializedRef,
37
+ setAppInfo,
38
+ setError,
39
+ setLoading
40
+ ),
41
+ [repository]
42
+ );
43
+
44
+ const update = useCallback(
45
+ (config: AboutConfig) => updateAppInfoConfig(
46
+ config,
47
+ repository,
48
+ isMountedRef,
49
+ setAppInfo,
50
+ setError,
51
+ setLoading
52
+ ),
53
+ [repository]
54
+ );
55
+
56
+ const updateAppInfoCallback = useCallback(
57
+ (updates: Partial<AppInfo>) => {
58
+ if (!appInfo) {
59
+ setErrorIfMounted(isMountedRef, setError, 'App info not initialized');
60
+ return Promise.resolve();
134
61
  }
135
- }
136
- }, [repository]);
137
-
138
- const updateAppInfo = useCallback(async (updates: Partial<AppInfo>) => {
139
- if (!appInfo || !isMountedRef.current) {
140
- if (isMountedRef.current) {
141
- setError('App info not initialized');
142
- }
143
- return;
144
- }
145
-
146
- setLoading(true);
147
- setError(null);
148
-
149
- try {
150
- const updatedInfo = await repository.updateAppInfo(updates);
151
-
152
- // Only update state if component is still mounted
153
- if (isMountedRef.current) {
154
- setAppInfo(updatedInfo);
155
- }
156
- } catch (err) {
157
- if (!isMountedRef.current) {
158
- return;
159
- }
160
-
161
- const errorMessage = err instanceof Error ? err.message : 'Unknown error';
162
- setError(errorMessage);
163
- } finally {
164
- // Only update loading state if component is still mounted
165
- if (isMountedRef.current) {
166
- setLoading(false);
167
- }
168
- }
169
- }, [repository, appInfo]);
170
-
171
- const refresh = useCallback(async () => {
172
- if (!isMountedRef.current || !appInfo) {
173
- return;
174
- }
175
-
176
- setLoading(true);
177
- setError(null);
178
-
179
- try {
180
- const refreshedInfo = await repository.getAppInfo();
181
-
182
- // Only update state if component is still mounted
183
- if (isMountedRef.current) {
184
- setAppInfo(refreshedInfo);
185
- }
186
- } catch (err) {
187
- if (!isMountedRef.current) {
188
- return;
189
- }
190
-
191
- const errorMessage = err instanceof Error ? err.message : 'Unknown error';
192
- setError(errorMessage);
193
- } finally {
194
- // Only update loading state if component is still mounted
195
- if (isMountedRef.current) {
196
- setLoading(false);
197
- }
198
- }
199
- }, [repository, appInfo]);
62
+ return updateAppInfoPartial(
63
+ updates,
64
+ repository,
65
+ isMountedRef,
66
+ setAppInfo,
67
+ setError,
68
+ setLoading
69
+ );
70
+ },
71
+ [repository, appInfo]
72
+ );
73
+
74
+ const refresh = useCallback(
75
+ () => refreshAppInfo(
76
+ repository,
77
+ isMountedRef,
78
+ setAppInfo,
79
+ setError,
80
+ setLoading
81
+ ),
82
+ [repository]
83
+ );
200
84
 
201
85
  const reset = useCallback(() => {
202
86
  if (!isMountedRef.current) {
@@ -209,43 +93,36 @@ export const useAboutInfo = (
209
93
  isInitializedRef.current = false;
210
94
  }, []);
211
95
 
212
- // Cleanup on unmount to prevent memory leaks
96
+ // Cleanup on unmount
213
97
  useEffect(() => {
214
98
  return () => {
215
99
  isMountedRef.current = false;
216
100
 
217
- // Cleanup repository if it has destroy method
218
101
  if (repository && typeof repository.destroy === 'function') {
219
102
  repository.destroy();
220
103
  }
221
104
  };
222
105
  }, [repository]);
223
106
 
224
-
225
-
226
- // Set initial config when provided (if autoInit is not explicitly false)
107
+ // Initialize with default config if provided
227
108
  useEffect(() => {
228
- if (initialConfig && autoInit !== false && isMountedRef.current && !isInitializedRef.current) {
229
- const defaultAppInfo: AppInfo = {
230
- name: initialConfig.appInfo?.name || '',
231
- version: initialConfig.appInfo?.version || '1.0.0',
232
- description: initialConfig.appInfo?.description,
233
- developer: initialConfig.appInfo?.developer,
234
- contactEmail: initialConfig.appInfo?.contactEmail,
235
- websiteUrl: initialConfig.appInfo?.websiteUrl,
236
- websiteDisplay: initialConfig.appInfo?.websiteDisplay,
237
- moreAppsUrl: initialConfig.appInfo?.moreAppsUrl,
238
- };
239
-
109
+ if (
110
+ initialConfig &&
111
+ autoInit !== false &&
112
+ isMountedRef.current &&
113
+ !isInitializedRef.current
114
+ ) {
115
+ const { createDefaultAppInfo } = require('../../utils/AppInfoFactory');
116
+ const defaultAppInfo = createDefaultAppInfo(initialConfig);
240
117
  setAppInfo(defaultAppInfo);
241
118
  isInitializedRef.current = true;
242
119
  }
243
120
  }, [initialConfig, autoInit]);
244
121
 
245
- // Auto-initialize with dependency optimization
122
+ // Auto-initialize if autoInit is true
246
123
  useEffect(() => {
247
124
  if (autoInit === true && initialConfig && isMountedRef.current) {
248
- initialize(initialConfig, true);
125
+ initialize(initialConfig);
249
126
  }
250
127
  }, [autoInit, initialConfig, initialize]);
251
128
 
@@ -255,8 +132,11 @@ export const useAboutInfo = (
255
132
  error,
256
133
  initialize,
257
134
  update,
258
- updateAppInfo,
135
+ updateAppInfo: updateAppInfoCallback,
259
136
  refresh,
260
137
  reset,
261
138
  };
262
- };
139
+ };
140
+
141
+ // Re-export types for convenience
142
+ export type { UseAboutInfoOptions, UseAboutInfoReturn } from './useAboutInfo.types';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * useAboutInfo Hook Types
3
+ * Type definitions for about info hook
4
+ */
5
+
6
+ import type { AppInfo, AboutConfig } from '../../domain/entities/AppInfo';
7
+
8
+ export interface UseAboutInfoOptions {
9
+ /** Initial configuration */
10
+ initialConfig?: AboutConfig;
11
+ /** Auto-initialize on mount */
12
+ autoInit?: boolean;
13
+ }
14
+
15
+ export interface UseAboutInfoReturn {
16
+ /** Current app info */
17
+ appInfo: AppInfo | null;
18
+ /** Loading state */
19
+ loading: boolean;
20
+ /** Error state */
21
+ error: string | null;
22
+ /** Initialize with config */
23
+ initialize: (config: AboutConfig) => Promise<void>;
24
+ /** Update with new config */
25
+ update: (config: AboutConfig) => Promise<void>;
26
+ /** Update app info */
27
+ updateAppInfo: (updates: Partial<AppInfo>) => Promise<void>;
28
+ /** Refresh current app info */
29
+ refresh: () => Promise<void>;
30
+ /** Reset to initial state */
31
+ reset: () => void;
32
+ }