@umituz/react-native-settings 4.23.85 → 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.
Files changed (38) hide show
  1. package/package.json +3 -3
  2. package/src/domains/about/presentation/hooks/useAboutInfo.ts +1 -1
  3. package/src/domains/faqs/presentation/screens/FAQScreen.tsx +1 -1
  4. package/src/domains/feedback/presentation/components/FeedbackForm.styles.ts +1 -1
  5. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +11 -4
  6. package/src/domains/gamification/components/GamificationScreen/GamificationScreen.tsx +1 -6
  7. package/src/domains/gamification/store/gamificationStore.ts +6 -7
  8. package/src/domains/localization/infrastructure/storage/LocalizationStore.ts +50 -181
  9. package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +182 -0
  10. package/src/domains/notifications/infrastructure/utils/dev.ts +3 -3
  11. package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +1 -1
  12. package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +52 -46
  13. package/src/infrastructure/types/commonComponentTypes.ts +142 -0
  14. package/src/infrastructure/utils/async/core.ts +109 -0
  15. package/src/infrastructure/utils/async/debounceAndBatch.ts +69 -0
  16. package/src/infrastructure/utils/async/index.ts +8 -0
  17. package/src/infrastructure/utils/async/retryAndTimeout.ts +57 -0
  18. package/src/infrastructure/utils/configFactory.ts +101 -0
  19. package/src/infrastructure/utils/errorHandlers.ts +249 -0
  20. package/src/infrastructure/utils/index.ts +5 -0
  21. package/src/infrastructure/utils/memoComparisonUtils.ts +0 -2
  22. package/src/infrastructure/utils/memoUtils.ts +10 -2
  23. package/src/infrastructure/utils/styleTokens.ts +132 -0
  24. package/src/infrastructure/utils/validation/core.ts +42 -0
  25. package/src/infrastructure/utils/validation/formValidators.ts +82 -0
  26. package/src/infrastructure/utils/validation/index.ts +37 -0
  27. package/src/infrastructure/utils/validation/numericValidators.ts +66 -0
  28. package/src/infrastructure/utils/validation/passwordValidator.ts +53 -0
  29. package/src/infrastructure/utils/validation/textValidators.ts +118 -0
  30. package/src/presentation/hooks/useSettingsScreenConfig.ts +32 -79
  31. package/src/presentation/navigation/SettingsStackNavigator.tsx +1 -24
  32. package/src/presentation/navigation/hooks/useSettingsScreens.ts +1 -1
  33. package/src/presentation/utils/config-creators/base-configs.ts +54 -42
  34. package/src/presentation/utils/faqTranslator.ts +31 -0
  35. package/src/presentation/utils/index.ts +6 -1
  36. package/src/presentation/utils/screenFactory.ts +1 -1
  37. package/src/presentation/utils/settingsConfigFactory.ts +89 -0
  38. package/src/presentation/utils/useAuthHandlers.ts +98 -0
@@ -4,22 +4,20 @@
4
4
  */
5
5
 
6
6
  import React, { useState, useMemo, useCallback } from 'react';
7
- import { View, TextInput, StyleSheet, ScrollView } from 'react-native';
7
+ import { View, TextInput, ScrollView } from 'react-native';
8
8
  import { AtomicText } from '@umituz/react-native-design-system';
9
9
  import { useAppDesignTokens } from '@umituz/react-native-design-system';
10
10
  import { TimePresetSelector } from './TimePresetSelector';
11
11
  import { FrequencySelector } from './FrequencySelector';
12
12
  import { WeekdaySelector } from './WeekdaySelector';
13
13
  import { FormButton } from './FormButton';
14
- import {
15
- DEFAULT_HOUR,
16
- DEFAULT_MINUTE,
17
- DEFAULT_WEEKDAY,
18
- MAX_TITLE_LENGTH,
19
- MAX_BODY_LENGTH,
20
- VALID_HOUR_RANGE,
21
- VALID_MINUTE_RANGE,
22
- VALID_WEEKDAY_RANGE,
14
+ import { validateReminderForm } from '../../../../../infrastructure/utils/validation';
15
+ import {
16
+ DEFAULT_HOUR,
17
+ DEFAULT_MINUTE,
18
+ DEFAULT_WEEKDAY,
19
+ MAX_TITLE_LENGTH,
20
+ MAX_BODY_LENGTH,
23
21
  type ReminderFormProps,
24
22
  } from './ReminderForm.constants';
25
23
  import { createReminderFormStyles as createStyles } from './ReminderForm.styles';
@@ -44,25 +42,16 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
44
42
  const [minute, setMinute] = useState(initialData?.minute ?? DEFAULT_MINUTE);
45
43
  const [weekday, setWeekday] = useState(initialData?.weekday ?? DEFAULT_WEEKDAY);
46
44
  const [isCustomTime, setIsCustomTime] = useState(!initialData?.timePresetId);
47
-
48
- // Validation helper functions
49
- const isValidHour = useCallback((h: number): boolean => {
50
- return h >= VALID_HOUR_RANGE.min && h <= VALID_HOUR_RANGE.max;
51
- }, []);
52
-
53
- const isValidMinute = useCallback((m: number): boolean => {
54
- return m >= VALID_MINUTE_RANGE.min && m <= VALID_MINUTE_RANGE.max;
55
- }, []);
56
-
57
- const isValidWeekday = useCallback((w: number): boolean => {
58
- return w >= VALID_WEEKDAY_RANGE.min && w <= VALID_WEEKDAY_RANGE.max;
59
- }, []);
45
+ // FIXED: Add error state for user feedback
46
+ const [error, setError] = useState<string | null>(null);
60
47
 
61
48
  const handlePresetSelect = useCallback((preset: TimePreset) => {
62
49
  setSelectedPresetId(preset.id);
63
50
  setHour(preset.hour);
64
51
  setMinute(preset.minute);
65
52
  setIsCustomTime(false);
53
+ // Clear error when user changes something
54
+ setError(null);
66
55
  }, []);
67
56
 
68
57
  const handleCustomSelect = useCallback(() => {
@@ -74,29 +63,26 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
74
63
  const trimmedTitle = title.trim();
75
64
  const trimmedBody = body.trim();
76
65
 
77
- // Validate title
78
- if (!trimmedTitle) {
79
- return;
80
- }
81
-
82
- if (trimmedTitle.length > MAX_TITLE_LENGTH) {
83
- return;
84
- }
85
-
86
- // Validate body length
87
- if (trimmedBody.length > MAX_BODY_LENGTH) {
88
- return;
89
- }
66
+ // Validate using centralized validation
67
+ const validationResult = validateReminderForm({
68
+ title: trimmedTitle,
69
+ body: trimmedBody,
70
+ frequency,
71
+ hour,
72
+ minute,
73
+ weekday,
74
+ maxTitleLength: MAX_TITLE_LENGTH,
75
+ maxBodyLength: MAX_BODY_LENGTH,
76
+ });
90
77
 
91
- // Validate time values
92
- if (!isValidHour(hour) || !isValidMinute(minute)) {
78
+ if (!validationResult.isValid) {
79
+ // FIXED: Show error to user
80
+ setError(validationResult.error || "Validation failed");
93
81
  return;
94
82
  }
95
83
 
96
- // Validate weekday if frequency is weekly
97
- if (frequency === 'weekly' && !isValidWeekday(weekday)) {
98
- return;
99
- }
84
+ // Clear error and proceed
85
+ setError(null);
100
86
 
101
87
  // Sanitize input (React Native handles XSS, but we trim extra whitespace)
102
88
  const sanitizedTitle = trimmedTitle.replace(/\s+/g, ' ').trim();
@@ -112,7 +98,7 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
112
98
  weekday: frequency === 'weekly' ? weekday : undefined,
113
99
  dayOfMonth: frequency === 'monthly' ? 1 : undefined,
114
100
  });
115
- }, [title, body, frequency, selectedPresetId, hour, minute, weekday, isCustomTime, onSave, isValidHour, isValidMinute, isValidWeekday]);
101
+ }, [title, body, frequency, selectedPresetId, hour, minute, weekday, isCustomTime, onSave]);
116
102
 
117
103
  return (
118
104
  <ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
@@ -121,7 +107,10 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
121
107
  <TextInput
122
108
  style={styles.input}
123
109
  value={title}
124
- onChangeText={setTitle}
110
+ onChangeText={(text) => {
111
+ setTitle(text);
112
+ setError(null); // Clear error on input
113
+ }}
125
114
  placeholder={translations.titlePlaceholder}
126
115
  placeholderTextColor={tokens.colors.textSecondary}
127
116
  />
@@ -132,7 +121,10 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
132
121
  <TextInput
133
122
  style={[styles.input, styles.multilineInput]}
134
123
  value={body}
135
- onChangeText={setBody}
124
+ onChangeText={(text) => {
125
+ setBody(text);
126
+ setError(null); // Clear error on input
127
+ }}
136
128
  placeholder={translations.bodyPlaceholder}
137
129
  placeholderTextColor={tokens.colors.textSecondary}
138
130
  multiline
@@ -171,9 +163,23 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
171
163
  </View>
172
164
  )}
173
165
 
166
+ {/* FIXED: Show error message to user */}
167
+ {error && (
168
+ <View style={styles.section}>
169
+ <AtomicText type="bodySmall" color="error">
170
+ {error}
171
+ </AtomicText>
172
+ </View>
173
+ )}
174
+
174
175
  <View style={styles.buttonRow}>
175
176
  <FormButton label={translations.cancelButton} onPress={onCancel} variant="secondary" />
176
- <FormButton label={translations.saveButton} onPress={handleSave} disabled={!title.trim()} />
177
+ {/* FIXED: Disable button when form is invalid */}
178
+ <FormButton
179
+ label={translations.saveButton}
180
+ onPress={handleSave}
181
+ disabled={!title.trim() || !frequency}
182
+ />
177
183
  </View>
178
184
  </ScrollView>
179
185
  );
@@ -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,8 @@
1
+ /**
2
+ * Async Utilities
3
+ * Barrel export for all async utility modules
4
+ */
5
+
6
+ export * from "./core";
7
+ export * from "./retryAndTimeout";
8
+ export * from "./debounceAndBatch";
@@ -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
+ };