@umituz/react-native-settings 4.23.86 → 4.23.87
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/feedback/presentation/components/FeedbackForm.tsx +10 -3
- package/src/domains/gamification/store/gamificationStore.ts +6 -7
- package/src/domains/localization/infrastructure/storage/LocalizationStore.ts +50 -181
- package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +182 -0
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +51 -45
- package/src/infrastructure/types/commonComponentTypes.ts +142 -0
- package/src/infrastructure/utils/async/core.ts +109 -0
- package/src/infrastructure/utils/async/debounceAndBatch.ts +69 -0
- package/src/infrastructure/utils/async/index.ts +8 -0
- package/src/infrastructure/utils/async/retryAndTimeout.ts +57 -0
- package/src/infrastructure/utils/configFactory.ts +101 -0
- package/src/infrastructure/utils/errorHandlers.ts +249 -0
- package/src/infrastructure/utils/index.ts +5 -0
- package/src/infrastructure/utils/memoUtils.ts +10 -2
- package/src/infrastructure/utils/styleTokens.ts +132 -0
- package/src/infrastructure/utils/validation/core.ts +42 -0
- package/src/infrastructure/utils/validation/formValidators.ts +82 -0
- package/src/infrastructure/utils/validation/index.ts +37 -0
- package/src/infrastructure/utils/validation/numericValidators.ts +66 -0
- package/src/infrastructure/utils/validation/passwordValidator.ts +53 -0
- package/src/infrastructure/utils/validation/textValidators.ts +118 -0
- package/src/presentation/hooks/useSettingsScreenConfig.ts +32 -79
- package/src/presentation/utils/config-creators/base-configs.ts +54 -42
- package/src/presentation/utils/faqTranslator.ts +31 -0
- package/src/presentation/utils/index.ts +6 -1
- package/src/presentation/utils/settingsConfigFactory.ts +89 -0
- package/src/presentation/utils/useAuthHandlers.ts +98 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common Component Types
|
|
3
|
+
* Shared interfaces for common component props to reduce duplication
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { StyleProp, ViewStyle } from "react-native";
|
|
7
|
+
import type { IconName } from "@umituz/react-native-design-system";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Base props for settings item components
|
|
11
|
+
*/
|
|
12
|
+
export interface BaseSettingsItemProps {
|
|
13
|
+
title: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
icon?: IconName;
|
|
16
|
+
onPress?: () => void;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
style?: StyleProp<ViewStyle>;
|
|
19
|
+
testID?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Base props for card components
|
|
24
|
+
*/
|
|
25
|
+
export interface BaseCardProps {
|
|
26
|
+
style?: StyleProp<ViewStyle>;
|
|
27
|
+
testID?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Base props for section components
|
|
32
|
+
*/
|
|
33
|
+
export interface BaseSectionProps {
|
|
34
|
+
title?: string;
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
testID?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Navigation item props
|
|
41
|
+
*/
|
|
42
|
+
export interface NavigationItemProps extends BaseSettingsItemProps {
|
|
43
|
+
route?: string;
|
|
44
|
+
showChevron?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Toggle item props (items with switches)
|
|
49
|
+
*/
|
|
50
|
+
export interface ToggleItemProps extends BaseSettingsItemProps {
|
|
51
|
+
showSwitch: true;
|
|
52
|
+
switchValue: boolean;
|
|
53
|
+
onSwitchChange: (value: boolean) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Pressable item props
|
|
58
|
+
*/
|
|
59
|
+
export interface PressableItemProps extends BaseSettingsItemProps {
|
|
60
|
+
onPress: () => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Icon styling props
|
|
65
|
+
*/
|
|
66
|
+
export interface IconStyleProps {
|
|
67
|
+
iconBgColor?: string;
|
|
68
|
+
iconColor?: string;
|
|
69
|
+
iconSize?: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Loading state props
|
|
74
|
+
*/
|
|
75
|
+
export interface LoadingStateProps {
|
|
76
|
+
loading?: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Badge props (for notification counts, etc.)
|
|
81
|
+
*/
|
|
82
|
+
export interface BadgeProps {
|
|
83
|
+
badge?: string | number;
|
|
84
|
+
badgeColor?: string;
|
|
85
|
+
badgeTextColor?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Screen header props
|
|
90
|
+
*/
|
|
91
|
+
export interface ScreenHeaderProps {
|
|
92
|
+
title: string;
|
|
93
|
+
subtitle?: string;
|
|
94
|
+
showBackButton?: boolean;
|
|
95
|
+
onBackPress?: () => void;
|
|
96
|
+
rightElement?: React.ReactNode;
|
|
97
|
+
style?: StyleProp<ViewStyle>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* List item props
|
|
102
|
+
*/
|
|
103
|
+
export interface ListItemProps extends BaseSettingsItemProps {
|
|
104
|
+
rightElement?: React.ReactNode;
|
|
105
|
+
leftElement?: React.ReactNode;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Modal props
|
|
110
|
+
*/
|
|
111
|
+
export interface BaseModalProps {
|
|
112
|
+
visible: boolean;
|
|
113
|
+
onClose: () => void;
|
|
114
|
+
title?: string;
|
|
115
|
+
testID?: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Form input props
|
|
120
|
+
*/
|
|
121
|
+
export interface BaseInputProps {
|
|
122
|
+
value: string;
|
|
123
|
+
onChangeText: (text: string) => void;
|
|
124
|
+
placeholder?: string;
|
|
125
|
+
error?: string;
|
|
126
|
+
disabled?: boolean;
|
|
127
|
+
style?: StyleProp<ViewStyle>;
|
|
128
|
+
testID?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Button props
|
|
133
|
+
*/
|
|
134
|
+
export interface BaseButtonProps {
|
|
135
|
+
title: string;
|
|
136
|
+
onPress: () => void;
|
|
137
|
+
disabled?: boolean;
|
|
138
|
+
loading?: boolean;
|
|
139
|
+
variant?: "primary" | "secondary" | "danger" | "ghost";
|
|
140
|
+
style?: StyleProp<ViewStyle>;
|
|
141
|
+
testID?: string;
|
|
142
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Async Operation Utilities
|
|
3
|
+
* Base types and handlers for async operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ValidationResult } from "../validation";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Result type for async operations
|
|
10
|
+
*/
|
|
11
|
+
export type AsyncResult<T, E = Error> =
|
|
12
|
+
| { success: true; data: T }
|
|
13
|
+
| { success: false; error: E };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Generic async handler with error handling
|
|
17
|
+
*/
|
|
18
|
+
export const handleAsyncOperation = async <T>(
|
|
19
|
+
operation: () => Promise<T>,
|
|
20
|
+
onError?: (error: Error) => void
|
|
21
|
+
): Promise<AsyncResult<T>> => {
|
|
22
|
+
try {
|
|
23
|
+
const data = await operation();
|
|
24
|
+
return { success: true, data };
|
|
25
|
+
} catch (error) {
|
|
26
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
27
|
+
if (onError) {
|
|
28
|
+
onError(err);
|
|
29
|
+
}
|
|
30
|
+
return { success: false, error: err };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Async operation with loading state
|
|
36
|
+
* FIXED: Properly handles errors in onSuccess callback
|
|
37
|
+
*/
|
|
38
|
+
export const createAsyncHandler = <T extends unknown[], R>(
|
|
39
|
+
handler: (...args: T) => Promise<R>,
|
|
40
|
+
options: {
|
|
41
|
+
onLoadingStart?: () => void;
|
|
42
|
+
onLoadingEnd?: () => void;
|
|
43
|
+
onError?: (error: Error) => void;
|
|
44
|
+
onSuccess?: (result: R) => void;
|
|
45
|
+
}
|
|
46
|
+
) => {
|
|
47
|
+
return async (...args: T): Promise<void> => {
|
|
48
|
+
const { onLoadingStart, onLoadingEnd, onError, onSuccess } = options;
|
|
49
|
+
|
|
50
|
+
let loadingStarted = false;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
onLoadingStart?.();
|
|
54
|
+
loadingStarted = true;
|
|
55
|
+
const result = await handler(...args);
|
|
56
|
+
// FIXED: Wrap onSuccess in try-catch to handle errors separately
|
|
57
|
+
try {
|
|
58
|
+
onSuccess?.(result);
|
|
59
|
+
} catch (callbackError) {
|
|
60
|
+
// Log callback error but don't treat it as handler error
|
|
61
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
62
|
+
console.error("[createAsyncHandler] onSuccess callback error:", callbackError);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
67
|
+
onError?.(err);
|
|
68
|
+
} finally {
|
|
69
|
+
// FIXED: Only call onLoadingEnd if it was started
|
|
70
|
+
if (loadingStarted) {
|
|
71
|
+
try {
|
|
72
|
+
onLoadingEnd?.();
|
|
73
|
+
} catch (callbackError) {
|
|
74
|
+
// Log callback error but don't throw
|
|
75
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
76
|
+
console.error("[createAsyncHandler] onLoadingEnd callback error:", callbackError);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Async operation with validation
|
|
86
|
+
*/
|
|
87
|
+
export const createValidatedAsyncHandler = <T, R>(
|
|
88
|
+
validator: (data: T) => ValidationResult,
|
|
89
|
+
handler: (data: T) => Promise<R>,
|
|
90
|
+
options: {
|
|
91
|
+
onValidationError?: (error: string) => void;
|
|
92
|
+
onError?: (error: Error) => void;
|
|
93
|
+
} = {}
|
|
94
|
+
) => {
|
|
95
|
+
return async (data: T): Promise<AsyncResult<R>> => {
|
|
96
|
+
const { onValidationError, onError } = options;
|
|
97
|
+
|
|
98
|
+
// Validate first
|
|
99
|
+
const validationResult = validator(data);
|
|
100
|
+
if (!validationResult.isValid) {
|
|
101
|
+
const error = new Error(validationResult.error || "Validation failed");
|
|
102
|
+
onValidationError?.(error.message);
|
|
103
|
+
return { success: false, error };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Execute handler
|
|
107
|
+
return handleAsyncOperation(() => handler(data), onError);
|
|
108
|
+
};
|
|
109
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debounce and Batch Utilities
|
|
3
|
+
* Utilities for debouncing and batching async operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AsyncResult } from "./core";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Debounced async operation
|
|
10
|
+
*/
|
|
11
|
+
export const createDebouncedAsyncOperation = <T extends unknown[], R>(
|
|
12
|
+
operation: (...args: T) => Promise<R>,
|
|
13
|
+
delayMs: number
|
|
14
|
+
): ((...args: T) => Promise<R>) => {
|
|
15
|
+
let timeoutId: NodeJS.Timeout | null = null;
|
|
16
|
+
|
|
17
|
+
return (...args: T): Promise<R> => {
|
|
18
|
+
if (timeoutId) {
|
|
19
|
+
clearTimeout(timeoutId);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
timeoutId = setTimeout(async () => {
|
|
24
|
+
try {
|
|
25
|
+
const result = await operation(...args);
|
|
26
|
+
resolve(result);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
reject(error);
|
|
29
|
+
}
|
|
30
|
+
}, delayMs);
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Batch async operations
|
|
37
|
+
*/
|
|
38
|
+
export const batchAsyncOperations = async <T, R>(
|
|
39
|
+
items: T[],
|
|
40
|
+
operation: (item: T) => Promise<R>,
|
|
41
|
+
options: {
|
|
42
|
+
concurrency?: number;
|
|
43
|
+
onProgress?: (completed: number, total: number) => void;
|
|
44
|
+
} = {}
|
|
45
|
+
): Promise<AsyncResult<R[]>> => {
|
|
46
|
+
const { concurrency = 5, onProgress } = options;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const results: R[] = [];
|
|
50
|
+
const batches: T[][] = [];
|
|
51
|
+
|
|
52
|
+
// Create batches
|
|
53
|
+
for (let i = 0; i < items.length; i += concurrency) {
|
|
54
|
+
batches.push(items.slice(i, i + concurrency));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Process batches
|
|
58
|
+
for (const batch of batches) {
|
|
59
|
+
const batchResults = await Promise.all(batch.map(operation));
|
|
60
|
+
results.push(...batchResults);
|
|
61
|
+
onProgress?.(results.length, items.length);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { success: true, data: results };
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
67
|
+
return { success: false, error: err };
|
|
68
|
+
}
|
|
69
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry and Timeout Utilities
|
|
3
|
+
* Utilities for handling retries and timeouts in async operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Retry utility for async operations
|
|
8
|
+
*/
|
|
9
|
+
export const retryAsyncOperation = async <T>(
|
|
10
|
+
operation: () => Promise<T>,
|
|
11
|
+
options: {
|
|
12
|
+
maxAttempts?: number;
|
|
13
|
+
delayMs?: number;
|
|
14
|
+
backoffMultiplier?: number;
|
|
15
|
+
onRetry?: (attempt: number, error: Error) => void;
|
|
16
|
+
} = {}
|
|
17
|
+
): Promise<T> => {
|
|
18
|
+
const {
|
|
19
|
+
maxAttempts = 3,
|
|
20
|
+
delayMs = 1000,
|
|
21
|
+
backoffMultiplier = 2,
|
|
22
|
+
onRetry,
|
|
23
|
+
} = options;
|
|
24
|
+
|
|
25
|
+
let lastError: Error | undefined;
|
|
26
|
+
|
|
27
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
28
|
+
try {
|
|
29
|
+
return await operation();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
32
|
+
|
|
33
|
+
if (attempt < maxAttempts) {
|
|
34
|
+
onRetry?.(attempt, lastError);
|
|
35
|
+
const delay = delayMs * Math.pow(backoffMultiplier, attempt - 1);
|
|
36
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
throw lastError || new Error("Operation failed after retries");
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Timeout wrapper for async operations
|
|
46
|
+
*/
|
|
47
|
+
export const withTimeout = async <T>(
|
|
48
|
+
operation: Promise<T>,
|
|
49
|
+
timeoutMs: number,
|
|
50
|
+
timeoutMessage: string = "Operation timed out"
|
|
51
|
+
): Promise<T> => {
|
|
52
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
53
|
+
setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return Promise.race([operation, timeoutPromise]);
|
|
57
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Factory
|
|
3
|
+
* Generic configuration creator to reduce duplication in base-configs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TranslationFunction } from "../../presentation/utils/config-creators/types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Feature visibility configuration
|
|
10
|
+
* - true: Always show (if navigation screen exists)
|
|
11
|
+
* - false: Never show
|
|
12
|
+
* - 'auto': Automatically detect (check if navigation screen exists and package is available)
|
|
13
|
+
*/
|
|
14
|
+
export type FeatureVisibility = boolean | "auto";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Base configuration type for all settings items
|
|
18
|
+
*/
|
|
19
|
+
export interface BaseConfigType {
|
|
20
|
+
enabled?: FeatureVisibility;
|
|
21
|
+
title?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
icon?: string;
|
|
24
|
+
route?: string;
|
|
25
|
+
onPress?: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Configuration parameters for creating a settings item config
|
|
30
|
+
*/
|
|
31
|
+
export interface ConfigCreatorParams {
|
|
32
|
+
t: TranslationFunction;
|
|
33
|
+
titleKey: string;
|
|
34
|
+
descriptionKey: string;
|
|
35
|
+
icon: string;
|
|
36
|
+
routeOrOnPress?: string | (() => void);
|
|
37
|
+
defaultRoute?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generic configuration creator function
|
|
42
|
+
* Reduces duplication across all config creators
|
|
43
|
+
*/
|
|
44
|
+
export const createBaseConfig = <T extends BaseConfigType = BaseConfigType>(
|
|
45
|
+
params: ConfigCreatorParams
|
|
46
|
+
): T => {
|
|
47
|
+
const { t, titleKey, descriptionKey, icon, routeOrOnPress, defaultRoute } = params;
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
enabled: true,
|
|
51
|
+
title: t(titleKey),
|
|
52
|
+
description: t(descriptionKey),
|
|
53
|
+
icon,
|
|
54
|
+
route: typeof routeOrOnPress === "string" ? routeOrOnPress : defaultRoute,
|
|
55
|
+
onPress: typeof routeOrOnPress === "function" ? routeOrOnPress : undefined,
|
|
56
|
+
} as T;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a configuration with extended properties
|
|
61
|
+
*/
|
|
62
|
+
export const createConfigWithExtensions = <T extends BaseConfigType>(
|
|
63
|
+
baseParams: ConfigCreatorParams,
|
|
64
|
+
extensions: Partial<Omit<T, keyof BaseConfigType>>
|
|
65
|
+
): T => {
|
|
66
|
+
const baseConfig = createBaseConfig<T>(baseParams);
|
|
67
|
+
return { ...baseConfig, ...extensions };
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a disabled configuration
|
|
72
|
+
*/
|
|
73
|
+
export const createDisabledConfig = <T extends BaseConfigType>(
|
|
74
|
+
params: Omit<ConfigCreatorParams, "routeOrOnPress" | "defaultRoute">
|
|
75
|
+
): T => {
|
|
76
|
+
const baseConfig = createBaseConfig<T>(params);
|
|
77
|
+
return { ...baseConfig, enabled: false } as T;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Batch create configurations
|
|
82
|
+
*/
|
|
83
|
+
export const createBatchConfigs = <T extends BaseConfigType>(
|
|
84
|
+
items: Array<{
|
|
85
|
+
titleKey: string;
|
|
86
|
+
descriptionKey: string;
|
|
87
|
+
icon: string;
|
|
88
|
+
routeOrOnPress?: string | (() => void);
|
|
89
|
+
}>,
|
|
90
|
+
t: TranslationFunction
|
|
91
|
+
): T[] => {
|
|
92
|
+
return items.map((item) =>
|
|
93
|
+
createBaseConfig<T>({
|
|
94
|
+
t,
|
|
95
|
+
titleKey: item.titleKey,
|
|
96
|
+
descriptionKey: item.descriptionKey,
|
|
97
|
+
icon: item.icon,
|
|
98
|
+
routeOrOnPress: item.routeOrOnPress,
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
};
|