@umituz/react-native-settings 5.2.34 → 5.2.36
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 -4
- package/src/domains/about/presentation/screens/AboutScreenContent.tsx +87 -63
- package/src/domains/appearance/data/colorPalettes.ts +0 -23
- package/src/domains/appearance/presentation/components/CustomColorsSection.tsx +2 -4
- package/src/domains/appearance/presentation/components/ThemeOption.tsx +2 -2
- package/src/domains/dev/presentation/components/DevSettingsSection.tsx +5 -2
- package/src/domains/faqs/presentation/screens/FAQScreen.tsx +19 -25
- package/src/domains/feedback/presentation/components/FeedbackForm.tsx +160 -81
- package/src/domains/gamification/components/GamificationScreen/GamificationScreenWithConfig.tsx +11 -11
- package/src/domains/localization/infrastructure/components/LanguageSwitcher.tsx +0 -2
- package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +1 -1
- package/src/domains/localization/presentation/screens/LanguageSelectionScreen.tsx +85 -48
- package/src/domains/localization/presentation/screens/__tests__/LanguageSelectionScreen.test.tsx +0 -15
- package/src/domains/notifications/presentation/screens/NotificationsScreen.tsx +1 -3
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +0 -4
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +69 -31
- package/src/domains/rating/presentation/components/StarRating.tsx +7 -13
- package/src/infrastructure/utils/configFactory.ts +0 -26
- package/src/infrastructure/utils/constants/textLimits.ts +0 -2
- package/src/infrastructure/utils/sanitizers.ts +1 -25
- package/src/infrastructure/utils/validation/core.ts +0 -33
- package/src/infrastructure/utils/validation/formValidators.ts +7 -1
- package/src/infrastructure/utils/validation/index.ts +2 -33
- package/src/infrastructure/utils/validators.ts +0 -6
- package/src/presentation/navigation/utils/index.ts +1 -7
- package/src/presentation/navigation/utils/navigationHelpers.ts +2 -87
- package/src/presentation/screens/components/SettingsContent.tsx +4 -19
- package/src/presentation/screens/components/sections/CustomSettingsList.tsx +3 -8
- package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +0 -4
- package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +0 -4
- package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +0 -4
- package/src/presentation/utils/screenFactory.ts +0 -25
- package/src/utils/appUtils.ts +0 -18
- package/src/utils/devUtils.ts +0 -10
- package/src/utils/errorUtils.ts +0 -22
- package/src/domains/about/utils/index.ts +0 -156
- package/src/domains/faqs/domain/services/index.ts +0 -1
- package/src/domains/faqs/presentation/screens/index.ts +0 -2
- package/src/domains/gamification/components/GamificationScreen/Header.tsx +0 -30
- package/src/domains/legal/presentation/components/LegalLinks.tsx +0 -137
- package/src/domains/legal/presentation/components/index.ts +0 -5
- package/src/domains/localization/infrastructure/config/languagesData.ts +0 -26
- package/src/domains/localization/infrastructure/hooks/TranslationHook.ts +0 -37
- package/src/domains/localization/infrastructure/storage/AsyncStorageWrapper.ts +0 -34
- package/src/infrastructure/storage/storeConfig.ts +0 -114
- package/src/infrastructure/types/commonComponentTypes.ts +0 -142
- package/src/infrastructure/utils/async/core.ts +0 -110
- package/src/infrastructure/utils/async/debounceAndBatch.ts +0 -69
- package/src/infrastructure/utils/async/index.ts +0 -8
- package/src/infrastructure/utils/async/retryAndTimeout.ts +0 -65
- package/src/infrastructure/utils/dateUtils.ts +0 -61
- package/src/infrastructure/utils/errorHandlers.ts +0 -250
- package/src/infrastructure/utils/index.ts +0 -12
- package/src/infrastructure/utils/memoComparisonUtils.ts +0 -66
- package/src/infrastructure/utils/memoUtils.ts +0 -167
- package/src/infrastructure/utils/styleTokens.ts +0 -145
- package/src/infrastructure/utils/styles/componentStyles.ts +0 -90
- package/src/infrastructure/utils/styles/index.ts +0 -9
- package/src/infrastructure/utils/styles/layoutStyles.ts +0 -56
- package/src/infrastructure/utils/styles/spacingStyles.ts +0 -33
- package/src/infrastructure/utils/styles/styleHelpers.ts +0 -22
- package/src/infrastructure/utils/translationHelpers.ts +0 -81
- package/src/infrastructure/utils/validation/numericValidators.ts +0 -66
- package/src/infrastructure/utils/validation/passwordValidator.ts +0 -53
- package/src/infrastructure/utils/validation/textValidators.ts +0 -118
- package/src/presentation/components/ErrorBoundary/SettingsErrorBoundary.tsx +0 -105
- package/src/presentation/components/ErrorBoundary/index.ts +0 -12
- package/src/presentation/components/ErrorBoundary/withErrorBoundary.tsx +0 -45
- package/src/utils/hooks/index.ts +0 -6
- package/src/utils/index.ts +0 -3
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Component Style Utilities
|
|
3
|
-
* Reusable component styling patterns
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { ViewStyle, TextStyle } from 'react-native';
|
|
7
|
-
import type { DesignTokens } from '@umituz/react-native-design-system';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Creates a header style
|
|
11
|
-
*/
|
|
12
|
-
export const createHeaderStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
13
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
14
|
-
paddingVertical: tokens.spacing.md,
|
|
15
|
-
...overrides,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Creates a section style
|
|
20
|
-
*/
|
|
21
|
-
export const createSectionStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
22
|
-
padding: tokens.spacing.lg,
|
|
23
|
-
...overrides,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Creates a card style
|
|
28
|
-
*/
|
|
29
|
-
export const createCardStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
30
|
-
backgroundColor: tokens.colors.surface,
|
|
31
|
-
borderRadius: tokens.borders.radius.md,
|
|
32
|
-
padding: tokens.spacing.lg,
|
|
33
|
-
...overrides,
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Creates a button style
|
|
38
|
-
*/
|
|
39
|
-
export const createButtonStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
40
|
-
backgroundColor: tokens.colors.primary,
|
|
41
|
-
borderRadius: tokens.borders.radius.md,
|
|
42
|
-
paddingVertical: tokens.spacing.md,
|
|
43
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
44
|
-
...overrides,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Creates an icon container style
|
|
49
|
-
*/
|
|
50
|
-
export const createIconContainerStyle = (
|
|
51
|
-
size: number = 48,
|
|
52
|
-
tokens: DesignTokens,
|
|
53
|
-
overrides: ViewStyle = {}
|
|
54
|
-
): ViewStyle => ({
|
|
55
|
-
width: size,
|
|
56
|
-
height: size,
|
|
57
|
-
borderRadius: size / 2,
|
|
58
|
-
justifyContent: 'center',
|
|
59
|
-
alignItems: 'center',
|
|
60
|
-
backgroundColor: tokens.colors.surfaceSecondary,
|
|
61
|
-
...overrides,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Creates a separator/border style
|
|
66
|
-
*/
|
|
67
|
-
export const createSeparatorStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
68
|
-
height: 1,
|
|
69
|
-
backgroundColor: tokens.colors.border,
|
|
70
|
-
...overrides,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Creates a title text style
|
|
75
|
-
*/
|
|
76
|
-
export const createTitleStyle = (tokens: DesignTokens, overrides: TextStyle = {}): TextStyle => ({
|
|
77
|
-
fontSize: tokens.typography.headlineMedium.responsiveFontSize,
|
|
78
|
-
fontWeight: '600',
|
|
79
|
-
color: tokens.colors.textPrimary,
|
|
80
|
-
...overrides,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Creates a subtitle text style
|
|
85
|
-
*/
|
|
86
|
-
export const createSubtitleStyle = (tokens: DesignTokens, overrides: TextStyle = {}): TextStyle => ({
|
|
87
|
-
fontSize: tokens.typography.bodyMedium.responsiveFontSize,
|
|
88
|
-
color: tokens.colors.textSecondary,
|
|
89
|
-
...overrides,
|
|
90
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layout Style Utilities
|
|
3
|
-
* Common layout patterns
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { ViewStyle } from 'react-native';
|
|
7
|
-
import type { DesignTokens } from '@umituz/react-native-design-system';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Creates a container style with flex 1
|
|
11
|
-
*/
|
|
12
|
-
export const createContainerStyle = (overrides: ViewStyle = {}): ViewStyle => ({
|
|
13
|
-
flex: 1,
|
|
14
|
-
...overrides,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Creates a centered container style
|
|
19
|
-
*/
|
|
20
|
-
export const createCenteredContainerStyle = (overrides: ViewStyle = {}): ViewStyle => ({
|
|
21
|
-
flex: 1,
|
|
22
|
-
justifyContent: 'center',
|
|
23
|
-
alignItems: 'center',
|
|
24
|
-
...overrides,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Creates a row style for horizontal layouts
|
|
29
|
-
*/
|
|
30
|
-
export const createRowStyle = (overrides: ViewStyle = {}): ViewStyle => ({
|
|
31
|
-
flexDirection: 'row',
|
|
32
|
-
alignItems: 'center',
|
|
33
|
-
...overrides,
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Creates a scroll content style
|
|
38
|
-
*/
|
|
39
|
-
export const createScrollContentStyle = (tokens: DesignTokens, overrides: ViewStyle = {}): ViewStyle => ({
|
|
40
|
-
padding: tokens.spacing.lg,
|
|
41
|
-
...overrides,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Creates a safe area aware style
|
|
46
|
-
*/
|
|
47
|
-
export const createSafeAreaStyle = (
|
|
48
|
-
insets: { top?: number; bottom?: number; left?: number; right?: number },
|
|
49
|
-
overrides: ViewStyle = {}
|
|
50
|
-
): ViewStyle => ({
|
|
51
|
-
paddingTop: insets.top,
|
|
52
|
-
paddingBottom: insets.bottom,
|
|
53
|
-
paddingLeft: insets.left,
|
|
54
|
-
paddingRight: insets.right,
|
|
55
|
-
...overrides,
|
|
56
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spacing Style Utilities
|
|
3
|
-
* Margin and padding helpers
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { ViewStyle } from 'react-native';
|
|
7
|
-
import type { DesignTokens } from '@umituz/react-native-design-system';
|
|
8
|
-
|
|
9
|
-
type SpacingSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Creates a margin utility style
|
|
13
|
-
*/
|
|
14
|
-
export const createMarginStyle = (
|
|
15
|
-
spacing: SpacingSize,
|
|
16
|
-
tokens: DesignTokens,
|
|
17
|
-
overrides: ViewStyle = {}
|
|
18
|
-
): ViewStyle => ({
|
|
19
|
-
margin: tokens.spacing[spacing],
|
|
20
|
-
...overrides,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Creates a padding utility style
|
|
25
|
-
*/
|
|
26
|
-
export const createPaddingStyle = (
|
|
27
|
-
spacing: SpacingSize,
|
|
28
|
-
tokens: DesignTokens,
|
|
29
|
-
overrides: ViewStyle = {}
|
|
30
|
-
): ViewStyle => ({
|
|
31
|
-
padding: tokens.spacing[spacing],
|
|
32
|
-
...overrides,
|
|
33
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Style Helper Utilities
|
|
3
|
-
* Generic style manipulation functions
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { StyleSheet, ViewStyle, TextStyle, ImageStyle } from 'react-native';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Combines multiple styles into one
|
|
10
|
-
*/
|
|
11
|
-
export const combineStyles = (
|
|
12
|
-
...styles: (ViewStyle | TextStyle | ImageStyle | undefined | false | null)[]
|
|
13
|
-
): ViewStyle | TextStyle | ImageStyle => {
|
|
14
|
-
return StyleSheet.flatten(styles.filter(Boolean));
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Type guard for ViewStyle
|
|
19
|
-
*/
|
|
20
|
-
export const isViewStyle = (style: unknown): style is ViewStyle => {
|
|
21
|
-
return typeof style === 'object' && style !== null;
|
|
22
|
-
};
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Translation Helper Utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides safe translation utilities that handle cases where
|
|
5
|
-
* the translation function might not be available yet.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export type TranslationFunction = (key: string, params?: Record<string, string | number>) => string;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Safely get a translation with fallback
|
|
12
|
-
* @param t Translation function
|
|
13
|
-
* @param key Translation key
|
|
14
|
-
* @param fallback Fallback text if translation fails
|
|
15
|
-
* @returns Translated text or fallback
|
|
16
|
-
*/
|
|
17
|
-
export function getTranslationWithFallback(
|
|
18
|
-
t: TranslationFunction | unknown,
|
|
19
|
-
key: string,
|
|
20
|
-
fallback: string
|
|
21
|
-
): string {
|
|
22
|
-
if (typeof t === "function") {
|
|
23
|
-
try {
|
|
24
|
-
return t(key);
|
|
25
|
-
} catch {
|
|
26
|
-
return fallback;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return fallback;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Get translation with parameters support
|
|
34
|
-
* @param t Translation function
|
|
35
|
-
* @param key Translation key
|
|
36
|
-
* @param params Parameters for translation
|
|
37
|
-
* @param fallback Fallback text if translation fails
|
|
38
|
-
* @returns Translated text or fallback
|
|
39
|
-
*/
|
|
40
|
-
export function getTranslationWithParams(
|
|
41
|
-
t: TranslationFunction | unknown,
|
|
42
|
-
key: string,
|
|
43
|
-
params: Record<string, string | number>,
|
|
44
|
-
fallback: string
|
|
45
|
-
): string {
|
|
46
|
-
if (typeof t === "function") {
|
|
47
|
-
try {
|
|
48
|
-
return t(key, params);
|
|
49
|
-
} catch {
|
|
50
|
-
return fallback;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return fallback;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Check if translation function is available
|
|
58
|
-
* @param t Translation function to check
|
|
59
|
-
* @returns true if translation function is available
|
|
60
|
-
*/
|
|
61
|
-
export function isTranslationFunctionAvailable(t: unknown): t is TranslationFunction {
|
|
62
|
-
return typeof t === "function";
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Create a safe translation wrapper that always returns a string
|
|
67
|
-
* @param t Translation function
|
|
68
|
-
* @returns Safe translation function
|
|
69
|
-
*/
|
|
70
|
-
export function createSafeTranslator(t: TranslationFunction | unknown): TranslationFunction {
|
|
71
|
-
return (key: string, params?: Record<string, string | number>) => {
|
|
72
|
-
if (isTranslationFunctionAvailable(t)) {
|
|
73
|
-
try {
|
|
74
|
-
return t(key, params);
|
|
75
|
-
} catch {
|
|
76
|
-
return key;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return key;
|
|
80
|
-
};
|
|
81
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Numeric Validators
|
|
3
|
-
* Validation functions for numeric inputs
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { ValidationResult } from "./core";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Validates a rating value (1-5)
|
|
10
|
-
*/
|
|
11
|
-
export const validateRating = (rating: number): ValidationResult => {
|
|
12
|
-
if (rating < 1 || rating > 5) {
|
|
13
|
-
return { isValid: false, error: "Rating must be between 1 and 5" };
|
|
14
|
-
}
|
|
15
|
-
return { isValid: true };
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Validates a number is within a range
|
|
20
|
-
*/
|
|
21
|
-
export const validateRange = (
|
|
22
|
-
value: number,
|
|
23
|
-
min: number,
|
|
24
|
-
max: number,
|
|
25
|
-
fieldName?: string
|
|
26
|
-
): ValidationResult => {
|
|
27
|
-
if (value < min || value > max) {
|
|
28
|
-
return {
|
|
29
|
-
isValid: false,
|
|
30
|
-
error: `${fieldName || "Value"} must be between ${min} and ${max}`,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
return { isValid: true };
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Validates a positive number
|
|
38
|
-
*/
|
|
39
|
-
export const validatePositiveNumber = (
|
|
40
|
-
value: number,
|
|
41
|
-
fieldName?: string
|
|
42
|
-
): ValidationResult => {
|
|
43
|
-
if (value <= 0) {
|
|
44
|
-
return {
|
|
45
|
-
isValid: false,
|
|
46
|
-
error: `${fieldName || "Value"} must be greater than 0`,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
return { isValid: true };
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Validates a non-negative number
|
|
54
|
-
*/
|
|
55
|
-
export const validateNonNegativeNumber = (
|
|
56
|
-
value: number,
|
|
57
|
-
fieldName?: string
|
|
58
|
-
): ValidationResult => {
|
|
59
|
-
if (value < 0) {
|
|
60
|
-
return {
|
|
61
|
-
isValid: false,
|
|
62
|
-
error: `${fieldName || "Value"} must be 0 or greater`,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
return { isValid: true };
|
|
66
|
-
};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Password Validator
|
|
3
|
-
* Password-specific validation logic
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { ValidationResult, PasswordValidationOptions } from "./core";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Validates a password
|
|
10
|
-
*/
|
|
11
|
-
export const validatePassword = (
|
|
12
|
-
password: string,
|
|
13
|
-
options: PasswordValidationOptions = {}
|
|
14
|
-
): ValidationResult => {
|
|
15
|
-
const {
|
|
16
|
-
minLength = 8,
|
|
17
|
-
requireUppercase = false,
|
|
18
|
-
requireLowercase = false,
|
|
19
|
-
requireNumber = false,
|
|
20
|
-
requireSpecialChar = false,
|
|
21
|
-
} = options;
|
|
22
|
-
|
|
23
|
-
const errors: string[] = [];
|
|
24
|
-
|
|
25
|
-
if (password.length < minLength) {
|
|
26
|
-
errors.push(`at least ${minLength} characters`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (requireUppercase && !/[A-Z]/.test(password)) {
|
|
30
|
-
errors.push("one uppercase letter");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (requireLowercase && !/[a-z]/.test(password)) {
|
|
34
|
-
errors.push("one lowercase letter");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (requireNumber && !/\d/.test(password)) {
|
|
38
|
-
errors.push("one number");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (requireSpecialChar && !/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) {
|
|
42
|
-
errors.push("one special character");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (errors.length > 0) {
|
|
46
|
-
return {
|
|
47
|
-
isValid: false,
|
|
48
|
-
error: `Password must contain ${errors.join(", ")}`,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return { isValid: true };
|
|
53
|
-
};
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Text Validators
|
|
3
|
-
* Validation functions for text-based inputs
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { ValidationResult, TextValidationOptions } from "./core";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Validates a text field based on provided options
|
|
10
|
-
*/
|
|
11
|
-
export const validateTextField = (
|
|
12
|
-
value: string,
|
|
13
|
-
options: TextValidationOptions = {}
|
|
14
|
-
): ValidationResult => {
|
|
15
|
-
const {
|
|
16
|
-
minLength,
|
|
17
|
-
maxLength,
|
|
18
|
-
required = false,
|
|
19
|
-
pattern,
|
|
20
|
-
customValidator,
|
|
21
|
-
} = options;
|
|
22
|
-
|
|
23
|
-
// Check required
|
|
24
|
-
if (required && !value.trim()) {
|
|
25
|
-
return { isValid: false, error: "This field is required" };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Skip other validations if empty and not required
|
|
29
|
-
if (!value.trim() && !required) {
|
|
30
|
-
return { isValid: true };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Check min length
|
|
34
|
-
if (minLength && value.length < minLength) {
|
|
35
|
-
return { isValid: false, error: `Minimum length is ${minLength} characters` };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Check max length
|
|
39
|
-
if (maxLength && value.length > maxLength) {
|
|
40
|
-
return { isValid: false, error: `Maximum length is ${maxLength} characters` };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Check pattern
|
|
44
|
-
if (pattern && !pattern.test(value)) {
|
|
45
|
-
return { isValid: false, error: "Invalid format" };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Custom validator
|
|
49
|
-
if (customValidator) {
|
|
50
|
-
const customError = customValidator(value);
|
|
51
|
-
if (customError) {
|
|
52
|
-
return { isValid: false, error: customError };
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return { isValid: true };
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Validates an email address
|
|
61
|
-
*/
|
|
62
|
-
export const validateEmail = (
|
|
63
|
-
email: string,
|
|
64
|
-
options: { required?: boolean; allowEmpty?: boolean } = {}
|
|
65
|
-
): ValidationResult => {
|
|
66
|
-
const { required = false, allowEmpty = false } = options;
|
|
67
|
-
|
|
68
|
-
// Check if empty
|
|
69
|
-
if (!email.trim()) {
|
|
70
|
-
if (required && !allowEmpty) {
|
|
71
|
-
return { isValid: false, error: "Email is required" };
|
|
72
|
-
}
|
|
73
|
-
return { isValid: true };
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Email regex pattern
|
|
77
|
-
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
78
|
-
if (!emailPattern.test(email)) {
|
|
79
|
-
return { isValid: false, error: "Invalid email address" };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return { isValid: true };
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Validates a URL
|
|
87
|
-
*/
|
|
88
|
-
export const validateUrl = (url: string): ValidationResult => {
|
|
89
|
-
if (!url.trim()) {
|
|
90
|
-
return { isValid: true };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
try {
|
|
94
|
-
new URL(url);
|
|
95
|
-
return { isValid: true };
|
|
96
|
-
} catch {
|
|
97
|
-
return { isValid: false, error: "Invalid URL" };
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Validates a phone number (basic validation)
|
|
103
|
-
*/
|
|
104
|
-
export const validatePhoneNumber = (phone: string): ValidationResult => {
|
|
105
|
-
if (!phone.trim()) {
|
|
106
|
-
return { isValid: true };
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Remove all non-digit characters
|
|
110
|
-
const digitsOnly = phone.replace(/\D/g, "");
|
|
111
|
-
|
|
112
|
-
// Check if length is reasonable (10-15 digits)
|
|
113
|
-
if (digitsOnly.length < 10 || digitsOnly.length > 15) {
|
|
114
|
-
return { isValid: false, error: "Invalid phone number" };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return { isValid: true };
|
|
118
|
-
};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Settings Error Boundary
|
|
3
|
-
*
|
|
4
|
-
* Generic error boundary component for Settings domains.
|
|
5
|
-
* Catches JavaScript errors in child component tree and displays fallback UI.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import React, { Component, ReactNode } from 'react';
|
|
9
|
-
import { View, Text, StyleSheet } from 'react-native';
|
|
10
|
-
import { devError } from '../../../utils/devUtils';
|
|
11
|
-
|
|
12
|
-
export interface SettingsErrorBoundaryProps {
|
|
13
|
-
children: ReactNode;
|
|
14
|
-
domainName: string;
|
|
15
|
-
fallback?: ReactNode;
|
|
16
|
-
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface SettingsErrorBoundaryState {
|
|
20
|
-
hasError: boolean;
|
|
21
|
-
error: Error | null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Error Boundary for Settings domains
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```tsx
|
|
29
|
-
* <SettingsErrorBoundary domainName="Appearance">
|
|
30
|
-
* <AppearanceSection {...props} />
|
|
31
|
-
* </SettingsErrorBoundary>
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
export class SettingsErrorBoundary extends Component<
|
|
35
|
-
SettingsErrorBoundaryProps,
|
|
36
|
-
SettingsErrorBoundaryState
|
|
37
|
-
> {
|
|
38
|
-
constructor(props: SettingsErrorBoundaryProps) {
|
|
39
|
-
super(props);
|
|
40
|
-
this.state = { hasError: false, error: null };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
static getDerivedStateFromError(error: Error): SettingsErrorBoundaryState {
|
|
44
|
-
return { hasError: true, error };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
|
|
48
|
-
const { domainName, onError } = this.props;
|
|
49
|
-
|
|
50
|
-
// Log error in development
|
|
51
|
-
devError(`[${domainName}] Error caught by boundary:`, error, errorInfo);
|
|
52
|
-
|
|
53
|
-
// Call custom error handler if provided
|
|
54
|
-
onError?.(error, errorInfo);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
private handleReset = (): void => {
|
|
58
|
-
this.setState({ hasError: false, error: null });
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
render(): ReactNode {
|
|
62
|
-
const { hasError } = this.state;
|
|
63
|
-
const { children, fallback, domainName } = this.props;
|
|
64
|
-
|
|
65
|
-
if (hasError) {
|
|
66
|
-
// Use custom fallback if provided
|
|
67
|
-
if (fallback) {
|
|
68
|
-
return fallback;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Default fallback UI
|
|
72
|
-
return (
|
|
73
|
-
<View style={styles.container}>
|
|
74
|
-
<Text style={styles.title}>Something went wrong</Text>
|
|
75
|
-
<Text style={styles.message}>
|
|
76
|
-
{domainName} encountered an error. Please try again.
|
|
77
|
-
</Text>
|
|
78
|
-
</View>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return children;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const styles = StyleSheet.create({
|
|
87
|
-
container: {
|
|
88
|
-
flex: 1,
|
|
89
|
-
justifyContent: 'center',
|
|
90
|
-
alignItems: 'center',
|
|
91
|
-
padding: 16,
|
|
92
|
-
},
|
|
93
|
-
title: {
|
|
94
|
-
fontSize: 18,
|
|
95
|
-
fontWeight: '600',
|
|
96
|
-
marginBottom: 8,
|
|
97
|
-
textAlign: 'center',
|
|
98
|
-
},
|
|
99
|
-
message: {
|
|
100
|
-
fontSize: 14,
|
|
101
|
-
color: '#666',
|
|
102
|
-
textAlign: 'center',
|
|
103
|
-
marginBottom: 16,
|
|
104
|
-
},
|
|
105
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error Boundary exports
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { SettingsErrorBoundary } from './SettingsErrorBoundary';
|
|
6
|
-
export type {
|
|
7
|
-
SettingsErrorBoundaryProps,
|
|
8
|
-
SettingsErrorBoundaryState,
|
|
9
|
-
} from './SettingsErrorBoundary';
|
|
10
|
-
|
|
11
|
-
export { withErrorBoundary } from './withErrorBoundary';
|
|
12
|
-
export type { WithErrorBoundaryOptions } from './withErrorBoundary';
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* withErrorBoundary HOC
|
|
3
|
-
* Wraps screens with SettingsErrorBoundary for error handling
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from 'react';
|
|
7
|
-
import { SettingsErrorBoundary } from './SettingsErrorBoundary';
|
|
8
|
-
|
|
9
|
-
export interface WithErrorBoundaryOptions {
|
|
10
|
-
domainName: string;
|
|
11
|
-
fallback?: React.ReactNode;
|
|
12
|
-
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Higher-Order Component to wrap screens with ErrorBoundary
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```tsx
|
|
20
|
-
* export const AppearanceScreen = withErrorBoundary(
|
|
21
|
-
* AppearanceScreenComponent,
|
|
22
|
-
* { domainName: 'Appearance' }
|
|
23
|
-
* );
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export function withErrorBoundary<P extends object>(
|
|
27
|
-
Component: React.ComponentType<P>,
|
|
28
|
-
options: WithErrorBoundaryOptions
|
|
29
|
-
): React.FC<P> {
|
|
30
|
-
const WrappedComponent: React.FC<P> = (props) => {
|
|
31
|
-
return (
|
|
32
|
-
<SettingsErrorBoundary
|
|
33
|
-
domainName={options.domainName}
|
|
34
|
-
fallback={options.fallback}
|
|
35
|
-
onError={options.onError}
|
|
36
|
-
>
|
|
37
|
-
<Component {...props} />
|
|
38
|
-
</SettingsErrorBoundary>
|
|
39
|
-
);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name || 'Component'})`;
|
|
43
|
-
|
|
44
|
-
return WrappedComponent;
|
|
45
|
-
}
|
package/src/utils/hooks/index.ts
DELETED