@umituz/react-native-settings 5.4.9 → 5.4.11

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 (31) hide show
  1. package/package.json +1 -1
  2. package/src/core/base/BaseService.ts +141 -0
  3. package/src/core/index.ts +60 -0
  4. package/src/core/patterns/Modal/ModalConfig.ts +282 -0
  5. package/src/core/patterns/Modal/useModalState.ts +128 -0
  6. package/src/core/patterns/Screen/ScreenConfig.ts +375 -0
  7. package/src/core/patterns/Screen/useScreenData.ts +201 -0
  8. package/src/core/utils/logger.ts +138 -0
  9. package/src/core/utils/validators.ts +203 -0
  10. package/src/domains/disclaimer/index.ts +0 -3
  11. package/src/domains/disclaimer/presentation/components/DisclaimerSetting.tsx +18 -43
  12. package/src/domains/disclaimer/presentation/screens/DisclaimerScreen.tsx +42 -92
  13. package/src/domains/feedback/index.ts +2 -1
  14. package/src/domains/feedback/presentation/components/SupportSection.tsx +16 -43
  15. package/src/domains/feedback/presentation/screens/FeatureRequestScreen.tsx +4 -4
  16. package/src/domains/feedback/presentation/screens/FeedbackScreen.tsx +75 -0
  17. package/src/domains/notifications/infrastructure/services/NotificationService.ts +16 -13
  18. package/src/domains/rating/application/services/RatingService.ts +115 -79
  19. package/src/domains/rating/index.ts +3 -3
  20. package/src/domains/rating/presentation/hooks/useAppRating.tsx +42 -65
  21. package/src/domains/rating/presentation/screens/RatingPromptScreen.tsx +162 -0
  22. package/src/index.ts +12 -0
  23. package/src/infrastructure/services/SettingsService.ts +23 -19
  24. package/src/presentation/components/GenericModal.tsx +208 -0
  25. package/src/presentation/components/GenericScreen.tsx +273 -0
  26. package/src/presentation/components/index.ts +27 -0
  27. package/src/presentation/navigation/hooks/useSettingsScreens.ts +26 -1
  28. package/src/presentation/navigation/types.ts +6 -0
  29. package/src/domains/disclaimer/presentation/components/DisclaimerModal.tsx +0 -103
  30. package/src/domains/feedback/presentation/components/FeedbackModal.tsx +0 -99
  31. package/src/domains/rating/presentation/components/RatingPromptModal.tsx +0 -152
@@ -0,0 +1,375 @@
1
+ /**
2
+ * Screen Configuration Types
3
+ *
4
+ * Standardized configuration for all screen components.
5
+ * Ensures consistency across the application.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const config: ScreenConfig = {
10
+ * title: 'Settings',
11
+ * loadingMessage: 'Loading settings...',
12
+ * errorMessage: 'Failed to load settings'
13
+ * };
14
+ * ```
15
+ */
16
+
17
+ import type { ReactNode } from 'react';
18
+
19
+ /**
20
+ * Screen header configuration
21
+ */
22
+ export interface ScreenHeader {
23
+ /**
24
+ * Header title
25
+ */
26
+ title?: string;
27
+
28
+ /**
29
+ * Header subtitle
30
+ */
31
+ subtitle?: string;
32
+
33
+ /**
34
+ * Show back button
35
+ */
36
+ showBackButton?: boolean;
37
+
38
+ /**
39
+ * Custom back button handler
40
+ */
41
+ onBackPress?: () => void;
42
+
43
+ /**
44
+ * Custom header component
45
+ */
46
+ custom?: ReactNode;
47
+
48
+ /**
49
+ * Right action buttons
50
+ */
51
+ actions?: Array<{
52
+ icon: string;
53
+ onPress: () => void;
54
+ testID?: string;
55
+ }>;
56
+
57
+ /**
58
+ * Test ID for E2E testing
59
+ */
60
+ testID?: string;
61
+ }
62
+
63
+ /**
64
+ * Screen loading state configuration
65
+ */
66
+ export interface ScreenLoading {
67
+ /**
68
+ * Loading spinner size
69
+ */
70
+ size?: 'sm' | 'md' | 'lg' | 'xl';
71
+
72
+ /**
73
+ * Loading message
74
+ */
75
+ message?: string;
76
+
77
+ /**
78
+ * Custom loading component
79
+ */
80
+ custom?: ReactNode;
81
+
82
+ /**
83
+ * Show full container spinner
84
+ */
85
+ fullContainer?: boolean;
86
+ }
87
+
88
+ /**
89
+ * Screen error state configuration
90
+ */
91
+ export interface ScreenError {
92
+ /**
93
+ * Error message prefix
94
+ */
95
+ prefix?: string;
96
+
97
+ /**
98
+ * Default error message (when error details unknown)
99
+ */
100
+ message?: string;
101
+
102
+ /**
103
+ * Retry button text
104
+ */
105
+ retryText?: string;
106
+
107
+ /**
108
+ * Show retry button
109
+ */
110
+ showRetry?: boolean;
111
+
112
+ /**
113
+ * Retry handler
114
+ */
115
+ onRetry?: () => void;
116
+
117
+ /**
118
+ * Custom error component
119
+ */
120
+ custom?: ReactNode;
121
+
122
+ /**
123
+ * Error icon name
124
+ */
125
+ icon?: string;
126
+ }
127
+
128
+ /**
129
+ * Screen empty state configuration
130
+ */
131
+ export interface ScreenEmpty {
132
+ /**
133
+ * Empty state message
134
+ */
135
+ message?: string;
136
+
137
+ /**
138
+ * Empty state description
139
+ */
140
+ description?: string;
141
+
142
+ /**
143
+ * Empty state icon
144
+ */
145
+ icon?: string;
146
+
147
+ /**
148
+ * Custom empty component
149
+ */
150
+ custom?: ReactNode;
151
+
152
+ /**
153
+ * Action button label
154
+ */
155
+ actionLabel?: string;
156
+
157
+ /**
158
+ * Action button handler
159
+ */
160
+ onAction?: () => void;
161
+ }
162
+
163
+ /**
164
+ * Screen layout configuration
165
+ */
166
+ export interface ScreenLayout {
167
+ /**
168
+ * Safe area edges
169
+ */
170
+ edges?: Array<'top' | 'bottom' | 'left' | 'right'>;
171
+
172
+ /**
173
+ * Scrollable content
174
+ */
175
+ scrollable?: boolean;
176
+
177
+ /**
178
+ * Keyboard avoiding view
179
+ */
180
+ keyboardAvoiding?: boolean;
181
+
182
+ /**
183
+ * Hide scroll indicator
184
+ */
185
+ hideScrollIndicator?: boolean;
186
+
187
+ /**
188
+ * Content container style
189
+ */
190
+ contentContainerStyle?: object;
191
+
192
+ /**
193
+ * Test ID for E2E testing
194
+ */
195
+ testID?: string;
196
+ }
197
+
198
+ /**
199
+ * Complete screen configuration
200
+ */
201
+ export interface ScreenConfig {
202
+ /**
203
+ * Screen header configuration
204
+ */
205
+ header?: ScreenHeader;
206
+
207
+ /**
208
+ * Loading state configuration
209
+ */
210
+ loading?: ScreenLoading;
211
+
212
+ /**
213
+ * Error state configuration
214
+ */
215
+ error?: ScreenError;
216
+
217
+ /**
218
+ * Empty state configuration
219
+ */
220
+ empty?: ScreenEmpty;
221
+
222
+ /**
223
+ * Layout configuration
224
+ */
225
+ layout?: ScreenLayout;
226
+
227
+ /**
228
+ * Custom container style
229
+ */
230
+ containerStyle?: object;
231
+
232
+ /**
233
+ * Screen test ID
234
+ */
235
+ testID?: string;
236
+ }
237
+
238
+ /**
239
+ * Screen data state (for useScreenData hook)
240
+ */
241
+ export interface ScreenDataState<T> {
242
+ /**
243
+ * Loading state
244
+ */
245
+ loading: boolean;
246
+
247
+ /**
248
+ * Error message
249
+ */
250
+ error: string | null;
251
+
252
+ /**
253
+ * Data
254
+ */
255
+ data: T | null;
256
+
257
+ /**
258
+ * Is initialized
259
+ */
260
+ initialized: boolean;
261
+ }
262
+
263
+ /**
264
+ * Screen data actions (for useScreenData hook)
265
+ */
266
+ export interface ScreenDataActions<T> {
267
+ /**
268
+ * Set loading state
269
+ */
270
+ setLoading: (loading: boolean) => void;
271
+
272
+ /**
273
+ * Set error state
274
+ */
275
+ setError: (error: string | null) => void;
276
+
277
+ /**
278
+ * Set data state
279
+ */
280
+ setData: (data: T | null) => void;
281
+
282
+ /**
283
+ * Reset state
284
+ */
285
+ reset: () => void;
286
+
287
+ /**
288
+ * Refresh data (calls fetch function)
289
+ */
290
+ refresh: () => Promise<void>;
291
+ }
292
+
293
+ /**
294
+ * Screen data hook return type
295
+ */
296
+ export type ScreenData<T> = ScreenDataState<T> & ScreenDataActions<T>;
297
+
298
+ /**
299
+ * Screen fetch function type
300
+ */
301
+ export type ScreenFetchFunction<T> = () => Promise<T>;
302
+
303
+ /**
304
+ * Common preset configurations
305
+ */
306
+ export const ScreenPresets: {
307
+ /**
308
+ * Default screen configuration
309
+ */
310
+ default: ScreenConfig;
311
+
312
+ /**
313
+ * Modal screen configuration (no back button, full edges)
314
+ */
315
+ modal: ScreenConfig;
316
+
317
+ /**
318
+ * Loading screen configuration
319
+ */
320
+ loading: (message?: string) => ScreenConfig;
321
+
322
+ /**
323
+ * Error screen configuration
324
+ */
325
+ error: (message?: string) => ScreenConfig;
326
+ } = {
327
+ default: {
328
+ header: {
329
+ showBackButton: true,
330
+ },
331
+ layout: {
332
+ edges: ['top', 'bottom', 'left', 'right'],
333
+ scrollable: true,
334
+ },
335
+ loading: {
336
+ size: 'lg',
337
+ fullContainer: true,
338
+ },
339
+ error: {
340
+ icon: 'error',
341
+ showRetry: true,
342
+ },
343
+ },
344
+
345
+ modal: {
346
+ header: {
347
+ showBackButton: false,
348
+ },
349
+ layout: {
350
+ edges: [],
351
+ scrollable: true,
352
+ keyboardAvoiding: true,
353
+ },
354
+ loading: {
355
+ size: 'md',
356
+ fullContainer: true,
357
+ },
358
+ },
359
+
360
+ loading: (message = 'Loading...') => ({
361
+ loading: {
362
+ size: 'lg',
363
+ message,
364
+ fullContainer: true,
365
+ },
366
+ }),
367
+
368
+ error: (message = 'Something went wrong') => ({
369
+ error: {
370
+ icon: 'error',
371
+ message,
372
+ showRetry: true,
373
+ },
374
+ }),
375
+ };
@@ -0,0 +1,201 @@
1
+ /**
2
+ * useScreenData Hook
3
+ *
4
+ * Generic screen data management hook.
5
+ * Handles loading, error, and data states for screens.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const screenData = useScreenData({
10
+ * fetch: async () => await api.getData(),
11
+ * autoFetch: true
12
+ * });
13
+ *
14
+ * if (screenData.loading) return <LoadingSpinner />;
15
+ * if (screenData.error) return <ErrorMessage error={screenData.error} />;
16
+ * return <DataView data={screenData.data} />;
17
+ * ```
18
+ */
19
+
20
+ import { useCallback, useEffect, useRef, useState } from 'react';
21
+ import type {
22
+ ScreenData,
23
+ ScreenDataState,
24
+ ScreenFetchFunction,
25
+ } from './ScreenConfig';
26
+
27
+ export interface UseScreenDataOptions<T> {
28
+ /**
29
+ * Fetch function to load data
30
+ */
31
+ fetch: ScreenFetchFunction<T>;
32
+
33
+ /**
34
+ * Auto-fetch on mount
35
+ */
36
+ autoFetch?: boolean;
37
+
38
+ /**
39
+ * Initial data
40
+ */
41
+ initialData?: T | null;
42
+
43
+ /**
44
+ * Dependencies for re-fetching
45
+ */
46
+ deps?: unknown[];
47
+
48
+ /**
49
+ * Error handler
50
+ */
51
+ onError?: (error: Error) => void;
52
+
53
+ /**
54
+ * Success handler
55
+ */
56
+ onSuccess?: (data: T) => void;
57
+ }
58
+
59
+ /**
60
+ * Generic screen data management hook
61
+ */
62
+ export function useScreenData<T>(options: UseScreenDataOptions<T>): ScreenData<T> {
63
+ const {
64
+ fetch,
65
+ autoFetch = true,
66
+ initialData = null,
67
+ deps = [],
68
+ onError,
69
+ onSuccess,
70
+ } = options;
71
+
72
+ const [state, setState] = useState<ScreenDataState<T>>({
73
+ loading: autoFetch,
74
+ error: null,
75
+ data: initialData,
76
+ initialized: false,
77
+ });
78
+
79
+ const isMountedRef = useRef(true);
80
+ const isFetchingRef = useRef(false);
81
+
82
+ // Update mounted ref
83
+ useEffect(() => {
84
+ isMountedRef.current = true;
85
+ return () => {
86
+ isMountedRef.current = false;
87
+ };
88
+ }, []);
89
+
90
+ // Reset function
91
+ const reset = useCallback(() => {
92
+ setState({
93
+ loading: false,
94
+ error: null,
95
+ data: null,
96
+ initialized: false,
97
+ });
98
+ }, []);
99
+
100
+ // Set loading function
101
+ const setLoading = useCallback((loading: boolean) => {
102
+ if (isMountedRef.current) {
103
+ setState((prev) => ({ ...prev, loading }));
104
+ }
105
+ }, []);
106
+
107
+ // Set error function
108
+ const setError = useCallback((error: string | null) => {
109
+ if (isMountedRef.current) {
110
+ setState((prev) => ({ ...prev, error, loading: false }));
111
+ }
112
+ }, []);
113
+
114
+ // Set data function
115
+ const setData = useCallback((data: T | null) => {
116
+ if (isMountedRef.current) {
117
+ setState((prev) => ({ ...prev, data, loading: false, error: null, initialized: true }));
118
+ }
119
+ }, []);
120
+
121
+ // Refresh function
122
+ const refresh = useCallback(async () => {
123
+ // Prevent concurrent fetches
124
+ if (isFetchingRef.current) {
125
+ return;
126
+ }
127
+
128
+ isFetchingRef.current = true;
129
+ setLoading(true);
130
+ setError(null);
131
+
132
+ try {
133
+ const data = await fetch();
134
+
135
+ if (isMountedRef.current) {
136
+ setState((prev) => ({
137
+ ...prev,
138
+ data,
139
+ loading: false,
140
+ error: null,
141
+ initialized: true,
142
+ }));
143
+ onSuccess?.(data);
144
+ }
145
+ } catch (error) {
146
+ if (isMountedRef.current) {
147
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
148
+ setState((prev) => ({
149
+ ...prev,
150
+ error: errorMessage,
151
+ loading: false,
152
+ }));
153
+ onError?.(error as Error);
154
+ }
155
+ } finally {
156
+ isFetchingRef.current = false;
157
+ }
158
+ }, [fetch, setLoading, setError, onSuccess, onError]);
159
+
160
+ // Auto-fetch on mount and dependency changes
161
+ useEffect(() => {
162
+ if (autoFetch && !state.initialized) {
163
+ refresh();
164
+ }
165
+ // eslint-disable-next-line react-hooks/exhaustive-deps
166
+ }, [autoFetch, state.initialized, ...deps]);
167
+
168
+ return {
169
+ ...state,
170
+ setLoading,
171
+ setError,
172
+ setData,
173
+ reset,
174
+ refresh,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Simple version for basic use cases
180
+ *
181
+ * @example
182
+ * ```ts
183
+ * const { loading, error, data, refresh } = useSimpleScreenData(
184
+ * async () => await api.getData()
185
+ * );
186
+ * ```
187
+ */
188
+ export function useSimpleScreenData<T>(
189
+ fetch: ScreenFetchFunction<T>,
190
+ autoFetch = true
191
+ ): ScreenDataState<T> & { refresh: () => Promise<void> } {
192
+ const screenData = useScreenData({ fetch, autoFetch });
193
+
194
+ return {
195
+ loading: screenData.loading,
196
+ error: screenData.error,
197
+ data: screenData.data,
198
+ initialized: screenData.initialized,
199
+ refresh: screenData.refresh,
200
+ };
201
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Logger Utility
3
+ *
4
+ * Centralized logging with development mode support.
5
+ * All logging should go through this utility for consistency.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { logger } from '@/core/utils/logger';
10
+ *
11
+ * logger.info('MyComponent', 'Component mounted');
12
+ * logger.error('MyService', 'Operation failed', error);
13
+ * ```
14
+ */
15
+
16
+ import { isDev } from '../../utils/devUtils';
17
+
18
+ export type LogLevel = 'info' | 'warn' | 'error' | 'debug';
19
+
20
+ export interface LogContext {
21
+ component?: string;
22
+ service?: string;
23
+ domain?: string;
24
+ operation?: string;
25
+ }
26
+
27
+ /**
28
+ * Centralized logger with development mode support
29
+ */
30
+ export class Logger {
31
+ private context: LogContext;
32
+
33
+ constructor(context: LogContext) {
34
+ this.context = context;
35
+ }
36
+
37
+ /**
38
+ * Log info message (only in development)
39
+ */
40
+ info(message: string, ...args: unknown[]): void {
41
+ if (isDev()) {
42
+ const prefix = this.formatPrefix('info');
43
+ console.log(prefix, message, ...args);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Log warning message (only in development)
49
+ */
50
+ warn(message: string, ...args: unknown[]): void {
51
+ if (isDev()) {
52
+ const prefix = this.formatPrefix('warn');
53
+ console.warn(prefix, message, ...args);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Log error message (only in development)
59
+ */
60
+ error(message: string, error?: unknown, ...args: unknown[]): void {
61
+ if (isDev()) {
62
+ const prefix = this.formatPrefix('error');
63
+ console.error(prefix, message, error, ...args);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Log debug message (only in development)
69
+ */
70
+ debug(message: string, ...args: unknown[]): void {
71
+ if (isDev()) {
72
+ const prefix = this.formatPrefix('debug');
73
+ console.log(prefix, message, ...args);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Create a child logger with additional context
79
+ */
80
+ child(additionalContext: Partial<LogContext>): Logger {
81
+ return new Logger({ ...this.context, ...additionalContext });
82
+ }
83
+
84
+ /**
85
+ * Format log prefix with context
86
+ */
87
+ private formatPrefix(level: LogLevel): string {
88
+ const parts = [`[${level}]`];
89
+
90
+ if (this.context.domain) {
91
+ parts.push(this.context.domain);
92
+ }
93
+
94
+ if (this.context.service) {
95
+ parts.push(this.context.service);
96
+ }
97
+
98
+ if (this.context.component) {
99
+ parts.push(this.context.component);
100
+ }
101
+
102
+ if (this.context.operation) {
103
+ parts.push(`#${this.context.operation}`);
104
+ }
105
+
106
+ return parts.join(' | ');
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Create a logger instance with context
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * const logger = createLogger({ component: 'MyComponent' });
116
+ * logger.info('Something happened');
117
+ * // Output: [INFO] | MyComponent: Something happened
118
+ * ```
119
+ */
120
+ export function createLogger(context: LogContext): Logger {
121
+ return new Logger(context);
122
+ }
123
+
124
+ /**
125
+ * Default logger instance for quick usage
126
+ */
127
+ export const logger = new Logger({});
128
+
129
+ /**
130
+ * Convenience functions for quick logging
131
+ */
132
+ export const log = {
133
+ info: (message: string, ...args: unknown[]) => logger.info(message, ...args),
134
+ warn: (message: string, ...args: unknown[]) => logger.warn(message, ...args),
135
+ error: (message: string, error?: unknown, ...args: unknown[]) =>
136
+ logger.error(message, error, ...args),
137
+ debug: (message: string, ...args: unknown[]) => logger.debug(message, ...args),
138
+ };