@umituz/react-native-design-system 4.23.68 → 4.23.70

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 (44) hide show
  1. package/package.json +1 -1
  2. package/src/atoms/EmptyState.tsx +2 -2
  3. package/src/atoms/icon/AtomicIcon.tsx +1 -1
  4. package/src/atoms/icon/utils/iconUtils.ts +1 -1
  5. package/src/exception/infrastructure/services/ExceptionService.ts +29 -17
  6. package/src/exception/presentation/components/ExceptionEmptyState.tsx +1 -1
  7. package/src/exception/presentation/components/ExceptionErrorState.tsx +1 -1
  8. package/src/image/infrastructure/services/ImageBatchService.ts +1 -7
  9. package/src/image/infrastructure/types/BatchTypes.ts +11 -0
  10. package/src/image/infrastructure/utils/BatchProcessor.ts +1 -1
  11. package/src/image/infrastructure/utils/ImageErrorHandler.ts +1 -0
  12. package/src/infinite-scroll/presentation/components/infinite-scroll-list.tsx +2 -2
  13. package/src/layouts/ScreenHeader/ScreenHeader.tsx +3 -3
  14. package/src/media/presentation/hooks/useCardMediaGeneration.ts +4 -4
  15. package/src/media/presentation/hooks/useCardMediaUpload.ts +4 -4
  16. package/src/media/presentation/hooks/useCardMediaValidation.ts +4 -4
  17. package/src/media/presentation/hooks/useCardMultimediaFlashcard.ts +5 -5
  18. package/src/media/presentation/hooks/useMediaGeneration.ts +4 -4
  19. package/src/media/presentation/hooks/useMediaUpload.ts +4 -4
  20. package/src/media/presentation/hooks/useMediaValidation.ts +4 -4
  21. package/src/media/presentation/hooks/useMultimediaFlashcard.ts +5 -5
  22. package/src/molecules/BaseModal.tsx +1 -1
  23. package/src/molecules/ConfirmationModalContent.tsx +2 -2
  24. package/src/molecules/ConfirmationModalMain.tsx +2 -2
  25. package/src/molecules/bottom-sheet/components/filter/FilterBottomSheet.tsx +2 -2
  26. package/src/molecules/calendar/presentation/components/AtomicCalendar.tsx +1 -1
  27. package/src/molecules/calendar/presentation/components/CalendarDayCell.tsx +2 -1
  28. package/src/molecules/calendar/presentation/components/CalendarWeekdayHeader.tsx +1 -1
  29. package/src/molecules/confirmation-modal/useConfirmationModal.ts +6 -6
  30. package/src/molecules/countdown/components/Countdown.tsx +2 -2
  31. package/src/molecules/splash/components/SplashScreen.tsx +9 -23
  32. package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +1 -1
  33. package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +2 -2
  34. package/src/organisms/FormContainer.tsx +2 -2
  35. package/src/responsive/validation.ts +1 -0
  36. package/src/storage/cache/domain/ErrorHandler.ts +1 -0
  37. package/src/storage/domain/errors/StorageError.ts +6 -0
  38. package/src/storage/infrastructure/repositories/AsyncStorageRepository.ts +31 -16
  39. package/src/tanstack/domain/repositories/IBaseRepository.ts +34 -0
  40. package/src/tanstack/domain/repositories/mixins/repositoryInvalidationMethods.ts +4 -4
  41. package/src/tanstack/domain/repositories/mixins/repositoryQueryMethods.ts +3 -3
  42. package/src/tanstack/infrastructure/providers/TanstackProvider.tsx +3 -3
  43. package/src/theme/index.ts +0 -3
  44. package/src/theme/infrastructure/providers/DesignSystemProvider.tsx +15 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "4.23.68",
3
+ "version": "4.23.70",
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: {
@@ -22,7 +22,7 @@
22
22
 
23
23
  import React from 'react';
24
24
  import { StyleProp, ViewStyle } from 'react-native';
25
- import { useAppDesignTokens } from '../../theme';
25
+ import { useAppDesignTokens } from '../../theme/hooks/useAppDesignTokens';
26
26
  import { useIconRenderer } from './iconStore';
27
27
  import {
28
28
  type IconSize as BaseIconSize,
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { IconColor } from '../AtomicIcon.types';
7
- import type { DesignTokens } from '../../../theme';
7
+ import type { DesignTokens } from '../../../theme/types/ThemeTypes';
8
8
 
9
9
  /**
10
10
  * Maps semantic color names to actual color values from tokens
@@ -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,
@@ -12,12 +12,12 @@ import type {
12
12
  } from "../../domain/entities/CardMultimedia.types";
13
13
 
14
14
  export const useCardMediaGeneration = (): UseCardMediaGenerationResult => {
15
- const [isGenerating, setIsGenerating] = React.useState(false);
15
+ const [isGenerating, setIsGenerating] = useState(false);
16
16
  const [generationResult, setGenerationResult] =
17
- React.useState<CardMediaGenerationResult | null>(null);
18
- const [error, setError] = React.useState<string | null>(null);
17
+ useState<CardMediaGenerationResult | null>(null);
18
+ const [error, setError] = useState<string | null>(null);
19
19
 
20
- const generateMedia = React.useCallback(
20
+ const generateMedia = useCallback(
21
21
  async (
22
22
  request: CardMediaGenerationRequest,
23
23
  ): Promise<CardMediaGenerationResult> => {
@@ -15,12 +15,12 @@ import type {
15
15
  } from "../../domain/entities/CardMultimedia.types";
16
16
 
17
17
  export const useCardMediaUpload = (): UseCardMediaUploadResult => {
18
- const [isUploading, setIsUploading] = React.useState(false);
18
+ const [isUploading, setIsUploading] = useState(false);
19
19
  const [uploadProgress, setUploadProgress] =
20
- React.useState<CardMediaUploadProgress | null>(null);
21
- const [error, setError] = React.useState<string | null>(null);
20
+ useState<CardMediaUploadProgress | null>(null);
21
+ const [error, setError] = useState<string | null>(null);
22
22
 
23
- const uploadMedia = React.useCallback(
23
+ const uploadMedia = useCallback(
24
24
  async (file: CardMediaFile, _options?: CardMediaCompressionOptions) => {
25
25
  try {
26
26
  setIsUploading(true);
@@ -9,12 +9,12 @@ import type { UseCardMediaValidationResult } from "./card-multimedia.types";
9
9
  import type { CardMediaValidation, CardMediaFile } from "../../domain/entities/CardMultimedia.types";
10
10
 
11
11
  export const useCardMediaValidation = (): UseCardMediaValidationResult => {
12
- const [isValidating, setIsValidating] = React.useState(false);
12
+ const [isValidating, setIsValidating] = useState(false);
13
13
  const [validation, setValidation] =
14
- React.useState<CardMediaValidation | null>(null);
15
- const [error, setError] = React.useState<string | null>(null);
14
+ useState<CardMediaValidation | null>(null);
15
+ const [error, setError] = useState<string | null>(null);
16
16
 
17
- const validateMedia = React.useCallback(
17
+ const validateMedia = useCallback(
18
18
  async (file: CardMediaFile): Promise<CardMediaValidation> => {
19
19
  try {
20
20
  setIsValidating(true);
@@ -30,10 +30,10 @@ export type {
30
30
  */
31
31
  export const useCardMultimediaFlashcard =
32
32
  (): UseCardMultimediaFlashcardResult => {
33
- const [isProcessing, setIsProcessing] = React.useState(false);
34
- const [error, setError] = React.useState<string | null>(null);
33
+ const [isProcessing, setIsProcessing] = useState(false);
34
+ const [error, setError] = useState<string | null>(null);
35
35
 
36
- const createCardMultimedia = React.useCallback(
36
+ const createCardMultimedia = useCallback(
37
37
  async (cardData: CreateCardMultimediaData): Promise<CardMultimediaFlashcard> => {
38
38
  try {
39
39
  setIsProcessing(true);
@@ -72,7 +72,7 @@ export const useCardMultimediaFlashcard =
72
72
  [],
73
73
  );
74
74
 
75
- const updateCardMedia = React.useCallback(
75
+ const updateCardMedia = useCallback(
76
76
  async (
77
77
  _cardId: string,
78
78
  _media: CardMediaAttachment[],
@@ -84,7 +84,7 @@ export const useCardMultimediaFlashcard =
84
84
  [],
85
85
  );
86
86
 
87
- const deleteCardMedia = React.useCallback(
87
+ const deleteCardMedia = useCallback(
88
88
  async (_attachmentId: string): Promise<void> => {
89
89
  // Mock implementation
90
90
  await new Promise((resolve) => setTimeout(resolve, 500));
@@ -12,12 +12,12 @@ import type {
12
12
  } from "../../domain/entities/MultimediaFlashcardTypes";
13
13
 
14
14
  export const useMediaGeneration = (): UseMediaGenerationResult => {
15
- const [isGenerating, setIsGenerating] = React.useState(false);
15
+ const [isGenerating, setIsGenerating] = useState(false);
16
16
  const [generationResult, setGenerationResult] =
17
- React.useState<MediaGenerationResult | null>(null);
18
- const [error, setError] = React.useState<string | null>(null);
17
+ useState<MediaGenerationResult | null>(null);
18
+ const [error, setError] = useState<string | null>(null);
19
19
 
20
- const generateMedia = React.useCallback(
20
+ const generateMedia = useCallback(
21
21
  async (request: MediaGenerationRequest): Promise<MediaGenerationResult> => {
22
22
  try {
23
23
  setIsGenerating(true);
@@ -15,12 +15,12 @@ import type {
15
15
  } from "../../domain/entities/MultimediaFlashcardTypes";
16
16
 
17
17
  export const useMediaUpload = (): UseMediaUploadResult => {
18
- const [isUploading, setIsUploading] = React.useState(false);
18
+ const [isUploading, setIsUploading] = useState(false);
19
19
  const [uploadProgress, setUploadProgress] =
20
- React.useState<MediaUploadProgress | null>(null);
21
- const [error, setError] = React.useState<string | null>(null);
20
+ useState<MediaUploadProgress | null>(null);
21
+ const [error, setError] = useState<string | null>(null);
22
22
 
23
- const uploadMedia = React.useCallback(
23
+ const uploadMedia = useCallback(
24
24
  async (file: MediaFile, _options?: MediaCompressionOptions) => {
25
25
  try {
26
26
  setIsUploading(true);
@@ -9,13 +9,13 @@ import type { UseMediaValidationResult } from "./multimedia.types";
9
9
  import type { MediaValidation, MediaFile } from "../../domain/entities/MultimediaFlashcardTypes";
10
10
 
11
11
  export const useMediaValidation = (): UseMediaValidationResult => {
12
- const [isValidating, setIsValidating] = React.useState(false);
13
- const [validation, setValidation] = React.useState<MediaValidation | null>(
12
+ const [isValidating, setIsValidating] = useState(false);
13
+ const [validation, setValidation] = useState<MediaValidation | null>(
14
14
  null,
15
15
  );
16
- const [error, setError] = React.useState<string | null>(null);
16
+ const [error, setError] = useState<string | null>(null);
17
17
 
18
- const validateMedia = React.useCallback(
18
+ const validateMedia = useCallback(
19
19
  async (file: MediaFile): Promise<MediaValidation> => {
20
20
  try {
21
21
  setIsValidating(true);
@@ -29,10 +29,10 @@ export type {
29
29
  * Main hook for multimedia flashcard operations
30
30
  */
31
31
  export const useMultimediaFlashcard = (): UseMultimediaFlashcardResult => {
32
- const [isProcessing, setIsProcessing] = React.useState(false);
33
- const [error, setError] = React.useState<string | null>(null);
32
+ const [isProcessing, setIsProcessing] = useState(false);
33
+ const [error, setError] = useState<string | null>(null);
34
34
 
35
- const createMultimediaCard = React.useCallback(
35
+ const createMultimediaCard = useCallback(
36
36
  async (cardData: CreateMultimediaCardData): Promise<MultimediaFlashcard> => {
37
37
  try {
38
38
  setIsProcessing(true);
@@ -71,7 +71,7 @@ export const useMultimediaFlashcard = (): UseMultimediaFlashcardResult => {
71
71
  [],
72
72
  );
73
73
 
74
- const updateMedia = React.useCallback(
74
+ const updateMedia = useCallback(
75
75
  async (
76
76
  _cardId: string,
77
77
  _media: MediaAttachment[],
@@ -83,7 +83,7 @@ export const useMultimediaFlashcard = (): UseMultimediaFlashcardResult => {
83
83
  [],
84
84
  );
85
85
 
86
- const deleteMedia = React.useCallback(
86
+ const deleteMedia = useCallback(
87
87
  async (_attachmentId: string): Promise<void> => {
88
88
  // Mock implementation
89
89
  await new Promise((resolve) => setTimeout(resolve, 500));
@@ -47,7 +47,7 @@ export const BaseModal: React.FC<BaseModalProps> = ({
47
47
  }
48
48
  }, [visible, testID, modalLayout.width, modalLayout.height]);
49
49
 
50
- const handleBackdropPress = React.useCallback(() => {
50
+ const handleBackdropPress = useCallback(() => {
51
51
  if (dismissOnBackdrop) {
52
52
  onClose();
53
53
  }
@@ -4,7 +4,7 @@
4
4
  * Content component for confirmation modal
5
5
  */
6
6
 
7
- import React from 'react';
7
+ import React, { useState, useEffect, useCallback, useMemo, useRef, useContext } from 'react';
8
8
  import { View, ViewStyle, StyleProp } from 'react-native';
9
9
  import { useAppDesignTokens } from '../theme';
10
10
  import { ConfirmationModalVariant } from './confirmation-modal/types/';
@@ -27,7 +27,7 @@ const useConfirmButtonStyle = (
27
27
  variant: ConfirmationModalVariant,
28
28
  tokens: ReturnType<typeof useAppDesignTokens>
29
29
  ) => {
30
- return React.useCallback(() => {
30
+ return useCallback(() => {
31
31
  const baseStyle = getButtonStyle();
32
32
  const variantStyles: ViewStyle[] = [];
33
33
 
@@ -4,7 +4,7 @@
4
4
  * Main confirmation modal component
5
5
  */
6
6
 
7
- import React from 'react';
7
+ import React, { useState, useEffect, useCallback, useMemo, useRef, useContext } from 'react';
8
8
  import { View, Modal, TouchableOpacity } from 'react-native';
9
9
  import { useAppDesignTokens } from '../theme';
10
10
  import { ConfirmationModalProps } from './confirmation-modal/types/';
@@ -15,7 +15,7 @@ import {
15
15
  import { ConfirmationModalContent } from './ConfirmationModalContent';
16
16
 
17
17
  const useBackdropHandler = (backdropDismissible: boolean, onCancel: () => void) => {
18
- return React.useCallback(() => {
18
+ return useCallback(() => {
19
19
  if (backdropDismissible) {
20
20
  onCancel();
21
21
  }
@@ -54,7 +54,7 @@ export const FilterBottomSheet = forwardRef<BottomSheetModalRef, FilterBottomShe
54
54
  });
55
55
  }
56
56
 
57
- const styles = React.useMemo(() => StyleSheet.create({
57
+ const styles = useMemo(() => StyleSheet.create({
58
58
  container: {
59
59
  flex: 1,
60
60
  padding: 16,
@@ -151,7 +151,7 @@ export const FilterBottomSheet = forwardRef<BottomSheetModalRef, FilterBottomShe
151
151
 
152
152
  const hasActiveFilters = FilterUtils.hasActiveFilter(safeSelectedIds, defaultId);
153
153
 
154
- React.useEffect(() => {
154
+ useEffect(() => {
155
155
  if (__DEV__) {
156
156
  console.log('[FilterBottomSheet] useEffect - Component ready', {
157
157
  refCurrent: !!internalRef.current,
@@ -32,7 +32,7 @@
32
32
 
33
33
  import React from 'react';
34
34
  import { View, StyleProp, ViewStyle } from 'react-native';
35
- import { useAppDesignTokens } from '../../../../index';
35
+ import { useAppDesignTokens } from '../../../../theme/hooks/useAppDesignTokens';
36
36
  import type { CalendarDay } from '../../domain/entities/CalendarDay.entity';
37
37
  import { CalendarService } from '../../infrastructure/services/CalendarService';
38
38
  import { calendarStyles } from './calendarStyles';
@@ -4,7 +4,8 @@
4
4
 
5
5
  import React from 'react';
6
6
  import { TouchableOpacity, View, StyleProp, ViewStyle } from 'react-native';
7
- import { AtomicText, useAppDesignTokens } from '../../../../index';
7
+ import { AtomicText } from '../../../../atoms';
8
+ import { useAppDesignTokens } from '../../../../theme/hooks/useAppDesignTokens';
8
9
  import type { CalendarDay } from '../../domain/entities/CalendarDay.entity';
9
10
 
10
11
  import { calendarStyles } from './calendarStyles';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import React from 'react';
6
6
  import { View } from 'react-native';
7
- import { AtomicText } from '../../../../index';
7
+ import { AtomicText } from '../../../../atoms';
8
8
  import { calendarStyles } from './calendarStyles';
9
9
 
10
10
  interface CalendarWeekdayHeaderProps {
@@ -4,14 +4,14 @@
4
4
  * Hook for managing confirmation modal state
5
5
  */
6
6
 
7
- import React from 'react';
7
+ import React, { useState, useEffect, useCallback, useMemo, useRef, useContext } from 'react';
8
8
  import { ConfirmationModalProps, ConfirmationModalVariant } from './types';
9
9
 
10
10
  const useConfirmationModalState = () => {
11
- const [visible, setVisible] = React.useState(false);
11
+ const [visible, setVisible] = useState(false);
12
12
 
13
- const showConfirmation = React.useCallback(() => setVisible(true), []);
14
- const hideConfirmation = React.useCallback(() => setVisible(false), []);
13
+ const showConfirmation = useCallback(() => setVisible(true), []);
14
+ const hideConfirmation = useCallback(() => setVisible(false), []);
15
15
 
16
16
  return { visible, showConfirmation, hideConfirmation };
17
17
  };
@@ -26,12 +26,12 @@ export const useConfirmationModal = (config: {
26
26
  }) => {
27
27
  const { visible, showConfirmation, hideConfirmation } = useConfirmationModalState();
28
28
 
29
- const handleConfirm = React.useCallback(() => {
29
+ const handleConfirm = useCallback(() => {
30
30
  config.onConfirm();
31
31
  hideConfirmation();
32
32
  }, [config, hideConfirmation]);
33
33
 
34
- const confirmationProps: ConfirmationModalProps = React.useMemo(() => ({
34
+ const confirmationProps: ConfirmationModalProps = useMemo(() => ({
35
35
  visible,
36
36
  title: config.title,
37
37
  message: config.message,
@@ -37,7 +37,7 @@ export const Countdown: React.FC<CountdownProps> = ({
37
37
  showSeconds = true,
38
38
  } = displayConfig;
39
39
 
40
- const [currentTargetIndex, setCurrentTargetIndex] = React.useState(0);
40
+ const [currentTargetIndex, setCurrentTargetIndex] = useState(0);
41
41
  const allTargets = useMemo(
42
42
  () => [target, ...alternateTargets],
43
43
  [target, alternateTargets]
@@ -49,7 +49,7 @@ export const Countdown: React.FC<CountdownProps> = ({
49
49
  onExpire,
50
50
  });
51
51
 
52
- React.useEffect(() => {
52
+ useEffect(() => {
53
53
  if (currentTarget) {
54
54
  updateTarget(currentTarget);
55
55
  }
@@ -1,9 +1,8 @@
1
1
 
2
2
  import React, { useEffect, useState, useCallback } from "react";
3
- import { View, Image, StyleSheet } from "react-native";
3
+ import { View, Image, StyleSheet, Text, ActivityIndicator } from "react-native";
4
4
  import { initialWindowMetrics } from "../../../safe-area";
5
- import { AtomicText, AtomicSpinner } from "../../../atoms";
6
- import { useAppDesignTokens } from "../../../theme";
5
+ import { useAppDesignTokens } from "../../../theme/hooks/useAppDesignTokens";
7
6
  import type { SplashScreenProps, SplashColors } from "../types";
8
7
  import { SPLASH_CONSTANTS } from "../constants";
9
8
 
@@ -100,39 +99,26 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({
100
99
  )}
101
100
 
102
101
  {appName ? (
103
- <AtomicText
104
- type="displaySmall"
105
- style={[styles.title, { color: colors.text }]}
106
- >
102
+ <Text style={[styles.title, { color: colors.text, fontSize: 30, fontWeight: '800' }]}>
107
103
  {appName}
108
- </AtomicText>
104
+ </Text>
109
105
  ) : null}
110
106
 
111
107
  {tagline ? (
112
- <AtomicText
113
- type="bodyLarge"
114
- style={[styles.tagline, { color: colors.text }]}
115
- >
108
+ <Text style={[styles.tagline, { color: colors.text, fontSize: 16 }]}>
116
109
  {tagline}
117
- </AtomicText>
110
+ </Text>
118
111
  ) : null}
119
112
 
120
113
  {/* Always show loading indicator during initialization */}
121
114
  <View style={styles.loadingContainer}>
122
- <AtomicSpinner
123
- size="lg"
124
- color={colors.text}
125
- style={styles.loadingIndicator}
126
- />
115
+ <ActivityIndicator size="large" color={colors.text} />
127
116
  </View>
128
117
 
129
118
  {timedOut && __DEV__ ? (
130
- <AtomicText
131
- type="labelSmall"
132
- style={[styles.timeoutText, { color: colors.text }]}
133
- >
119
+ <Text style={[styles.timeoutText, { color: colors.text, fontSize: 12 }]}>
134
120
  Initialization timeout
135
- </AtomicText>
121
+ </Text>
136
122
  ) : null}
137
123
  </View>
138
124
  </View>
@@ -8,7 +8,7 @@
8
8
  * @layer domain/entities
9
9
  */
10
10
 
11
- import type { IconName } from '../../../../index';
11
+ import type { IconName } from '../../../../atoms/icon';
12
12
 
13
13
  /**
14
14
  * Pre-built swipe action types
@@ -10,8 +10,8 @@
10
10
 
11
11
  import React from 'react';
12
12
  import { StyleSheet, TouchableOpacity, View, type StyleProp, type ViewStyle } from 'react-native';
13
- import { AtomicText, AtomicIcon } from '../../../../index';
14
- import { useAppDesignTokens } from '../../../../index';
13
+ import { AtomicText, AtomicIcon } from '../../../../atoms';
14
+ import { useAppDesignTokens } from '../../../../theme/hooks/useAppDesignTokens';
15
15
  import { HapticService } from '../../../../haptics';
16
16
  import type { SwipeActionConfig } from '../../domain/entities/SwipeAction';
17
17
  import { SwipeActionUtils } from '../../domain/entities/SwipeAction';
@@ -43,7 +43,7 @@
43
43
  * @module FormContainer
44
44
  */
45
45
 
46
- import React from 'react';
46
+ import React, { useState, useEffect, useCallback, useMemo, useRef, useContext } from 'react';
47
47
  import {
48
48
  ScrollView,
49
49
  View,
@@ -106,7 +106,7 @@ export const FormContainer: React.FC<FormContainerProps> = ({
106
106
  const formElementSpacing = tokens.spacing.lg;
107
107
 
108
108
  // Create styles for form container (memoized to avoid re-creation on every render)
109
- const styles = React.useMemo(
109
+ const styles = useMemo(
110
110
  () =>
111
111
  StyleSheet.create({
112
112
  container: {
@@ -14,6 +14,7 @@ export class ResponsiveValidationError extends Error {
14
14
  constructor(message: string) {
15
15
  super(`[Responsive] ${message}`);
16
16
  this.name = 'ResponsiveValidationError';
17
+ Object.setPrototypeOf(this, ResponsiveValidationError.prototype);
17
18
  }
18
19
  }
19
20
 
@@ -6,6 +6,7 @@ export class CacheError extends Error {
6
6
  constructor(message: string, public readonly code: string) {
7
7
  super(message);
8
8
  this.name = 'CacheError';
9
+ Object.setPrototypeOf(this, CacheError.prototype);
9
10
  }
10
11
  }
11
12
 
@@ -12,6 +12,7 @@ export class StorageError extends Error {
12
12
  constructor(message: string, public readonly key?: string) {
13
13
  super(message);
14
14
  this.name = 'StorageError';
15
+ Object.setPrototypeOf(this, StorageError.prototype);
15
16
  }
16
17
  }
17
18
 
@@ -25,6 +26,7 @@ export class StorageReadError extends StorageError {
25
26
  super(`Failed to read from storage: ${key}`, key);
26
27
  this.name = 'StorageReadError';
27
28
  this.cause = cause;
29
+ Object.setPrototypeOf(this, StorageReadError.prototype);
28
30
  }
29
31
  }
30
32
 
@@ -38,6 +40,7 @@ export class StorageWriteError extends StorageError {
38
40
  super(`Failed to write to storage: ${key}`, key);
39
41
  this.name = 'StorageWriteError';
40
42
  this.cause = cause;
43
+ Object.setPrototypeOf(this, StorageWriteError.prototype);
41
44
  }
42
45
  }
43
46
 
@@ -51,6 +54,7 @@ export class StorageDeleteError extends StorageError {
51
54
  super(`Failed to delete from storage: ${key}`, key);
52
55
  this.name = 'StorageDeleteError';
53
56
  this.cause = cause;
57
+ Object.setPrototypeOf(this, StorageDeleteError.prototype);
54
58
  }
55
59
  }
56
60
 
@@ -64,6 +68,7 @@ export class StorageSerializationError extends StorageError {
64
68
  super(`Failed to serialize data for key: ${key}`, key);
65
69
  this.name = 'StorageSerializationError';
66
70
  this.cause = cause;
71
+ Object.setPrototypeOf(this, StorageSerializationError.prototype);
67
72
  }
68
73
  }
69
74
 
@@ -77,5 +82,6 @@ export class StorageDeserializationError extends StorageError {
77
82
  super(`Failed to deserialize data for key: ${key}`, key);
78
83
  this.name = 'StorageDeserializationError';
79
84
  this.cause = cause;
85
+ Object.setPrototypeOf(this, StorageDeserializationError.prototype);
80
86
  }
81
87
  }
@@ -16,63 +16,76 @@ import { BatchStorageOperations } from './BatchStorageOperations';
16
16
  * Uses composition to follow Single Responsibility Principle
17
17
  */
18
18
  export class AsyncStorageRepository implements IStorageRepository {
19
- private baseOps: BaseStorageOperations;
20
- private stringOps: StringStorageOperations;
21
- private batchOps: BatchStorageOperations;
19
+ private baseOps: BaseStorageOperations | null = null;
20
+ private stringOps: StringStorageOperations | null = null;
21
+ private batchOps: BatchStorageOperations | null = null;
22
22
 
23
23
  constructor() {
24
- this.baseOps = new BaseStorageOperations();
25
- this.stringOps = new StringStorageOperations();
26
- this.batchOps = new BatchStorageOperations();
24
+ // Lazy initialization - defer object creation
25
+ }
26
+
27
+ private ensureInitialized() {
28
+ if (!this.baseOps) {
29
+ this.baseOps = new BaseStorageOperations();
30
+ this.stringOps = new StringStorageOperations();
31
+ this.batchOps = new BatchStorageOperations();
32
+ }
27
33
  }
28
34
 
29
35
  /**
30
36
  * Get item from AsyncStorage with type safety
31
37
  */
32
38
  async getItem<T>(key: string, defaultValue: T): Promise<StorageResult<T>> {
33
- return this.baseOps.getItem(key, defaultValue);
39
+ this.ensureInitialized();
40
+ return this.baseOps!.getItem(key, defaultValue);
34
41
  }
35
42
 
36
43
  /**
37
44
  * Set item in AsyncStorage with automatic JSON serialization
38
45
  */
39
46
  async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
40
- return this.baseOps.setItem(key, value);
47
+ this.ensureInitialized();
48
+ return this.baseOps!.setItem(key, value);
41
49
  }
42
50
 
43
51
  /**
44
52
  * Get string value (no JSON parsing)
45
53
  */
46
54
  async getString(key: string, defaultValue: string): Promise<StorageResult<string>> {
47
- return this.stringOps.getString(key, defaultValue);
55
+ this.ensureInitialized();
56
+ return this.stringOps!.getString(key, defaultValue);
48
57
  }
49
58
 
50
59
  /**
51
60
  * Set string value (no JSON serialization)
52
61
  */
53
62
  async setString(key: string, value: string): Promise<StorageResult<string>> {
54
- return this.stringOps.setString(key, value);
63
+ this.ensureInitialized();
64
+ return this.stringOps!.setString(key, value);
55
65
  }
56
66
 
57
67
  /**
58
68
  * Remove item from AsyncStorage
59
69
  */
60
70
  async removeItem(key: string): Promise<StorageResult<void>> {
61
- return this.baseOps.removeItem(key);
71
+ this.ensureInitialized();
72
+ return this.baseOps!.removeItem(key);
62
73
  }
63
74
 
64
75
  /**
65
76
  * Check if key exists in storage
66
77
  */
67
78
  async hasItem(key: string): Promise<boolean> {
68
- return this.baseOps.hasItem(key);
79
+ this.ensureInitialized();
80
+ return this.baseOps!.hasItem(key);
69
81
  }
70
82
 
71
83
  /**
72
84
  * Clear all AsyncStorage data
73
85
  */
74
86
  async clearAll(): Promise<StorageResult<void>> {
75
- return this.baseOps.clearAll();
87
+ this.ensureInitialized();
88
+ return this.baseOps!.clearAll();
76
89
  }
77
90
 
78
91
  /**
@@ -81,18 +94,20 @@ export class AsyncStorageRepository implements IStorageRepository {
81
94
  async getMultiple(
82
95
  keys: string[]
83
96
  ): Promise<StorageResult<Record<string, string | null>>> {
84
- return this.batchOps.getMultiple(keys);
97
+ this.ensureInitialized();
98
+ return this.batchOps!.getMultiple(keys);
85
99
  }
86
100
 
87
101
  /**
88
102
  * Get all keys from storage
89
103
  */
90
104
  async getAllKeys(): Promise<StorageResult<string[]>> {
91
- return this.batchOps.getAllKeys();
105
+ this.ensureInitialized();
106
+ return this.batchOps!.getAllKeys();
92
107
  }
93
108
  }
94
109
 
95
110
  /**
96
- * Singleton instance
111
+ * Singleton instance - lazy initialization
97
112
  */
98
113
  export const storageRepository = new AsyncStorageRepository();
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Base Repository Interface
3
+ * Defines the contract for repository implementations
4
+ */
5
+
6
+ import type { QueryClient } from '@tanstack/react-query';
7
+ import type { QueryKeyFactory } from '../utils/QueryKeyFactory';
8
+ import type {
9
+ CreateParams,
10
+ UpdateParams,
11
+ ListParams,
12
+ RepositoryOptions,
13
+ } from './RepositoryTypes';
14
+
15
+ export interface IBaseRepository<TData, TCreateVariables, TUpdateVariables> {
16
+ /** Query client instance */
17
+ getClient(): QueryClient;
18
+
19
+ /** Resource name */
20
+ readonly resource: string;
21
+
22
+ /** Query key factory */
23
+ readonly keys: QueryKeyFactory;
24
+
25
+ /** Cache options */
26
+ getCacheOptions(): RepositoryOptions;
27
+
28
+ /** Abstract methods to be implemented by subclasses */
29
+ fetchAll(params?: ListParams): Promise<TData[]>;
30
+ fetchById(id: string | number): Promise<TData>;
31
+ create(data: CreateParams<TCreateVariables>): Promise<TData>;
32
+ update(id: string | number, data: UpdateParams<TUpdateVariables>): Promise<TData>;
33
+ remove(id: string | number): Promise<void>;
34
+ }
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { QueryClient } from '@tanstack/react-query';
7
- import type { BaseRepository } from '../BaseRepository';
7
+ import type { IBaseRepository } from '../IBaseRepository';
8
8
  import { matchesResource } from '../helpers/repositoryHelpers';
9
9
 
10
10
  /**
@@ -13,7 +13,7 @@ import { matchesResource } from '../helpers/repositoryHelpers';
13
13
  * @param repository - Repository instance
14
14
  */
15
15
  export function invalidateAll<TData>(
16
- repository: BaseRepository<TData, unknown, unknown>
16
+ repository: IBaseRepository<TData, unknown, unknown>
17
17
  ): Promise<void> {
18
18
  const client = (repository as any).getClient() as QueryClient;
19
19
  const resource = (repository as any).resource;
@@ -31,7 +31,7 @@ export function invalidateAll<TData>(
31
31
  * @param repository - Repository instance
32
32
  */
33
33
  export function invalidateLists<TData>(
34
- repository: BaseRepository<TData, unknown, unknown>
34
+ repository: IBaseRepository<TData, unknown, unknown>
35
35
  ): Promise<void> {
36
36
  const client = (repository as any).getClient() as QueryClient;
37
37
  return client.invalidateQueries({
@@ -46,7 +46,7 @@ export function invalidateLists<TData>(
46
46
  * @param id - Item ID
47
47
  */
48
48
  export function invalidateDetail<TData>(
49
- repository: BaseRepository<TData, unknown, unknown>,
49
+ repository: IBaseRepository<TData, unknown, unknown>,
50
50
  id: string | number
51
51
  ): Promise<void> {
52
52
  const client = (repository as any).getClient() as QueryClient;
@@ -5,7 +5,7 @@
5
5
 
6
6
  import type { QueryClient, QueryKey } from '@tanstack/react-query';
7
7
  import type { ListParams } from '../RepositoryTypes';
8
- import type { BaseRepository } from '../BaseRepository';
8
+ import type { IBaseRepository } from '../IBaseRepository';
9
9
 
10
10
  /**
11
11
  * Query all items with caching
@@ -15,7 +15,7 @@ import type { BaseRepository } from '../BaseRepository';
15
15
  * @returns Promise of data array
16
16
  */
17
17
  export async function queryAll<TData>(
18
- repository: BaseRepository<TData, unknown, unknown>,
18
+ repository: IBaseRepository<TData, unknown, unknown>,
19
19
  params?: ListParams
20
20
  ): Promise<TData[]> {
21
21
  const client = (repository as any).getClient() as QueryClient;
@@ -39,7 +39,7 @@ export async function queryAll<TData>(
39
39
  * @returns Promise of data or undefined if not found
40
40
  */
41
41
  export async function queryById<TData>(
42
- repository: BaseRepository<TData, unknown, unknown>,
42
+ repository: IBaseRepository<TData, unknown, unknown>,
43
43
  id: string | number
44
44
  ): Promise<TData | undefined> {
45
45
  const client = (repository as any).getClient() as QueryClient;
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3
3
  import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
4
4
  import type { Persister } from '@tanstack/react-query-persist-client';
@@ -78,7 +78,7 @@ export function TanstackProvider({
78
78
  onPersistError,
79
79
  }: TanstackProviderProps): React.ReactElement {
80
80
  // Create QueryClient if not provided and set as global singleton
81
- const [queryClient] = React.useState(() => {
81
+ const [queryClient] = useState(() => {
82
82
  const client = providedQueryClient ?? createQueryClient(queryClientOptions);
83
83
  setGlobalQueryClient(client);
84
84
 
@@ -90,7 +90,7 @@ export function TanstackProvider({
90
90
  });
91
91
 
92
92
  // Create persister if persistence is enabled
93
- const [persister] = React.useState(() => {
93
+ const [persister] = useState(() => {
94
94
  if (!enablePersistence) return undefined;
95
95
  return providedPersister ?? createPersister(persisterOptions);
96
96
  });
@@ -76,9 +76,6 @@ export { useCommonStyles } from './hooks/useCommonStyles';
76
76
 
77
77
  export { DesignSystemProvider } from './infrastructure/providers/DesignSystemProvider';
78
78
 
79
- // Re-export icon types for convenience
80
- export type { IconRenderer, IconRenderProps, IconNames } from '../atoms/icon';
81
-
82
79
  // =============================================================================
83
80
  // THEME OBJECTS
84
81
  // =============================================================================
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState, ReactNode } from 'react';
1
+ import React, { useEffect, useState, ReactNode, lazy, Suspense } from 'react';
2
2
  import { ActivityIndicator, View, StyleSheet } from 'react-native';
3
3
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
4
4
  import { useFonts } from 'expo-font';
@@ -6,9 +6,12 @@ import { SafeAreaProvider, initialWindowMetrics } from '../../../safe-area';
6
6
  import { useTheme } from '../stores/themeStore';
7
7
  import { useDesignSystemTheme, type ThemeMode } from '../globalThemeStore';
8
8
  import type { CustomThemeColors } from '../../core/CustomColors';
9
- import { SplashScreen } from '../../../molecules/splash';
10
9
  import type { SplashScreenProps } from '../../../molecules/splash/types';
11
- import { useIconStore, type IconRenderer, type IconNames } from '../../../atoms/icon';
10
+ import { useIconStore } from '../../../atoms/icon/iconStore';
11
+ import type { IconRenderer, IconNames } from '../../../atoms/icon/iconStore';
12
+
13
+ // Lazy load SplashScreen to avoid circular dependency
14
+ const SplashScreen = lazy(() => import('../../../molecules/splash').then(m => ({ default: m.SplashScreen })));
12
15
 
13
16
 
14
17
  interface DesignSystemProviderProps {
@@ -97,7 +100,15 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
97
100
  if (loadingComponent) {
98
101
  content = loadingComponent;
99
102
  } else if (splashConfig) {
100
- content = <SplashScreen {...splashConfig} visible={true} />;
103
+ content = (
104
+ <Suspense fallback={
105
+ <View style={styles.loadingContainer}>
106
+ <ActivityIndicator size="large" />
107
+ </View>
108
+ }>
109
+ <SplashScreen {...splashConfig} visible={true} />
110
+ </Suspense>
111
+ );
101
112
  } else {
102
113
  content = (
103
114
  <View style={styles.loadingContainer}>