@umituz/react-native-design-system 4.23.67 → 4.23.69

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 (68) hide show
  1. package/package.json +1 -1
  2. package/src/atoms/EmptyState.tsx +2 -2
  3. package/src/atoms/icon/AtomicIcon.tsx +41 -112
  4. package/src/atoms/icon/components/iconRenderer.tsx +118 -0
  5. package/src/atoms/icon/utils/iconUtils.ts +94 -0
  6. package/src/exception/infrastructure/services/ExceptionService.ts +29 -17
  7. package/src/exception/presentation/components/ExceptionEmptyState.tsx +1 -1
  8. package/src/exception/presentation/components/ExceptionErrorState.tsx +1 -1
  9. package/src/image/infrastructure/services/ImageBatchService.ts +1 -7
  10. package/src/image/infrastructure/types/BatchTypes.ts +11 -0
  11. package/src/image/infrastructure/utils/BatchProcessor.ts +1 -1
  12. package/src/image/infrastructure/utils/ImageErrorHandler.ts +1 -0
  13. package/src/infinite-scroll/presentation/components/infinite-scroll-list.tsx +2 -2
  14. package/src/layouts/ScreenHeader/ScreenHeader.tsx +3 -3
  15. package/src/media/presentation/hooks/useCardMediaGeneration.ts +4 -4
  16. package/src/media/presentation/hooks/useCardMediaUpload.ts +4 -4
  17. package/src/media/presentation/hooks/useCardMediaValidation.ts +4 -4
  18. package/src/media/presentation/hooks/useCardMultimediaFlashcard.ts +5 -5
  19. package/src/media/presentation/hooks/useMediaGeneration.ts +4 -4
  20. package/src/media/presentation/hooks/useMediaUpload.ts +4 -4
  21. package/src/media/presentation/hooks/useMediaValidation.ts +4 -4
  22. package/src/media/presentation/hooks/useMultimediaFlashcard.ts +5 -5
  23. package/src/molecules/BaseModal.tsx +1 -1
  24. package/src/molecules/ConfirmationModalContent.tsx +2 -2
  25. package/src/molecules/ConfirmationModalMain.tsx +2 -2
  26. package/src/molecules/alerts/AlertToast.tsx +163 -192
  27. package/src/molecules/alerts/utils/alertToastHelpers.ts +70 -0
  28. package/src/molecules/bottom-sheet/components/filter/FilterBottomSheet.tsx +2 -2
  29. package/src/molecules/calendar/presentation/components/AtomicCalendar.tsx +1 -1
  30. package/src/molecules/calendar/presentation/components/CalendarDayCell.tsx +2 -1
  31. package/src/molecules/calendar/presentation/components/CalendarWeekdayHeader.tsx +1 -1
  32. package/src/molecules/confirmation-modal/useConfirmationModal.ts +6 -6
  33. package/src/molecules/countdown/components/Countdown.tsx +2 -2
  34. package/src/molecules/splash/components/SplashScreen.tsx +9 -23
  35. package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +1 -1
  36. package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +2 -2
  37. package/src/organisms/FormContainer.tsx +2 -2
  38. package/src/responsive/validation.ts +1 -0
  39. package/src/services/api/ApiClient.ts +242 -0
  40. package/src/services/api/index.ts +9 -0
  41. package/src/services/api/types/ApiTypes.ts +50 -0
  42. package/src/services/api/utils/requestBuilder.ts +92 -0
  43. package/src/services/api/utils/responseHandler.ts +130 -0
  44. package/src/storage/cache/domain/ErrorHandler.ts +1 -0
  45. package/src/storage/domain/errors/StorageError.ts +6 -0
  46. package/src/storage/infrastructure/repositories/AsyncStorageRepository.ts +31 -16
  47. package/src/tanstack/domain/repositories/BaseRepository.ts +16 -72
  48. package/src/tanstack/domain/repositories/IBaseRepository.ts +34 -0
  49. package/src/tanstack/domain/repositories/helpers/repositoryHelpers.ts +58 -0
  50. package/src/tanstack/domain/repositories/mixins/repositoryInvalidationMethods.ts +101 -0
  51. package/src/tanstack/domain/repositories/mixins/repositoryQueryMethods.ts +102 -0
  52. package/src/tanstack/infrastructure/providers/TanstackProvider.tsx +3 -3
  53. package/src/tanstack/presentation/hooks/types/prefetchTypes.ts +33 -0
  54. package/src/tanstack/presentation/hooks/usePrefetch.ts +8 -28
  55. package/src/tanstack/presentation/hooks/utils/prefetchLogger.ts +27 -0
  56. package/src/theme/index.ts +0 -3
  57. package/src/theme/infrastructure/providers/DesignSystemProvider.tsx +15 -4
  58. package/src/utils/colorMapper.ts +193 -0
  59. package/src/utils/formatHelper.ts +16 -0
  60. package/src/utils/formatters/dateFormatter.ts +64 -0
  61. package/src/utils/formatters/numberFormatter.ts +130 -0
  62. package/src/utils/formatters/stringFormatter.ts +190 -0
  63. package/src/utils/index.ts +15 -0
  64. package/src/utils/styleComposer.ts +94 -0
  65. package/src/utils/validationHelper.ts +16 -0
  66. package/src/utils/validators/dataValidators.ts +111 -0
  67. package/src/utils/validators/numericValidators.ts +106 -0
  68. package/src/utils/validators/stringValidators.ts +85 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "4.23.67",
3
+ "version": "4.23.69",
4
4
  "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -7,7 +7,7 @@
7
7
  * Purpose: Empty state indication across all apps
8
8
  */
9
9
 
10
- import React from 'react';
10
+ import React, { useState, useEffect, useCallback, useMemo, useRef, useContext } from 'react';
11
11
  import { View, StyleSheet, TouchableOpacity, ViewStyle } from 'react-native';
12
12
  import { AtomicIcon } from './icon';
13
13
  import { AtomicText } from './AtomicText';
@@ -39,7 +39,7 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
39
39
  const tokens = useAppDesignTokens();
40
40
  const displayDescription = description || subtitle;
41
41
 
42
- const themedStyles = React.useMemo(
42
+ const themedStyles = useMemo(
43
43
  () =>
44
44
  StyleSheet.create({
45
45
  container: {
@@ -20,19 +20,27 @@
20
20
  * <AtomicIcon name="favorite" size="md" color="primary" />
21
21
  */
22
22
 
23
- import React from "react";
24
- import { View, StyleSheet, StyleProp, ViewStyle } from "react-native";
25
- import Svg, { Path } from "react-native-svg";
26
- import { useAppDesignTokens } from "../../theme";
27
- import { useIconRenderer, type IconRenderProps } from "./iconStore";
23
+ import React from 'react';
24
+ import { StyleProp, ViewStyle } from 'react-native';
25
+ import { useAppDesignTokens } from '../../theme/hooks/useAppDesignTokens';
26
+ import { useIconRenderer } from './iconStore';
28
27
  import {
29
28
  type IconSize as BaseIconSize,
30
29
  type IconColor,
31
- } from "./AtomicIcon.types";
30
+ } from './AtomicIcon.types';
31
+ import {
32
+ calculateIconSize,
33
+ calculateIconColor,
34
+ } from './utils/iconUtils';
35
+ import {
36
+ renderSvgIcon,
37
+ renderWithBackground,
38
+ buildIconRenderProps,
39
+ } from './components/iconRenderer';
32
40
 
33
41
  export type IconSize = BaseIconSize;
34
42
  export type IconName = string;
35
- export type { IconColor, IconRenderProps };
43
+ export type { IconColor };
36
44
 
37
45
  export interface AtomicIconProps {
38
46
  /** Icon name - interpreted by the app's icon renderer */
@@ -61,44 +69,20 @@ export interface AtomicIconProps {
61
69
  style?: StyleProp<ViewStyle>;
62
70
  }
63
71
 
64
- const getSemanticColor = (
65
- color: IconColor,
66
- tokens: ReturnType<typeof useAppDesignTokens>
67
- ): string => {
68
- const colorMap: Record<IconColor, string> = {
69
- primary: tokens.colors.primary,
70
- secondary: tokens.colors.secondary,
71
- success: tokens.colors.success,
72
- warning: tokens.colors.warning,
73
- error: tokens.colors.error,
74
- info: tokens.colors.info,
75
- onSurface: tokens.colors.onSurface,
76
- surfaceVariant: tokens.colors.surfaceVariant,
77
- onPrimary: tokens.colors.onPrimary,
78
- onSecondary: tokens.colors.onSecondary,
79
- textInverse: tokens.colors.textInverse,
80
- textPrimary: tokens.colors.textPrimary,
81
- textSecondary: tokens.colors.textSecondary,
82
- textTertiary: tokens.colors.textTertiary,
83
- onSurfaceVariant: tokens.colors.onSurfaceVariant,
84
- };
85
- return colorMap[color];
86
- };
87
-
88
72
  /**
89
73
  * Agnostic icon component - requires iconRenderer in DesignSystemProvider
90
74
  */
91
75
  export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(
92
76
  ({
93
77
  name,
94
- size = "md",
78
+ size = 'md',
95
79
  customSize,
96
80
  color,
97
81
  customColor,
98
82
  withBackground = false,
99
83
  backgroundColor,
100
84
  svgPath,
101
- svgViewBox = "0 0 24 24",
85
+ svgViewBox = '0 0 24 24',
102
86
  accessibilityLabel,
103
87
  testID,
104
88
  style,
@@ -106,34 +90,19 @@ export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(
106
90
  const tokens = useAppDesignTokens();
107
91
  const iconRenderer = useIconRenderer();
108
92
 
109
- // Calculate size
110
- const baseSize = customSize ?? size;
111
- const iconSizesMap = tokens.iconSizes as Record<string, number>;
112
- const sizeInPixels: number =
113
- typeof baseSize === "number"
114
- ? baseSize * tokens.spacingMultiplier
115
- : iconSizesMap[baseSize] ?? iconSizesMap["md"] ?? 24;
116
-
117
- // Calculate color
118
- const iconColor = customColor
119
- ? customColor
120
- : color
121
- ? getSemanticColor(color, tokens)
122
- : tokens.colors.textPrimary;
93
+ // Calculate size and color using utility functions
94
+ const sizeInPixels = calculateIconSize(size, customSize, tokens);
95
+ const iconColor = calculateIconColor(customColor, color, tokens);
123
96
 
124
97
  // SVG path rendering (built-in, no external dependency)
125
98
  if (svgPath) {
126
- const svgElement = (
127
- <Svg
128
- viewBox={svgViewBox}
129
- width={sizeInPixels}
130
- height={sizeInPixels}
131
- key="custom-svg-icon"
132
- testID={testID}
133
- accessibilityLabel={accessibilityLabel}
134
- >
135
- <Path d={svgPath} fill={iconColor} />
136
- </Svg>
99
+ const svgElement = renderSvgIcon(
100
+ svgPath,
101
+ svgViewBox,
102
+ sizeInPixels,
103
+ iconColor,
104
+ testID,
105
+ accessibilityLabel
137
106
  );
138
107
 
139
108
  if (withBackground) {
@@ -154,27 +123,27 @@ export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(
154
123
  if (!iconRenderer) {
155
124
  if (__DEV__) {
156
125
  console.warn(
157
- "[DesignSystem] AtomicIcon requires an iconRenderer in DesignSystemProvider.\n" +
158
- "Example:\n" +
159
- "<DesignSystemProvider\n" +
126
+ '[DesignSystem] AtomicIcon requires an iconRenderer in DesignSystemProvider.\n' +
127
+ 'Example:\n' +
128
+ '<DesignSystemProvider\n' +
160
129
  ' iconRenderer={({ name, size, color }) => (\n' +
161
130
  ' <YourIconLibrary name={name} size={size} color={color} />\n' +
162
- " )}\n" +
163
- ">"
131
+ ' )}\n' +
132
+ '>'
164
133
  );
165
134
  }
166
135
  return null;
167
136
  }
168
137
 
169
- // Build render props
170
- const renderProps: IconRenderProps = {
171
- name: name || "",
172
- size: sizeInPixels,
173
- color: iconColor,
174
- style: style as StyleProp<ViewStyle>,
138
+ // Build render props and render icon
139
+ const renderProps = buildIconRenderProps(
140
+ name || '',
141
+ sizeInPixels,
142
+ iconColor,
143
+ style,
175
144
  testID,
176
- accessibilityLabel,
177
- };
145
+ accessibilityLabel
146
+ );
178
147
 
179
148
  const iconElement = iconRenderer(renderProps);
180
149
 
@@ -193,44 +162,4 @@ export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(
193
162
  }
194
163
  );
195
164
 
196
- AtomicIcon.displayName = "AtomicIcon";
197
-
198
- /**
199
- * Helper to render icon with circular background
200
- */
201
- function renderWithBackground(
202
- iconElement: React.ReactNode,
203
- sizeInPixels: number,
204
- bgColor: string,
205
- style: StyleProp<ViewStyle> | undefined,
206
- testID: string | undefined,
207
- accessibilityLabel: string | undefined
208
- ): React.ReactElement {
209
- const containerSize = sizeInPixels + 16;
210
-
211
- return (
212
- <View
213
- style={[
214
- styles.backgroundContainer,
215
- {
216
- width: containerSize,
217
- height: containerSize,
218
- borderRadius: containerSize / 2,
219
- backgroundColor: bgColor,
220
- },
221
- style,
222
- ]}
223
- testID={testID}
224
- accessibilityLabel={accessibilityLabel}
225
- >
226
- {iconElement}
227
- </View>
228
- );
229
- }
230
-
231
- const styles = StyleSheet.create({
232
- backgroundContainer: {
233
- justifyContent: "center",
234
- alignItems: "center",
235
- },
236
- });
165
+ AtomicIcon.displayName = 'AtomicIcon';
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Icon Renderer Components
3
+ * SVG rendering and background rendering logic
4
+ */
5
+
6
+ import React from 'react';
7
+ import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
8
+ import Svg, { Path } from 'react-native-svg';
9
+ import type { IconRenderProps } from '../iconStore';
10
+
11
+ /**
12
+ * Renders an SVG icon from path data
13
+ *
14
+ * @param svgPath - SVG path string
15
+ * @param svgViewBox - SVG viewBox attribute
16
+ * @param size - Icon size in pixels
17
+ * @param color - Icon color
18
+ * @param testID - Test ID for testing
19
+ * @param accessibilityLabel - Accessibility label
20
+ * @returns SVG element
21
+ */
22
+ export function renderSvgIcon(
23
+ svgPath: string,
24
+ svgViewBox: string,
25
+ size: number,
26
+ color: string,
27
+ testID?: string,
28
+ accessibilityLabel?: string
29
+ ): React.ReactElement {
30
+ return (
31
+ <Svg
32
+ viewBox={svgViewBox}
33
+ width={size}
34
+ height={size}
35
+ testID={testID}
36
+ accessibilityLabel={accessibilityLabel}
37
+ >
38
+ <Path d={svgPath} fill={color} />
39
+ </Svg>
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Renders an icon with a circular background
45
+ *
46
+ * @param iconElement - Icon element to wrap
47
+ * @param size - Icon size in pixels
48
+ * @param backgroundColor - Background color
49
+ * @param style - Additional styles
50
+ * @param testID - Test ID for testing
51
+ * @param accessibilityLabel - Accessibility label
52
+ * @returns Wrapped icon with background
53
+ */
54
+ export function renderWithBackground(
55
+ iconElement: React.ReactNode,
56
+ size: number,
57
+ backgroundColor: string,
58
+ style?: StyleProp<ViewStyle>,
59
+ testID?: string,
60
+ accessibilityLabel?: string
61
+ ): React.ReactElement {
62
+ const containerSize = size + 16;
63
+
64
+ return (
65
+ <View
66
+ style={[
67
+ styles.backgroundContainer,
68
+ {
69
+ width: containerSize,
70
+ height: containerSize,
71
+ borderRadius: containerSize / 2,
72
+ backgroundColor,
73
+ },
74
+ style,
75
+ ]}
76
+ testID={testID}
77
+ accessibilityLabel={accessibilityLabel}
78
+ >
79
+ {iconElement}
80
+ </View>
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Builds render props for custom icon renderer
86
+ *
87
+ * @param name - Icon name
88
+ * @param size - Icon size in pixels
89
+ * @param color - Icon color
90
+ * @param style - Additional styles
91
+ * @param testID - Test ID
92
+ * @param accessibilityLabel - Accessibility label
93
+ * @returns Icon render props
94
+ */
95
+ export function buildIconRenderProps(
96
+ name: string,
97
+ size: number,
98
+ color: string,
99
+ style?: StyleProp<ViewStyle>,
100
+ testID?: string,
101
+ accessibilityLabel?: string
102
+ ): IconRenderProps {
103
+ return {
104
+ name,
105
+ size,
106
+ color,
107
+ style: style as StyleProp<ViewStyle>,
108
+ testID,
109
+ accessibilityLabel,
110
+ };
111
+ }
112
+
113
+ const styles = StyleSheet.create({
114
+ backgroundContainer: {
115
+ justifyContent: 'center',
116
+ alignItems: 'center',
117
+ },
118
+ });
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Icon Utility Functions
3
+ * Helper functions for icon color and size calculations
4
+ */
5
+
6
+ import type { IconColor } from '../AtomicIcon.types';
7
+ import type { DesignTokens } from '../../../theme/types/ThemeTypes';
8
+
9
+ /**
10
+ * Maps semantic color names to actual color values from tokens
11
+ *
12
+ * @param color - Semantic color name
13
+ * @param tokens - Design tokens
14
+ * @returns Hex color string
15
+ */
16
+ export function getSemanticColor(
17
+ color: IconColor,
18
+ tokens: DesignTokens
19
+ ): string {
20
+ const colorMap: Record<IconColor, string> = {
21
+ primary: tokens.colors.primary,
22
+ secondary: tokens.colors.secondary,
23
+ success: tokens.colors.success,
24
+ warning: tokens.colors.warning,
25
+ error: tokens.colors.error,
26
+ info: tokens.colors.info,
27
+ onSurface: tokens.colors.onSurface,
28
+ surfaceVariant: tokens.colors.surfaceVariant,
29
+ onPrimary: tokens.colors.onPrimary,
30
+ onSecondary: tokens.colors.onSecondary,
31
+ textInverse: tokens.colors.textInverse,
32
+ textPrimary: tokens.colors.textPrimary,
33
+ textSecondary: tokens.colors.textSecondary,
34
+ textTertiary: tokens.colors.textTertiary,
35
+ onSurfaceVariant: tokens.colors.onSurfaceVariant,
36
+ };
37
+ return colorMap[color];
38
+ }
39
+
40
+ /**
41
+ * Calculates icon size in pixels
42
+ *
43
+ * @param size - Size preset or custom size value
44
+ * @param customSize - Custom size in pixels (overrides size)
45
+ * @param tokens - Design tokens
46
+ * @returns Size in pixels
47
+ */
48
+ export function calculateIconSize(
49
+ size: string | number,
50
+ customSize: number | undefined,
51
+ tokens: DesignTokens
52
+ ): number {
53
+ const baseSize = customSize ?? size;
54
+ const iconSizesMap = tokens.iconSizes as Record<string, number>;
55
+
56
+ return typeof baseSize === 'number'
57
+ ? baseSize * tokens.spacingMultiplier
58
+ : iconSizesMap[baseSize] ?? iconSizesMap['md'] ?? 24;
59
+ }
60
+
61
+ /**
62
+ * Calculates icon color
63
+ *
64
+ * @param customColor - Custom color override
65
+ * @param semanticColor - Semantic color from theme
66
+ * @param tokens - Design tokens
67
+ * @returns Hex color string
68
+ */
69
+ export function calculateIconColor(
70
+ customColor: string | undefined,
71
+ semanticColor: IconColor | undefined,
72
+ tokens: DesignTokens
73
+ ): string {
74
+ if (customColor) {
75
+ return customColor;
76
+ }
77
+
78
+ if (semanticColor) {
79
+ return getSemanticColor(semanticColor, tokens);
80
+ }
81
+
82
+ return tokens.colors.textPrimary;
83
+ }
84
+
85
+ /**
86
+ * Calculates container size for icon with background
87
+ *
88
+ * @param iconSize - Icon size in pixels
89
+ * @param padding - Padding around icon (default: 16)
90
+ * @returns Container size
91
+ */
92
+ export function calculateContainerSize(iconSize: number, padding: number = 16): number {
93
+ return iconSize + padding;
94
+ }
@@ -21,17 +21,22 @@ import { ExceptionLogger } from './ExceptionLogger';
21
21
  import { useExceptionStore } from '../storage/ExceptionStore';
22
22
 
23
23
  export class ExceptionService {
24
- private logger: ExceptionLogger;
25
- private reporter: ExceptionReporter;
24
+ private logger: ExceptionLogger | null = null;
25
+ private reporter: ExceptionReporter | null = null;
26
+ private reporterConfig: ExceptionReporter['config'];
26
27
 
27
28
  constructor(reporterConfig?: ExceptionReporter['config']) {
28
- this.logger = new ExceptionLogger();
29
- this.reporter = new ExceptionReporter(
30
- reporterConfig || {
31
- enabled: false,
32
- environment: 'development'
33
- }
34
- );
29
+ this.reporterConfig = reporterConfig || {
30
+ enabled: false,
31
+ environment: 'development'
32
+ };
33
+ }
34
+
35
+ private ensureInitialized() {
36
+ if (!this.logger) {
37
+ this.logger = new ExceptionLogger();
38
+ this.reporter = new ExceptionReporter(this.reporterConfig);
39
+ }
35
40
  }
36
41
 
37
42
  /**
@@ -43,17 +48,19 @@ export class ExceptionService {
43
48
  category: ExceptionCategory = 'unknown',
44
49
  context: ExceptionContext = {},
45
50
  ): Promise<void> {
51
+ this.ensureInitialized();
52
+
46
53
  const exception = ExceptionHandler.createException(error, severity, category, context);
47
54
 
48
55
  // Add to store
49
56
  useExceptionStore.getState().addException(exception);
50
57
 
51
58
  // Log locally
52
- await this.logger.logException(exception);
59
+ await this.logger!.logException(exception);
53
60
 
54
61
  // Report to external service if needed
55
62
  if (ExceptionHandler.shouldReportException(exception)) {
56
- await this.reporter.reportException(exception);
63
+ await this.reporter!.reportException(exception);
57
64
  }
58
65
 
59
66
  // Mark as handled
@@ -92,35 +99,40 @@ export class ExceptionService {
92
99
  * Get stored exceptions
93
100
  */
94
101
  async getStoredExceptions(): Promise<ExceptionEntity[]> {
95
- return this.logger.getStoredExceptions();
102
+ this.ensureInitialized();
103
+ return this.logger!.getStoredExceptions();
96
104
  }
97
105
 
98
106
  /**
99
107
  * Get exception statistics
100
108
  */
101
109
  async getExceptionStats() {
102
- return this.logger.getExceptionStats();
110
+ this.ensureInitialized();
111
+ return this.logger!.getExceptionStats();
103
112
  }
104
113
 
105
114
  /**
106
115
  * Clear stored exceptions
107
116
  */
108
117
  async clearStoredExceptions(): Promise<void> {
109
- await this.logger.clearStoredExceptions();
118
+ this.ensureInitialized();
119
+ await this.logger!.clearStoredExceptions();
110
120
  }
111
121
 
112
122
  /**
113
123
  * Update reporter configuration
114
124
  */
115
125
  updateReporterConfig(config: Partial<ExceptionReporter['config']>): void {
116
- this.reporter.updateConfig(config);
126
+ this.ensureInitialized();
127
+ this.reporter!.updateConfig(config);
117
128
  }
118
129
 
119
130
  /**
120
131
  * Set max stored exceptions
121
132
  */
122
133
  setMaxStoredExceptions(limit: number): void {
123
- this.logger.setMaxStoredExceptions(limit);
134
+ this.ensureInitialized();
135
+ this.logger!.setMaxStoredExceptions(limit);
124
136
  }
125
137
 
126
138
  /**
@@ -145,7 +157,7 @@ export class ExceptionService {
145
157
  }
146
158
  }
147
159
 
148
- // Export default instance
160
+ // Export default instance - lazy initialization
149
161
  export const exceptionService = new ExceptionService();
150
162
 
151
163
 
@@ -29,7 +29,7 @@ export const ExceptionEmptyState: React.FC<ExceptionEmptyStateProps> = ({
29
29
  }) => {
30
30
  const tokens = useAppDesignTokens();
31
31
 
32
- const styles = React.useMemo(
32
+ const styles = useMemo(
33
33
  () =>
34
34
  StyleSheet.create({
35
35
  actionButton: {
@@ -44,7 +44,7 @@ export const ExceptionErrorState: React.FC<ExceptionErrorStateProps> = ({
44
44
  const refreshIcon = useIconName('refresh');
45
45
  const displayIcon = icon || alertCircleIcon;
46
46
 
47
- const styles = React.useMemo(
47
+ const styles = useMemo(
48
48
  () =>
49
49
  StyleSheet.create({
50
50
  actionButton: {
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
8
+ import type { BatchOperation } from '../types/BatchTypes';
8
9
  import { BatchProcessor } from '../utils/BatchProcessor';
9
10
 
10
11
  export interface BatchProcessingOptions {
@@ -27,13 +28,6 @@ export interface BatchProcessingResult {
27
28
  failureCount: number;
28
29
  }
29
30
 
30
- export interface BatchOperation {
31
- uri: string;
32
- type: 'resize' | 'crop' | 'filter' | 'compress' | 'convert';
33
- params: Record<string, unknown>;
34
- options?: Record<string, unknown>;
35
- }
36
-
37
31
  export class ImageBatchService {
38
32
  static async processBatch(
39
33
  operations: BatchOperation[],
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Batch Operation Types
3
+ * Shared types for batch processing operations
4
+ */
5
+
6
+ export interface BatchOperation {
7
+ uri: string;
8
+ type: 'resize' | 'crop' | 'filter' | 'compress' | 'convert';
9
+ params: Record<string, unknown>;
10
+ options?: Record<string, unknown>;
11
+ }
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { ImageManipulationResult, ImageCropArea, SaveFormat } from '../../domain/entities/ImageTypes';
8
- import type { BatchOperation } from '../services/ImageBatchService';
8
+ import type { BatchOperation } from '../types/BatchTypes';
9
9
  import { ImageTransformService } from '../services/ImageTransformService';
10
10
  import { ImageConversionService } from '../services/ImageConversionService';
11
11
  import { ImageValidator } from './ImageValidator';
@@ -9,6 +9,7 @@ export class ImageError extends Error {
9
9
  ) {
10
10
  super(message);
11
11
  this.name = 'ImageError';
12
+ Object.setPrototypeOf(this, ImageError.prototype);
12
13
  }
13
14
  }
14
15
 
@@ -62,13 +62,13 @@ function InfiniteScrollListComponent<T>({
62
62
  }: InfiniteScrollListProps<T>): React.ReactElement {
63
63
  const { items, state, loadMore, refresh, canLoadMore } = useInfiniteScroll(config);
64
64
 
65
- const handleEndReached = React.useCallback(() => {
65
+ const handleEndReached = useCallback(() => {
66
66
  if (canLoadMore && config.autoLoad !== false) {
67
67
  loadMore();
68
68
  }
69
69
  }, [canLoadMore, loadMore, config.autoLoad]);
70
70
 
71
- const getItemKey = React.useCallback(
71
+ const getItemKey = useCallback(
72
72
  (item: T, index: number): string => {
73
73
  if (config.getItemKey) {
74
74
  return config.getItemKey(item, index);
@@ -12,7 +12,7 @@
12
12
  * - Fully configurable for general purpose use
13
13
  */
14
14
 
15
- import React from 'react';
15
+ import React, { useState, useEffect, useCallback, useMemo, useRef, useContext } from 'react';
16
16
  import { View, TouchableOpacity, ViewStyle } from 'react-native';
17
17
  import { AtomicIcon, AtomicText } from '../../atoms';
18
18
  import { useAppDesignTokens } from '../../theme';
@@ -71,7 +71,7 @@ const ScreenHeaderBackButton: React.FC<{
71
71
  backIconColor?: 'primary' | 'secondary' | 'error' | 'warning' | 'success' | 'surfaceVariant';
72
72
  testID?: string;
73
73
  }> = ({ hideBackButton, onBackPress, backIconName, backIconColor, testID }) => {
74
- const handleBackPress = React.useCallback(() => {
74
+ const handleBackPress = useCallback(() => {
75
75
  if (onBackPress) {
76
76
  onBackPress();
77
77
  }
@@ -136,7 +136,7 @@ export const ScreenHeader: React.FC<ScreenHeaderProps> = ({
136
136
  }) => {
137
137
  const tokens = useAppDesignTokens();
138
138
 
139
- const headerStyle = React.useMemo(() => [
139
+ const headerStyle = useMemo(() => [
140
140
  {
141
141
  flexDirection: 'row' as const,
142
142
  alignItems: 'center' as const,