@umituz/react-native-settings 5.4.8 → 5.4.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "5.4.8",
3
+ "version": "5.4.10",
4
4
  "description": "Complete settings hub for React Native apps - consolidated package with settings, localization, about, legal, appearance, feedback, FAQs, rating, and gamification - expo-store-review and expo-device now lazy loaded",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./dist/index.d.ts",
@@ -153,9 +153,6 @@
153
153
  "@react-native-async-storage/async-storage": "^2.2.0",
154
154
  "@react-native-community/datetimepicker": "^8.6.0",
155
155
  "@react-native-community/slider": "^5.1.1",
156
- "@react-navigation/bottom-tabs": "^7.9.0",
157
- "@react-navigation/native": "^7.1.26",
158
- "@react-navigation/stack": "^7.6.13",
159
156
  "@tanstack/query-async-storage-persister": "^5.66.7",
160
157
  "@tanstack/react-query": "^5.0.0",
161
158
  "@tanstack/react-query-persist-client": "^5.66.7",
@@ -0,0 +1,141 @@
1
+ /**
2
+ * BaseService
3
+ *
4
+ * Abstract base class for all domain services.
5
+ * Provides consistent error handling, logging, and result patterns.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * class MyService extends BaseService {
10
+ * protected serviceName = 'MyService';
11
+ *
12
+ * async doSomething(input: Input): Promise<Data> {
13
+ * return this.execute('doSomething', async () => {
14
+ * // Your logic here
15
+ * return result;
16
+ * });
17
+ * }
18
+ * }
19
+ * ```
20
+ */
21
+
22
+ import { isDev } from '../../utils/devUtils';
23
+ import { formatErrorMessage } from '../../utils/errorUtils';
24
+
25
+ export type Result<T> =
26
+ | { success: true; data: T }
27
+ | { success: false; error: string };
28
+
29
+ export type AsyncResult<T> = Promise<Result<T>>;
30
+
31
+ /**
32
+ * Abstract base service with error handling and logging
33
+ */
34
+ export abstract class BaseService {
35
+ /**
36
+ * Service name for logging (must be implemented by subclass)
37
+ */
38
+ protected abstract serviceName: string;
39
+
40
+ /**
41
+ * Execute an operation with automatic error handling and logging
42
+ *
43
+ * @param operation - Operation name for logging
44
+ * @param fn - Async function to execute
45
+ * @returns Result object with success flag
46
+ */
47
+ protected async execute<T>(
48
+ operation: string,
49
+ fn: () => Promise<T>
50
+ ): AsyncResult<T> {
51
+ try {
52
+ const data = await fn();
53
+ return { success: true, data };
54
+ } catch (error) {
55
+ this.logError(operation, error);
56
+ return { success: false, error: formatErrorMessage(error) };
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Execute without error handling (for critical operations that should crash)
62
+ *
63
+ * @param operation - Operation name for logging
64
+ * @param fn - Async function to execute
65
+ * @returns Direct result from function
66
+ */
67
+ protected async executeUnsafe<T>(
68
+ operation: string,
69
+ fn: () => Promise<T>
70
+ ): Promise<T> {
71
+ try {
72
+ return await fn();
73
+ } catch (error) {
74
+ this.logError(operation, error);
75
+ throw error; // Re-throw for caller to handle
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Execute a synchronous operation with error handling
81
+ *
82
+ * @param operation - Operation name for logging
83
+ * @param fn - Sync function to execute
84
+ * @returns Result object with success flag
85
+ */
86
+ protected executeSync<T>(
87
+ operation: string,
88
+ fn: () => T
89
+ ): Result<T> {
90
+ try {
91
+ const data = fn();
92
+ return { success: true, data };
93
+ } catch (error) {
94
+ this.logError(operation, error);
95
+ return { success: false, error: formatErrorMessage(error) };
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Log error in development mode
101
+ *
102
+ * @param operation - Operation name
103
+ * @param error - Error to log
104
+ */
105
+ protected logError(operation: string, error: unknown): void {
106
+ if (isDev()) {
107
+ console.error(`[${this.serviceName}] ${operation}:`, error);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Log info in development mode
113
+ *
114
+ * @param operation - Operation name
115
+ * @param message - Message to log
116
+ */
117
+ protected logInfo(operation: string, message: string): void {
118
+ if (isDev()) {
119
+ console.log(`[${this.serviceName}] ${operation}:`, message);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Log warning in development mode
125
+ *
126
+ * @param operation - Operation name
127
+ * @param message - Warning message
128
+ */
129
+ protected logWarning(operation: string, message: string): void {
130
+ if (isDev()) {
131
+ console.warn(`[${this.serviceName}] ${operation}:`, message);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Check if running in development mode
137
+ */
138
+ protected get isDev(): boolean {
139
+ return isDev();
140
+ }
141
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Core Layer - Public API
3
+ *
4
+ * Foundational utilities and base classes for the entire application.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { BaseService, logger, ModalPresets } from '@/core';
9
+ *
10
+ * class MyService extends BaseService {
11
+ * protected serviceName = 'MyService';
12
+ * }
13
+ * ```
14
+ */
15
+
16
+ // =============================================================================
17
+ // BASE CLASSES
18
+ // =============================================================================
19
+
20
+ export { BaseService } from './base/BaseService';
21
+ export type { Result, AsyncResult } from './base/BaseService';
22
+
23
+ // =============================================================================
24
+ // UTILITIES
25
+ // =============================================================================
26
+
27
+ export { logger, log, createLogger } from './utils/logger';
28
+ export type { Logger, LogContext } from './utils/logger';
29
+
30
+ export { validators, valid, invalid, validateAll } from './utils/validators';
31
+ export type { ValidationResult } from './utils/validators';
32
+
33
+ // =============================================================================
34
+ // PATTERNS - MODAL
35
+ // =============================================================================
36
+
37
+ export { useModalState, useModalStateWithResult } from './patterns/Modal/useModalState';
38
+ export type { ModalState, ModalConfig, ModalAction, ModalContent, ModalBehavior, ModalResult } from './patterns/Modal/ModalConfig';
39
+ export { ModalPresets, confirmed, dismissed } from './patterns/Modal/ModalConfig';
40
+
41
+ // =============================================================================
42
+ // PATTERNS - SCREEN
43
+ // =============================================================================
44
+
45
+ export { useScreenData, useSimpleScreenData } from './patterns/Screen/useScreenData';
46
+ export type {
47
+ ScreenData,
48
+ ScreenDataState,
49
+ ScreenDataActions,
50
+ ScreenFetchFunction,
51
+ ScreenConfig,
52
+ ScreenHeader,
53
+ ScreenLoading,
54
+ ScreenError,
55
+ ScreenEmpty,
56
+ ScreenLayout,
57
+ UseScreenDataOptions,
58
+ } from './patterns/Screen/ScreenConfig';
59
+
60
+ export { ScreenPresets } from './patterns/Screen/ScreenConfig';
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Modal Configuration Types
3
+ *
4
+ * Standardized configuration for all modal components.
5
+ * Ensures consistency across the application.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const config: ModalConfig = {
10
+ * title: 'Rating',
11
+ * content: 'Rate this app',
12
+ * actions: [
13
+ * { label: 'OK', onPress: () => {}, variant: 'primary' }
14
+ * ]
15
+ * };
16
+ * ```
17
+ */
18
+
19
+ import type { ReactNode } from 'react';
20
+
21
+ /**
22
+ * Modal action button configuration
23
+ */
24
+ export interface ModalAction {
25
+ /**
26
+ * Button label text
27
+ */
28
+ label: string;
29
+
30
+ /**
31
+ * Button press handler
32
+ */
33
+ onPress: () => void | Promise<void>;
34
+
35
+ /**
36
+ * Button variant
37
+ */
38
+ variant?: 'primary' | 'secondary' | 'outline' | 'text' | 'danger';
39
+
40
+ /**
41
+ * Disable button
42
+ */
43
+ disabled?: boolean;
44
+
45
+ /**
46
+ * Show loading indicator
47
+ */
48
+ loading?: boolean;
49
+
50
+ /**
51
+ * Test ID for E2E testing
52
+ */
53
+ testID?: string;
54
+ }
55
+
56
+ /**
57
+ * Modal content configuration
58
+ */
59
+ export interface ModalContent {
60
+ /**
61
+ * Modal title
62
+ */
63
+ title?: string;
64
+
65
+ /**
66
+ * Modal subtitle or description
67
+ */
68
+ subtitle?: string;
69
+
70
+ /**
71
+ * Main content (can be string or React component)
72
+ */
73
+ message?: string | ReactNode;
74
+
75
+ /**
76
+ * Custom icon name
77
+ */
78
+ icon?: string;
79
+
80
+ /**
81
+ * Icon color
82
+ */
83
+ iconColor?: string;
84
+
85
+ /**
86
+ * Custom header component (overrides title/subtitle)
87
+ */
88
+ header?: ReactNode;
89
+
90
+ /**
91
+ * Custom body component (overrides message)
92
+ */
93
+ body?: ReactNode;
94
+
95
+ /**
96
+ * Custom footer component (overrides actions)
97
+ */
98
+ footer?: ReactNode;
99
+ }
100
+
101
+ /**
102
+ * Modal behavior configuration
103
+ */
104
+ export interface ModalBehavior {
105
+ /**
106
+ * Close on backdrop press
107
+ */
108
+ closeOnBackdropPress?: boolean;
109
+
110
+ /**
111
+ * Close on back button press (Android)
112
+ */
113
+ closeOnBackPress?: boolean;
114
+
115
+ /**
116
+ * Animation type
117
+ */
118
+ animation?: 'none' | 'slide' | 'fade' | 'scale';
119
+
120
+ /**
121
+ * Dismissible flag
122
+ */
123
+ dismissible?: boolean;
124
+ }
125
+
126
+ /**
127
+ * Complete modal configuration
128
+ */
129
+ export interface ModalConfig extends ModalContent, ModalBehavior {
130
+ /**
131
+ * Modal actions (buttons)
132
+ */
133
+ actions?: ModalAction[];
134
+
135
+ /**
136
+ * Maximum width (for responsive design)
137
+ */
138
+ maxWidth?: number;
139
+
140
+ /**
141
+ * Test ID for E2E testing
142
+ */
143
+ testID?: string;
144
+
145
+ /**
146
+ * Custom container style
147
+ */
148
+ containerStyle?: object;
149
+
150
+ /**
151
+ * Custom content style
152
+ */
153
+ contentStyle?: object;
154
+ }
155
+
156
+ /**
157
+ * Modal state for useModalState hook
158
+ */
159
+ export interface ModalState {
160
+ /**
161
+ * Modal visibility
162
+ */
163
+ visible: boolean;
164
+
165
+ /**
166
+ * Current modal configuration
167
+ */
168
+ config: ModalConfig | null;
169
+
170
+ /**
171
+ * Show modal with configuration
172
+ */
173
+ show: (config: ModalConfig) => void;
174
+
175
+ /**
176
+ * Hide modal
177
+ */
178
+ hide: () => void;
179
+
180
+ /**
181
+ * Update modal configuration
182
+ */
183
+ update: (config: Partial<ModalConfig>) => void;
184
+ }
185
+
186
+ /**
187
+ * Modal result type for async operations
188
+ */
189
+ export type ModalResult<T = void> =
190
+ | { confirmed: true; data: T }
191
+ | { confirmed: false };
192
+
193
+ /**
194
+ * Create a confirmed modal result
195
+ */
196
+ export function confirmed<T>(data?: T): ModalResult<T> {
197
+ return { confirmed: true, data: data as T };
198
+ }
199
+
200
+ /**
201
+ * Create a dismissed modal result
202
+ */
203
+ export function dismissed(): ModalResult<never> {
204
+ return { confirmed: false };
205
+ }
206
+
207
+ /**
208
+ * Common preset configurations
209
+ */
210
+ export const ModalPresets: {
211
+ /**
212
+ * Alert modal (single OK button)
213
+ */
214
+ alert: (title: string, message: string, okText?: string) => ModalConfig;
215
+
216
+ /**
217
+ * Confirm modal (OK and Cancel buttons)
218
+ */
219
+ confirm: (
220
+ title: string,
221
+ message: string,
222
+ confirmText?: string,
223
+ cancelText?: string
224
+ ) => ModalConfig;
225
+
226
+ /**
227
+ * Info modal (just message with icon)
228
+ */
229
+ info: (title: string, message: string, icon?: string) => ModalConfig;
230
+
231
+ /**
232
+ * Error modal (error styling)
233
+ */
234
+ error: (title: string, message: string) => ModalConfig;
235
+
236
+ /**
237
+ * Loading modal (spinner)
238
+ */
239
+ loading: (message: string) => ModalConfig;
240
+ } = {
241
+ alert: (title, message, okText = 'OK') => ({
242
+ title,
243
+ message,
244
+ actions: [{ label: okText, onPress: () => {}, variant: 'primary' }],
245
+ dismissible: true,
246
+ }),
247
+
248
+ confirm: (title, message, confirmText = 'Confirm', cancelText = 'Cancel') => ({
249
+ title,
250
+ message,
251
+ actions: [
252
+ { label: cancelText, onPress: () => {}, variant: 'outline' },
253
+ { label: confirmText, onPress: () => {}, variant: 'primary' },
254
+ ],
255
+ dismissible: true,
256
+ }),
257
+
258
+ info: (title, message, icon = 'info') => ({
259
+ title,
260
+ message,
261
+ icon,
262
+ actions: [{ label: 'OK', onPress: () => {}, variant: 'primary' }],
263
+ dismissible: true,
264
+ }),
265
+
266
+ error: (title, message) => ({
267
+ title,
268
+ message,
269
+ icon: 'error',
270
+ iconColor: 'error',
271
+ actions: [{ label: 'OK', onPress: () => {}, variant: 'primary' }],
272
+ dismissible: true,
273
+ }),
274
+
275
+ loading: (message) => ({
276
+ message,
277
+ actions: [],
278
+ dismissible: false,
279
+ closeOnBackdropPress: false,
280
+ closeOnBackPress: false,
281
+ }),
282
+ };
@@ -0,0 +1,128 @@
1
+ /**
2
+ * useModalState Hook
3
+ *
4
+ * Generic modal state management hook.
5
+ * Replaces modal-specific state logic across domains.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const modal = useModalState();
10
+ *
11
+ * // Show modal
12
+ * modal.show({
13
+ * title: 'Confirm',
14
+ * message: 'Are you sure?',
15
+ * actions: [...]
16
+ * });
17
+ *
18
+ * // Hide modal
19
+ * modal.hide();
20
+ * ```
21
+ */
22
+
23
+ import { useCallback, useState } from 'react';
24
+ import type { ModalConfig, ModalState } from './ModalConfig';
25
+
26
+ /**
27
+ * Generic modal state management hook
28
+ */
29
+ export function useModalState(initialConfig: ModalConfig | null = null): ModalState {
30
+ const [config, setConfig] = useState<ModalConfig | null>(initialConfig);
31
+ const [visible, setVisible] = useState(false);
32
+
33
+ const show = useCallback((newConfig: ModalConfig) => {
34
+ setConfig(newConfig);
35
+ setVisible(true);
36
+ }, []);
37
+
38
+ const hide = useCallback(() => {
39
+ setVisible(false);
40
+ // Note: We keep config for animation purposes
41
+ // It will be reset when modal closes completely
42
+ }, []);
43
+
44
+ const update = useCallback((updates: Partial<ModalConfig>) => {
45
+ setConfig((prev) => {
46
+ if (!prev) return null;
47
+ return { ...prev, ...updates };
48
+ });
49
+ }, []);
50
+
51
+ return {
52
+ visible,
53
+ config,
54
+ show,
55
+ hide,
56
+ update,
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Extended modal state with result handling
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * const modal = useModalStateWithResult<string>();
66
+ *
67
+ * const result = await modal.showAsync({
68
+ * title: 'Input',
69
+ * actions: [
70
+ * { label: 'OK', onPress: (resolve) => resolve('data') }
71
+ * ]
72
+ * });
73
+ *
74
+ * if (result.confirmed) {
75
+ * console.log(result.data);
76
+ * }
77
+ * ```
78
+ */
79
+ export function useModalStateWithResult<T = void>() {
80
+ const [config, setConfig] = useState<ModalConfig | null>(null);
81
+ const [visible, setVisible] = useState(false);
82
+ const [resolver, setResolver] = useState<{
83
+ resolve: (result: import('./ModalConfig').ModalResult<T>) => void;
84
+ reject: (error: Error) => void;
85
+ } | null>(null);
86
+
87
+ const showAsync = useCallback(
88
+ (newConfig: ModalConfig): Promise<import('./ModalConfig').ModalResult<T>> => {
89
+ return new Promise((resolve, reject) => {
90
+ setResolver({ resolve, reject });
91
+ setConfig(newConfig);
92
+ setVisible(true);
93
+ });
94
+ },
95
+ []
96
+ );
97
+
98
+ const hide = useCallback(() => {
99
+ setVisible(false);
100
+ resolver?.resolve({ confirmed: false });
101
+ setResolver(null);
102
+ }, [resolver]);
103
+
104
+ const confirm = useCallback(
105
+ (data?: T) => {
106
+ setVisible(false);
107
+ resolver?.resolve({ confirmed: true, data: data as T });
108
+ setResolver(null);
109
+ },
110
+ [resolver]
111
+ );
112
+
113
+ const update = useCallback((updates: Partial<ModalConfig>) => {
114
+ setConfig((prev) => {
115
+ if (!prev) return null;
116
+ return { ...prev, ...updates };
117
+ });
118
+ }, []);
119
+
120
+ return {
121
+ visible,
122
+ config,
123
+ showAsync,
124
+ hide,
125
+ confirm,
126
+ update,
127
+ };
128
+ }