@umituz/react-native-settings 4.23.71 → 4.23.73
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 +1 -1
- package/src/domains/about/presentation/screens/AboutScreen.tsx +10 -173
- package/src/domains/about/presentation/screens/AboutScreenContainer.tsx +109 -0
- package/src/domains/about/presentation/screens/AboutScreenContent.tsx +122 -0
- package/src/domains/dev/presentation/screens/EnvViewerScreen.tsx +51 -50
- package/src/domains/disclaimer/presentation/screens/DisclaimerScreen.tsx +28 -40
- package/src/domains/feedback/presentation/components/FeedbackModal.tsx +32 -43
- package/src/domains/gamification/components/GamificationScreen/GamificationScreen.tsx +122 -0
- package/src/domains/gamification/components/GamificationScreen/GamificationScreenWithConfig.tsx +72 -0
- package/src/domains/gamification/components/GamificationScreen/index.tsx +8 -178
- package/src/domains/gamification/components/index.ts +2 -1
- package/src/domains/legal/presentation/screens/LegalContentScreen.tsx +19 -29
- package/src/domains/notifications/infrastructure/services/NotificationManager.ts +0 -2
- package/src/domains/notifications/infrastructure/services/NotificationService.ts +5 -1
- package/src/index.ts +0 -1
- package/src/infrastructure/utils/index.ts +7 -0
- package/src/infrastructure/utils/memoUtils.ts +159 -0
- package/src/infrastructure/utils/styleUtils.ts +190 -0
- package/src/presentation/screens/SettingsScreen.tsx +0 -5
- package/src/presentation/components/SettingsErrorBoundary/README.md +0 -67
- package/src/presentation/components/SettingsErrorBoundary.tsx +0 -139
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memo Utilities
|
|
3
|
+
* Centralized memoization helpers to reduce code duplication
|
|
4
|
+
*/
|
|
5
|
+
import { useMemo, useCallback, useRef, DependencyList } from 'react';
|
|
6
|
+
import type { DesignTokens } from '@umituz/react-native-design-system';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Custom hook to create memoized styles from a style factory function
|
|
10
|
+
* @param styleFactory Function that creates styles
|
|
11
|
+
* @param tokens Design tokens
|
|
12
|
+
* @param deps Dependencies for memoization
|
|
13
|
+
* @returns Memoized styles object
|
|
14
|
+
*/
|
|
15
|
+
export function useMemoizedStyles<T>(
|
|
16
|
+
styleFactory: (tokens: DesignTokens) => T,
|
|
17
|
+
tokens: DesignTokens,
|
|
18
|
+
deps: DependencyList = []
|
|
19
|
+
): T {
|
|
20
|
+
return useMemo(() => styleFactory(tokens), [tokens, ...deps]);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Custom hook to create a memoized callback with proper type inference
|
|
25
|
+
* @param callback Function to memoize
|
|
26
|
+
* @param deps Dependencies for memoization
|
|
27
|
+
* @returns Memoized callback
|
|
28
|
+
*/
|
|
29
|
+
export function useMemoizedCallback<T extends (...args: any[]) => any>(
|
|
30
|
+
callback: T,
|
|
31
|
+
deps: DependencyList
|
|
32
|
+
): T {
|
|
33
|
+
return useCallback(callback, deps) as T;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Custom hook to create a memoized value with proper type inference
|
|
38
|
+
* @param factory Function that creates the value
|
|
39
|
+
* @param deps Dependencies for memoization
|
|
40
|
+
* @returns Memoized value
|
|
41
|
+
*/
|
|
42
|
+
export function useMemoizedValue<T>(
|
|
43
|
+
factory: () => T,
|
|
44
|
+
deps: DependencyList
|
|
45
|
+
): T {
|
|
46
|
+
return useMemo(factory, deps);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates a memoized style object with proper caching
|
|
51
|
+
* @param styleCreator Function that creates styles
|
|
52
|
+
* @param deps Dependencies for memoization
|
|
53
|
+
* @returns Memoized styles
|
|
54
|
+
*/
|
|
55
|
+
export function useStyledMemo<T extends Record<string, any>>(
|
|
56
|
+
styleCreator: () => T,
|
|
57
|
+
deps: DependencyList = []
|
|
58
|
+
): T {
|
|
59
|
+
return useMemo(styleCreator, deps);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Memoizes a value with a custom equality check (non-hook version)
|
|
64
|
+
* @param value Value to memoize
|
|
65
|
+
* @param _isEqual Custom equality function
|
|
66
|
+
* @returns Memoized value
|
|
67
|
+
*/
|
|
68
|
+
export function memoWithEquality<T>(
|
|
69
|
+
value: T,
|
|
70
|
+
_isEqual: (prev: T, next: T) => boolean
|
|
71
|
+
): T {
|
|
72
|
+
// This is a utility function, not a hook
|
|
73
|
+
// It cannot use hooks internally
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Creates a cache key for style memoization
|
|
79
|
+
* @param tokens Design tokens
|
|
80
|
+
* @param prefix Optional prefix for the key
|
|
81
|
+
* @returns Cache key string
|
|
82
|
+
*/
|
|
83
|
+
export function createStyleCacheKey(
|
|
84
|
+
tokens: DesignTokens,
|
|
85
|
+
prefix: string = ''
|
|
86
|
+
): string {
|
|
87
|
+
const { colors, spacing, typography } = tokens;
|
|
88
|
+
|
|
89
|
+
return `${prefix}-${JSON.stringify({
|
|
90
|
+
colors: { primary: colors.primary, background: colors.backgroundPrimary },
|
|
91
|
+
spacing: { md: spacing.md, lg: spacing.lg },
|
|
92
|
+
typography: { body: typography.bodyMedium.responsiveFontSize },
|
|
93
|
+
})}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Memoizes a value with a custom equality check
|
|
98
|
+
* @param value Value to memoize
|
|
99
|
+
* @param _isEqual Custom equality function
|
|
100
|
+
* @returns Memoized value
|
|
101
|
+
*/
|
|
102
|
+
export function useMemoWithEquality<T>(
|
|
103
|
+
value: T,
|
|
104
|
+
_isEqual: (prev: T, next: T) => boolean
|
|
105
|
+
): T {
|
|
106
|
+
const ref = useRef<T>(value);
|
|
107
|
+
|
|
108
|
+
return useMemo(() => {
|
|
109
|
+
if (!_isEqual(ref.current, value)) {
|
|
110
|
+
ref.current = value;
|
|
111
|
+
}
|
|
112
|
+
return ref.current;
|
|
113
|
+
}, [value, _isEqual]);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Custom hook that creates a debounced callback
|
|
118
|
+
* @param callback Function to debounce
|
|
119
|
+
* @param delay Delay in milliseconds
|
|
120
|
+
* @returns Debounced callback
|
|
121
|
+
*/
|
|
122
|
+
export function useDebouncedCallback<T extends (...args: any[]) => any>(
|
|
123
|
+
callback: T,
|
|
124
|
+
delay: number
|
|
125
|
+
): T {
|
|
126
|
+
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
127
|
+
|
|
128
|
+
return useCallback((...args: Parameters<T>) => {
|
|
129
|
+
if (timeoutRef.current) {
|
|
130
|
+
clearTimeout(timeoutRef.current);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
timeoutRef.current = setTimeout(() => {
|
|
134
|
+
callback(...args);
|
|
135
|
+
}, delay);
|
|
136
|
+
}, [callback, delay]) as T;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Custom hook that creates a throttled callback
|
|
141
|
+
* @param callback Function to throttle
|
|
142
|
+
* @param delay Delay in milliseconds
|
|
143
|
+
* @returns Throttled callback
|
|
144
|
+
*/
|
|
145
|
+
export function useThrottledCallback<T extends (...args: any[]) => any>(
|
|
146
|
+
callback: T,
|
|
147
|
+
delay: number
|
|
148
|
+
): T {
|
|
149
|
+
const lastRunRef = useRef<number>(0);
|
|
150
|
+
|
|
151
|
+
return useCallback((...args: Parameters<T>) => {
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
|
|
154
|
+
if (now - lastRunRef.current >= delay) {
|
|
155
|
+
callback(...args);
|
|
156
|
+
lastRunRef.current = now;
|
|
157
|
+
}
|
|
158
|
+
}, [callback, delay]) as T;
|
|
159
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Style Utilities
|
|
3
|
+
* Centralized style creation functions to reduce code duplication
|
|
4
|
+
*/
|
|
5
|
+
import { StyleSheet, ViewStyle, TextStyle, ImageStyle } from 'react-native';
|
|
6
|
+
import type { DesignTokens } from '@umituz/react-native-design-system';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a container style with flex 1
|
|
10
|
+
*/
|
|
11
|
+
export const createContainerStyle = (overrides: ViewStyle = {}): ViewStyle => ({
|
|
12
|
+
flex: 1,
|
|
13
|
+
...overrides,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a centered container style
|
|
18
|
+
*/
|
|
19
|
+
export const createCenteredContainerStyle = (overrides: ViewStyle = {}): ViewStyle => ({
|
|
20
|
+
flex: 1,
|
|
21
|
+
justifyContent: 'center',
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
...overrides,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Creates a row style for horizontal layouts
|
|
28
|
+
*/
|
|
29
|
+
export const createRowStyle = (overrides: ViewStyle = {}): ViewStyle => ({
|
|
30
|
+
flexDirection: 'row',
|
|
31
|
+
alignItems: 'center',
|
|
32
|
+
...overrides,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a header style
|
|
37
|
+
*/
|
|
38
|
+
export const createHeaderStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
39
|
+
paddingHorizontal: tokens.spacing.lg,
|
|
40
|
+
paddingVertical: tokens.spacing.md,
|
|
41
|
+
...overrides,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Creates a section style
|
|
46
|
+
*/
|
|
47
|
+
export const createSectionStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
48
|
+
padding: tokens.spacing.lg,
|
|
49
|
+
...overrides,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a card style
|
|
54
|
+
*/
|
|
55
|
+
export const createCardStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
56
|
+
backgroundColor: tokens.colors.surface,
|
|
57
|
+
borderRadius: tokens.borders.radius.md,
|
|
58
|
+
padding: tokens.spacing.lg,
|
|
59
|
+
...overrides,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a title text style
|
|
64
|
+
*/
|
|
65
|
+
export const createTitleStyle = (tokens: DesignTokens, overrides: TextStyle = {}): TextStyle => ({
|
|
66
|
+
fontSize: tokens.typography.headlineMedium.responsiveFontSize,
|
|
67
|
+
fontWeight: '600',
|
|
68
|
+
color: tokens.colors.textPrimary,
|
|
69
|
+
...overrides,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a subtitle text style
|
|
74
|
+
*/
|
|
75
|
+
export const createSubtitleStyle = (tokens: DesignTokens, overrides: TextStyle = {}): TextStyle => ({
|
|
76
|
+
fontSize: tokens.typography.bodyMedium.responsiveFontSize,
|
|
77
|
+
color: tokens.colors.textSecondary,
|
|
78
|
+
...overrides,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Creates a button style
|
|
83
|
+
*/
|
|
84
|
+
export const createButtonStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
85
|
+
backgroundColor: tokens.colors.primary,
|
|
86
|
+
borderRadius: tokens.borders.radius.md,
|
|
87
|
+
paddingVertical: tokens.spacing.md,
|
|
88
|
+
paddingHorizontal: tokens.spacing.lg,
|
|
89
|
+
...overrides,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates an icon container style
|
|
94
|
+
*/
|
|
95
|
+
export const createIconContainerStyle = (
|
|
96
|
+
size: number = 48,
|
|
97
|
+
tokens: DesignTokens,
|
|
98
|
+
overrides: ViewStyle = {}
|
|
99
|
+
): ViewStyle => ({
|
|
100
|
+
width: size,
|
|
101
|
+
height: size,
|
|
102
|
+
borderRadius: size / 2,
|
|
103
|
+
justifyContent: 'center',
|
|
104
|
+
alignItems: 'center',
|
|
105
|
+
backgroundColor: tokens.colors.surfaceSecondary,
|
|
106
|
+
...overrides,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Creates a scroll content style
|
|
111
|
+
*/
|
|
112
|
+
export const createScrollContentStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
113
|
+
padding: tokens.spacing.lg,
|
|
114
|
+
...overrides,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Creates a separator/border style
|
|
119
|
+
*/
|
|
120
|
+
export const createSeparatorStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
121
|
+
height: 1,
|
|
122
|
+
backgroundColor: tokens.colors.border,
|
|
123
|
+
...overrides,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Creates a margin utility style
|
|
128
|
+
*/
|
|
129
|
+
export const createMarginStyle = (
|
|
130
|
+
spacing: 'xs' | 'sm' | 'md' | 'lg' | 'xl',
|
|
131
|
+
tokens: DesignTokens,
|
|
132
|
+
overrides: ViewStyle = {}
|
|
133
|
+
): ViewStyle => ({
|
|
134
|
+
margin: tokens.spacing[spacing],
|
|
135
|
+
...overrides,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Creates a padding utility style
|
|
140
|
+
*/
|
|
141
|
+
export const createPaddingStyle = (
|
|
142
|
+
spacing: 'xs' | 'sm' | 'md' | 'lg' | 'xl',
|
|
143
|
+
tokens: DesignTokens,
|
|
144
|
+
overrides: ViewStyle = {}
|
|
145
|
+
): ViewStyle => ({
|
|
146
|
+
padding: tokens.spacing[spacing],
|
|
147
|
+
...overrides,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Combines multiple styles into one
|
|
152
|
+
*/
|
|
153
|
+
export const combineStyles = (
|
|
154
|
+
...styles: (ViewStyle | TextStyle | ImageStyle | undefined | false)[]
|
|
155
|
+
): ViewStyle | TextStyle | ImageStyle => {
|
|
156
|
+
return StyleSheet.flatten(styles.filter(Boolean));
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Creates a responsive style based on screen dimensions
|
|
161
|
+
*/
|
|
162
|
+
export const createResponsiveStyle = (
|
|
163
|
+
_tokens: DesignTokens,
|
|
164
|
+
phoneStyle: ViewStyle,
|
|
165
|
+
_tabletStyle?: ViewStyle
|
|
166
|
+
): ViewStyle => {
|
|
167
|
+
// For now, return phone style. Can be enhanced with actual responsive logic
|
|
168
|
+
return phoneStyle;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Type guard for ViewStyle
|
|
173
|
+
*/
|
|
174
|
+
export const isViewStyle = (style: any): style is ViewStyle => {
|
|
175
|
+
return style && typeof style === 'object';
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Creates a safe area aware style
|
|
180
|
+
*/
|
|
181
|
+
export const createSafeAreaStyle = (
|
|
182
|
+
insets: { top?: number; bottom?: number; left?: number; right?: number },
|
|
183
|
+
overrides: ViewStyle = {}
|
|
184
|
+
): ViewStyle => ({
|
|
185
|
+
paddingTop: insets.top,
|
|
186
|
+
paddingBottom: insets.bottom,
|
|
187
|
+
paddingLeft: insets.left,
|
|
188
|
+
paddingRight: insets.right,
|
|
189
|
+
...overrides,
|
|
190
|
+
});
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
} from "@umituz/react-native-design-system";
|
|
11
11
|
import { SettingsHeader } from "./components/SettingsHeader";
|
|
12
12
|
import { SettingsContent } from "./components/SettingsContent";
|
|
13
|
-
import { SettingsErrorBoundary } from "../components/SettingsErrorBoundary";
|
|
14
13
|
import { normalizeSettingsConfig } from "./utils/normalizeConfig";
|
|
15
14
|
import { useFeatureDetection } from "./hooks/useFeatureDetection";
|
|
16
15
|
import type { SettingsConfig, CustomSettingsSection } from "./types";
|
|
@@ -95,7 +94,6 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
|
|
95
94
|
// Workaround: Use conditional rendering with type assertion
|
|
96
95
|
if (showHeader) {
|
|
97
96
|
return <ScreenLayout header={<SettingsHeader showCloseButton={showCloseButton} onClose={onClose} />}>
|
|
98
|
-
<SettingsErrorBoundary>
|
|
99
97
|
{children ?? (
|
|
100
98
|
<SettingsContent
|
|
101
99
|
normalizedConfig={normalizedConfig}
|
|
@@ -110,12 +108,10 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
|
|
110
108
|
gamificationConfig={gamificationConfig}
|
|
111
109
|
/>
|
|
112
110
|
)}
|
|
113
|
-
</SettingsErrorBoundary>
|
|
114
111
|
</ScreenLayout>;
|
|
115
112
|
}
|
|
116
113
|
|
|
117
114
|
return <ScreenLayout>
|
|
118
|
-
<SettingsErrorBoundary>
|
|
119
115
|
{children ?? (
|
|
120
116
|
<SettingsContent
|
|
121
117
|
normalizedConfig={normalizedConfig}
|
|
@@ -130,6 +126,5 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
|
|
130
126
|
gamificationConfig={gamificationConfig}
|
|
131
127
|
/>
|
|
132
128
|
)}
|
|
133
|
-
</SettingsErrorBoundary>
|
|
134
129
|
</ScreenLayout>;
|
|
135
130
|
};
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# Settings Error Boundary
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Error boundary component for catching and handling errors in settings screens and components, providing fallback UI and error recovery options.
|
|
6
|
-
|
|
7
|
-
## File Paths
|
|
8
|
-
|
|
9
|
-
- **Component**: `/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-settings/src/presentation/components/SettingsErrorBoundary/SettingsErrorBoundary.tsx`
|
|
10
|
-
|
|
11
|
-
## Strategy
|
|
12
|
-
|
|
13
|
-
1. **Error Containment**: Catches JavaScript errors in the component tree below it, preventing app crashes
|
|
14
|
-
2. **User-Friendly Fallback**: Displays clear, non-technical error messages to users
|
|
15
|
-
3. **Error Recovery**: Provides retry or reset actions to recover from errors
|
|
16
|
-
4. **Development Support**: Shows detailed error information in development mode for debugging
|
|
17
|
-
5. **Error Reporting**: Integrates with error tracking services for monitoring
|
|
18
|
-
|
|
19
|
-
## Restrictions (Forbidden)
|
|
20
|
-
|
|
21
|
-
### DO NOT
|
|
22
|
-
- ❌ DO NOT wrap individual small components (use at screen or major section level)
|
|
23
|
-
- ❌ DO NOT use error boundaries to handle expected errors (e.g., network failures)
|
|
24
|
-
- ❌ DO NOT show technical stack traces to end users in production
|
|
25
|
-
|
|
26
|
-
### NEVER
|
|
27
|
-
- ❌ NEVER use error boundaries inside event handlers or async code
|
|
28
|
-
- ❌ NEVER use error boundaries to control flow or business logic
|
|
29
|
-
- ❌ NEVER expose sensitive information in error messages
|
|
30
|
-
|
|
31
|
-
### AVOID
|
|
32
|
-
- ❌ AVOID nesting multiple error boundaries without clear purpose
|
|
33
|
-
- ❌ AVOID generic error messages that don't help users understand what happened
|
|
34
|
-
- ❌ AVOID blocking the entire app when a recoverable error occurs
|
|
35
|
-
|
|
36
|
-
## Rules (Mandatory)
|
|
37
|
-
|
|
38
|
-
### ALWAYS
|
|
39
|
-
- ✅ ALWAYS provide a clear fallback UI when errors occur
|
|
40
|
-
- ✅ ALWAYS log errors for debugging and monitoring
|
|
41
|
-
- ✅ ALWAYS integrate with error tracking services (e.g., Sentry)
|
|
42
|
-
- ✅ ALWAYS show user-friendly error messages in production
|
|
43
|
-
|
|
44
|
-
### MUST
|
|
45
|
-
- ✅ MUST offer recovery options (retry, reset, or navigation) to users
|
|
46
|
-
- ✅ MUST ensure error boundaries don't interfere with normal error handling
|
|
47
|
-
- ✅ MUST test error scenarios during development
|
|
48
|
-
|
|
49
|
-
### SHOULD
|
|
50
|
-
- ✅ SHOULD provide context-specific error messages when possible
|
|
51
|
-
- ✅ SHOULD include development-only error details for debugging
|
|
52
|
-
- ✅ SHOULD offer a way to report errors or contact support
|
|
53
|
-
|
|
54
|
-
## AI Agent Guidelines
|
|
55
|
-
|
|
56
|
-
1. **File Reference**: When implementing error handling, refer to `/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-settings/src/presentation/components/SettingsErrorBoundary/SettingsErrorBoundary.tsx`
|
|
57
|
-
2. **Placement Strategy**: Place error boundaries at strategic locations (screen level, major feature sections)
|
|
58
|
-
3. **Fallback Design**: Design fallback UIs that match your app's visual design
|
|
59
|
-
4. **Error Tracking**: Always integrate with error tracking services like Sentry or Crashlytics
|
|
60
|
-
5. **Recovery Logic**: Implement appropriate recovery actions based on error type and context
|
|
61
|
-
|
|
62
|
-
## Component Reference
|
|
63
|
-
|
|
64
|
-
Related components:
|
|
65
|
-
- **SettingsScreen**: Main screen component that uses error boundaries
|
|
66
|
-
- **SettingsContent**: Content component wrapped by error boundaries
|
|
67
|
-
- **React Error Boundaries**: Official React documentation for error boundaries
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Settings Error Boundary Component
|
|
3
|
-
* Catches and handles errors in settings components
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { Component, ReactNode } from 'react';
|
|
7
|
-
import { View, StyleSheet } from 'react-native';
|
|
8
|
-
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
9
|
-
import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
|
|
10
|
-
import { useLocalization } from '../../domains/localization';
|
|
11
|
-
|
|
12
|
-
interface Props {
|
|
13
|
-
children: ReactNode;
|
|
14
|
-
fallback?: ReactNode;
|
|
15
|
-
fallbackTitle?: string;
|
|
16
|
-
fallbackMessage?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface State {
|
|
20
|
-
hasError: boolean;
|
|
21
|
-
error?: Error;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class SettingsErrorBoundary extends Component<Props, State> {
|
|
25
|
-
override state: State = {
|
|
26
|
-
hasError: false,
|
|
27
|
-
error: undefined,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
static getDerivedStateFromError(error: Error): State {
|
|
31
|
-
return { hasError: true, error };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
override componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
|
|
35
|
-
// Log error to console in development
|
|
36
|
-
if (__DEV__) {
|
|
37
|
-
console.error('Settings Error Boundary caught an error:', error);
|
|
38
|
-
console.error('Error Info:', errorInfo);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// TODO: Send to error tracking service in production
|
|
42
|
-
// Example: Sentry.captureException(error, { contexts: { react: { errorInfo } } });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
override render(): ReactNode {
|
|
46
|
-
const { hasError, error } = this.state;
|
|
47
|
-
const { children, fallback, fallbackTitle, fallbackMessage } = this.props;
|
|
48
|
-
|
|
49
|
-
if (hasError) {
|
|
50
|
-
if (fallback) {
|
|
51
|
-
return fallback;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<ErrorBoundaryFallback
|
|
56
|
-
error={error}
|
|
57
|
-
fallbackTitle={fallbackTitle}
|
|
58
|
-
fallbackMessage={fallbackMessage}
|
|
59
|
-
/>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return children;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
interface ErrorBoundaryFallbackProps {
|
|
68
|
-
error?: Error;
|
|
69
|
-
fallbackTitle?: string;
|
|
70
|
-
fallbackMessage?: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const ErrorBoundaryFallback: React.FC<ErrorBoundaryFallbackProps> = ({
|
|
74
|
-
error,
|
|
75
|
-
fallbackTitle,
|
|
76
|
-
fallbackMessage
|
|
77
|
-
}) => {
|
|
78
|
-
const tokens = useAppDesignTokens();
|
|
79
|
-
const { t } = useLocalization();
|
|
80
|
-
|
|
81
|
-
const title = __DEV__ && error?.message
|
|
82
|
-
? t("error_boundary.dev_title")
|
|
83
|
-
: (fallbackTitle || t("error_boundary.title"));
|
|
84
|
-
|
|
85
|
-
const message = __DEV__ && error?.message
|
|
86
|
-
? `${t("error_boundary.dev_message")}: ${error.message}`
|
|
87
|
-
: (fallbackMessage || t("error_boundary.message"));
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
|
|
91
|
-
<View style={[styles.content, { backgroundColor: tokens.colors.surface }]}>
|
|
92
|
-
<AtomicIcon
|
|
93
|
-
name="alert-circle"
|
|
94
|
-
color="warning"
|
|
95
|
-
size="lg"
|
|
96
|
-
style={styles.icon}
|
|
97
|
-
/>
|
|
98
|
-
<AtomicText
|
|
99
|
-
type="headlineSmall"
|
|
100
|
-
color="primary"
|
|
101
|
-
style={styles.title}
|
|
102
|
-
>
|
|
103
|
-
{title}
|
|
104
|
-
</AtomicText>
|
|
105
|
-
<AtomicText
|
|
106
|
-
type="bodyMedium"
|
|
107
|
-
color="secondary"
|
|
108
|
-
style={styles.message}
|
|
109
|
-
>
|
|
110
|
-
{message}
|
|
111
|
-
</AtomicText>
|
|
112
|
-
</View>
|
|
113
|
-
</View>
|
|
114
|
-
);
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const styles = StyleSheet.create({
|
|
118
|
-
container: {
|
|
119
|
-
flex: 1,
|
|
120
|
-
padding: 16,
|
|
121
|
-
justifyContent: 'center',
|
|
122
|
-
},
|
|
123
|
-
content: {
|
|
124
|
-
alignItems: 'center',
|
|
125
|
-
padding: 24,
|
|
126
|
-
borderRadius: 12,
|
|
127
|
-
},
|
|
128
|
-
icon: {
|
|
129
|
-
marginBottom: 16,
|
|
130
|
-
},
|
|
131
|
-
title: {
|
|
132
|
-
marginBottom: 8,
|
|
133
|
-
textAlign: 'center',
|
|
134
|
-
},
|
|
135
|
-
message: {
|
|
136
|
-
textAlign: 'center',
|
|
137
|
-
lineHeight: 20,
|
|
138
|
-
},
|
|
139
|
-
});
|