@umituz/react-native-settings 5.4.9 → 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 +1 -1
- package/src/core/base/BaseService.ts +141 -0
- package/src/core/index.ts +60 -0
- package/src/core/patterns/Modal/ModalConfig.ts +282 -0
- package/src/core/patterns/Modal/useModalState.ts +128 -0
- package/src/core/patterns/Screen/ScreenConfig.ts +375 -0
- package/src/core/patterns/Screen/useScreenData.ts +202 -0
- package/src/core/utils/logger.ts +138 -0
- package/src/core/utils/validators.ts +203 -0
- package/src/domains/disclaimer/index.ts +2 -2
- package/src/domains/disclaimer/presentation/hooks/useDisclaimerModal.ts +72 -0
- package/src/domains/feedback/index.ts +2 -1
- package/src/domains/feedback/presentation/hooks/useFeedbackModal.ts +182 -0
- package/src/domains/notifications/infrastructure/services/NotificationService.ts +16 -11
- package/src/domains/rating/application/services/RatingService.ts +89 -84
- package/src/domains/rating/index.ts +2 -2
- package/src/domains/rating/presentation/hooks/useRatingPromptModal.ts +122 -0
- package/src/index.ts +12 -0
- package/src/infrastructure/services/SettingsService.ts +33 -23
- package/src/presentation/components/GenericModal.tsx +212 -0
- package/src/presentation/components/GenericScreen.tsx +278 -0
- package/src/presentation/components/index.ts +27 -0
- package/src/domains/disclaimer/presentation/components/DisclaimerModal.tsx +0 -103
- package/src/domains/feedback/presentation/components/FeedbackModal.tsx +0 -99
- 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,202 @@
|
|
|
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
|
+
ScreenDataActions,
|
|
25
|
+
ScreenFetchFunction,
|
|
26
|
+
} from './ScreenConfig';
|
|
27
|
+
|
|
28
|
+
export interface UseScreenDataOptions<T> {
|
|
29
|
+
/**
|
|
30
|
+
* Fetch function to load data
|
|
31
|
+
*/
|
|
32
|
+
fetch: ScreenFetchFunction<T>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Auto-fetch on mount
|
|
36
|
+
*/
|
|
37
|
+
autoFetch?: boolean;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Initial data
|
|
41
|
+
*/
|
|
42
|
+
initialData?: T | null;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Dependencies for re-fetching
|
|
46
|
+
*/
|
|
47
|
+
deps?: unknown[];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Error handler
|
|
51
|
+
*/
|
|
52
|
+
onError?: (error: Error) => void;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Success handler
|
|
56
|
+
*/
|
|
57
|
+
onSuccess?: (data: T) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generic screen data management hook
|
|
62
|
+
*/
|
|
63
|
+
export function useScreenData<T>(options: UseScreenDataOptions<T>): ScreenData<T> {
|
|
64
|
+
const {
|
|
65
|
+
fetch,
|
|
66
|
+
autoFetch = true,
|
|
67
|
+
initialData = null,
|
|
68
|
+
deps = [],
|
|
69
|
+
onError,
|
|
70
|
+
onSuccess,
|
|
71
|
+
} = options;
|
|
72
|
+
|
|
73
|
+
const [state, setState] = useState<ScreenDataState<T>>({
|
|
74
|
+
loading: autoFetch,
|
|
75
|
+
error: null,
|
|
76
|
+
data: initialData,
|
|
77
|
+
initialized: false,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const isMountedRef = useRef(true);
|
|
81
|
+
const isFetchingRef = useRef(false);
|
|
82
|
+
|
|
83
|
+
// Update mounted ref
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
isMountedRef.current = true;
|
|
86
|
+
return () => {
|
|
87
|
+
isMountedRef.current = false;
|
|
88
|
+
};
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
// Reset function
|
|
92
|
+
const reset = useCallback(() => {
|
|
93
|
+
setState({
|
|
94
|
+
loading: false,
|
|
95
|
+
error: null,
|
|
96
|
+
data: null,
|
|
97
|
+
initialized: false,
|
|
98
|
+
});
|
|
99
|
+
}, []);
|
|
100
|
+
|
|
101
|
+
// Set loading function
|
|
102
|
+
const setLoading = useCallback((loading: boolean) => {
|
|
103
|
+
if (isMountedRef.current) {
|
|
104
|
+
setState((prev) => ({ ...prev, loading }));
|
|
105
|
+
}
|
|
106
|
+
}, []);
|
|
107
|
+
|
|
108
|
+
// Set error function
|
|
109
|
+
const setError = useCallback((error: string | null) => {
|
|
110
|
+
if (isMountedRef.current) {
|
|
111
|
+
setState((prev) => ({ ...prev, error, loading: false }));
|
|
112
|
+
}
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
115
|
+
// Set data function
|
|
116
|
+
const setData = useCallback((data: T | null) => {
|
|
117
|
+
if (isMountedRef.current) {
|
|
118
|
+
setState((prev) => ({ ...prev, data, loading: false, error: null, initialized: true }));
|
|
119
|
+
}
|
|
120
|
+
}, []);
|
|
121
|
+
|
|
122
|
+
// Refresh function
|
|
123
|
+
const refresh = useCallback(async () => {
|
|
124
|
+
// Prevent concurrent fetches
|
|
125
|
+
if (isFetchingRef.current) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
isFetchingRef.current = true;
|
|
130
|
+
setLoading(true);
|
|
131
|
+
setError(null);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const data = await fetch();
|
|
135
|
+
|
|
136
|
+
if (isMountedRef.current) {
|
|
137
|
+
setState((prev) => ({
|
|
138
|
+
...prev,
|
|
139
|
+
data,
|
|
140
|
+
loading: false,
|
|
141
|
+
error: null,
|
|
142
|
+
initialized: true,
|
|
143
|
+
}));
|
|
144
|
+
onSuccess?.(data);
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (isMountedRef.current) {
|
|
148
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
149
|
+
setState((prev) => ({
|
|
150
|
+
...prev,
|
|
151
|
+
error: errorMessage,
|
|
152
|
+
loading: false,
|
|
153
|
+
}));
|
|
154
|
+
onError?.(error as Error);
|
|
155
|
+
}
|
|
156
|
+
} finally {
|
|
157
|
+
isFetchingRef.current = false;
|
|
158
|
+
}
|
|
159
|
+
}, [fetch, setLoading, setError, onSuccess, onError]);
|
|
160
|
+
|
|
161
|
+
// Auto-fetch on mount and dependency changes
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (autoFetch && !state.initialized) {
|
|
164
|
+
refresh();
|
|
165
|
+
}
|
|
166
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
167
|
+
}, [autoFetch, state.initialized, ...deps]);
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
...state,
|
|
171
|
+
setLoading,
|
|
172
|
+
setError,
|
|
173
|
+
setData,
|
|
174
|
+
reset,
|
|
175
|
+
refresh,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Simple version for basic use cases
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* const { loading, error, data, refresh } = useSimpleScreenData(
|
|
185
|
+
* async () => await api.getData()
|
|
186
|
+
* );
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export function useSimpleScreenData<T>(
|
|
190
|
+
fetch: ScreenFetchFunction<T>,
|
|
191
|
+
autoFetch = true
|
|
192
|
+
): ScreenDataState<T> & { refresh: () => Promise<void> } {
|
|
193
|
+
const screenData = useScreenData({ fetch, autoFetch });
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
loading: screenData.loading,
|
|
197
|
+
error: screenData.error,
|
|
198
|
+
data: screenData.data,
|
|
199
|
+
initialized: screenData.initialized,
|
|
200
|
+
refresh: screenData.refresh,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -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
|
+
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
|
+
};
|