@umituz/react-native-design-system 4.25.8 → 4.25.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.
Files changed (82) hide show
  1. package/package.json +1 -1
  2. package/src/atoms/AtomicAvatar.tsx +0 -5
  3. package/src/atoms/AtomicInput.tsx +0 -2
  4. package/src/atoms/AtomicProgress.tsx +0 -5
  5. package/src/atoms/AtomicTextArea.tsx +0 -4
  6. package/src/atoms/card/AtomicCard.tsx +111 -54
  7. package/src/atoms/input/types.ts +0 -2
  8. package/src/atoms/skeleton/AtomicSkeleton.tsx +2 -2
  9. package/src/image/presentation/components/ImageGallery.tsx +16 -15
  10. package/src/image/presentation/components/editor/StickerPickerSheet.tsx +4 -7
  11. package/src/index.ts +5 -0
  12. package/src/layouts/ScreenHeader/ScreenHeader.tsx +0 -2
  13. package/src/loading/presentation/providers/LoadingProvider.tsx +13 -4
  14. package/src/molecules/SearchBar/SearchBar.tsx +0 -2
  15. package/src/molecules/SearchBar/SearchSuggestions.tsx +1 -1
  16. package/src/molecules/SearchBar/types.ts +0 -1
  17. package/src/molecules/StepHeader/StepHeader.tsx +1 -1
  18. package/src/molecules/avatar/Avatar.tsx +76 -71
  19. package/src/molecules/avatar/AvatarGroup.tsx +1 -1
  20. package/src/molecules/calendar/presentation/components/CalendarDayCell.tsx +2 -3
  21. package/src/molecules/calendar/presentation/components/CalendarWeekdayHeader.tsx +1 -1
  22. package/src/molecules/countdown/components/Countdown.tsx +14 -11
  23. package/src/molecules/info-grid/InfoGrid.tsx +2 -2
  24. package/src/onboarding/presentation/components/BackgroundImageCollage.tsx +2 -2
  25. package/src/onboarding/presentation/components/OnboardingBackground.tsx +63 -49
  26. package/src/onboarding/presentation/components/OnboardingSlide.tsx +2 -2
  27. package/src/theme/infrastructure/providers/DesignSystemProvider.tsx +3 -1
  28. package/src/gallery/gallery-download.service.ts +0 -69
  29. package/src/gallery/gallery-save.service.ts +0 -80
  30. package/src/gallery/index.ts +0 -3
  31. package/src/gallery/types.ts +0 -11
  32. package/src/image/domain/entities/EditorTypes.ts +0 -23
  33. package/src/image/domain/entities/editor/EditorConfigTypes.ts +0 -35
  34. package/src/image/domain/entities/editor/EditorElementTypes.ts +0 -60
  35. package/src/image/domain/entities/editor/EditorFilterTypes.ts +0 -9
  36. package/src/image/domain/entities/editor/EditorLayerTypes.ts +0 -34
  37. package/src/image/domain/entities/editor/EditorStateTypes.ts +0 -35
  38. package/src/image/domain/entities/editor/EditorToolTypes.ts +0 -33
  39. package/src/image/infrastructure/services/ImageEditorService.ts +0 -134
  40. package/src/image/infrastructure/utils/ImageAnalysisUtils.ts +0 -120
  41. package/src/image/infrastructure/utils/ImageEditorHistoryUtils.ts +0 -63
  42. package/src/image/infrastructure/utils/LayerManager.ts +0 -65
  43. package/src/media/infrastructure/hooks/useGenericMediaGeneration.ts +0 -170
  44. package/src/molecules/ConfirmationModal.tsx +0 -42
  45. package/src/molecules/calendar/infrastructure/storage/CalendarStore.types.ts +0 -64
  46. package/src/molecules/calendar/infrastructure/storage/CalendarStore.utils.ts +0 -56
  47. package/src/molecules/calendar/infrastructure/storage/EventActions.ts +0 -140
  48. package/src/molecules/calendar/infrastructure/storage/NavigationActions.ts +0 -118
  49. package/src/molecules/calendar/presentation/hooks/useCalendar.ts +0 -185
  50. package/src/molecules/confirmation-modal/index.ts +0 -7
  51. package/src/molecules/listitem/index.ts +0 -6
  52. package/src/molecules/navigation/components/index.ts +0 -4
  53. package/src/molecules/navigation/utils/NavigationTheme.ts +0 -21
  54. package/src/presentation/utils/variants/compound.ts +0 -34
  55. package/src/services/api/ApiClient.ts +0 -180
  56. package/src/services/api/index.ts +0 -9
  57. package/src/services/api/types/ApiTypes.ts +0 -50
  58. package/src/services/api/utils/requestBuilder.ts +0 -92
  59. package/src/services/api/utils/responseHandler.ts +0 -130
  60. package/src/storage/cache/index.ts +0 -28
  61. package/src/theme/core/tokens/BorderRadius.ts +0 -16
  62. package/src/utilities/clipboard/ClipboardUtils.ts +0 -67
  63. package/src/utilities/clipboard/index.ts +0 -5
  64. package/src/utilities/index.ts +0 -6
  65. package/src/utilities/sharing/domain/entities/Share.ts +0 -104
  66. package/src/utilities/sharing/domain/entities/SharingUtils.ts +0 -111
  67. package/src/utilities/sharing/index.ts +0 -33
  68. package/src/utilities/sharing/infrastructure/services/SharingService.ts +0 -165
  69. package/src/utilities/sharing/presentation/hooks/useSharing.ts +0 -116
  70. package/src/utils/colorMapper.ts +0 -193
  71. package/src/utils/errors/adapters/CacheErrorAdapter.ts +0 -68
  72. package/src/utils/errors/adapters/ImageErrorAdapter.ts +0 -91
  73. package/src/utils/errors/adapters/StorageErrorAdapter.ts +0 -107
  74. package/src/utils/formatHelper.ts +0 -16
  75. package/src/utils/formatters/dateFormatter.ts +0 -64
  76. package/src/utils/formatters/numberFormatter.ts +0 -130
  77. package/src/utils/index.ts +0 -16
  78. package/src/utils/styleComposer.ts +0 -94
  79. package/src/utils/validationHelper.ts +0 -16
  80. package/src/utils/validators/dataValidators.ts +0 -111
  81. package/src/utils/validators/numericValidators.ts +0 -106
  82. package/src/utils/validators/stringValidators.ts +0 -85
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "4.25.8",
3
+ "version": "4.25.10",
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",
@@ -154,8 +154,3 @@ export const AtomicAvatar: React.FC<AtomicAvatarProps> = ({
154
154
  );
155
155
  };
156
156
 
157
- // =============================================================================
158
- // EXPORTS
159
- // =============================================================================
160
-
161
- export default AtomicAvatar;
@@ -29,7 +29,6 @@ export const AtomicInput = React.forwardRef<React.ElementRef<typeof TextInput>,
29
29
  returnKeyType,
30
30
  onSubmitEditing,
31
31
  blurOnSubmit,
32
- autoFocus,
33
32
  autoCapitalize = 'sentences',
34
33
  autoCorrect = true,
35
34
  autoComplete,
@@ -116,7 +115,6 @@ export const AtomicInput = React.forwardRef<React.ElementRef<typeof TextInput>,
116
115
  returnKeyType={returnKeyType}
117
116
  onSubmitEditing={onSubmitEditing}
118
117
  blurOnSubmit={blurOnSubmit}
119
- autoFocus={autoFocus}
120
118
  autoCapitalize={autoCapitalize}
121
119
  autoCorrect={autoCorrect}
122
120
  autoComplete={autoComplete}
@@ -144,8 +144,3 @@ const styles = StyleSheet.create({
144
144
  },
145
145
  });
146
146
 
147
- // =============================================================================
148
- // EXPORTS
149
- // =============================================================================
150
-
151
- export default AtomicProgress;
@@ -30,8 +30,6 @@ export interface AtomicTextAreaProps {
30
30
  style?: StyleProp<ViewStyle>;
31
31
  /** Input text style */
32
32
  inputStyle?: StyleProp<TextStyle>;
33
- /** Auto focus */
34
- autoFocus?: boolean;
35
33
  /** Return key type */
36
34
  returnKeyType?: 'done' | 'go' | 'next' | 'search' | 'send';
37
35
  /** Callback when submit button is pressed */
@@ -60,7 +58,6 @@ export const AtomicTextArea = forwardRef<React.ElementRef<typeof TextInput>, Ato
60
58
  disabled = false,
61
59
  style,
62
60
  inputStyle,
63
- autoFocus,
64
61
  returnKeyType,
65
62
  onSubmitEditing,
66
63
  blurOnSubmit,
@@ -92,7 +89,6 @@ export const AtomicTextArea = forwardRef<React.ElementRef<typeof TextInput>, Ato
92
89
  numberOfLines={lineCount}
93
90
  multiline
94
91
  editable={!disabled}
95
- autoFocus={autoFocus}
96
92
  returnKeyType={returnKeyType}
97
93
  onSubmitEditing={onSubmitEditing}
98
94
  blurOnSubmit={blurOnSubmit}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * AtomicCard
3
- *
3
+ *
4
4
  * Unified card component that handles base card states, media, info, and glowing effects.
5
5
  * Replaces InfoCard, MediaCard, and GlowingCard molecules.
6
6
  */
@@ -10,6 +10,8 @@ import {
10
10
  View,
11
11
  Pressable,
12
12
  type GestureResponderEvent,
13
+ type StyleProp,
14
+ type TextStyle,
13
15
  } from 'react-native';
14
16
  import { AtomicImage } from '../image/AtomicImage';
15
17
  import { AtomicText } from '../AtomicText';
@@ -19,66 +21,45 @@ import { getCardVariantStyles, cardStyles } from './styles/cardStyles';
19
21
  import { getCardPadding } from './configs/cardPaddingConfig';
20
22
  import type { AtomicCardProps } from './types';
21
23
 
22
- const AtomicCardComponent: React.FC<AtomicCardProps> = ({
23
- children,
24
- variant = 'elevated',
25
- padding = 'md',
24
+ interface CardContentProps {
25
+ badge?: string;
26
+ image?: AtomicCardProps['image'];
27
+ imageAspectRatio: number;
28
+ selected: boolean;
29
+ checkCircleIcon: string;
30
+ paddingValue: number;
31
+ title?: string;
32
+ subtitle?: string;
33
+ description?: string;
34
+ leftIcon?: string;
35
+ rightIcon?: string;
36
+ titleStyle?: StyleProp<TextStyle>;
37
+ subtitleStyle?: StyleProp<TextStyle>;
38
+ descriptionStyle?: StyleProp<TextStyle>;
39
+ children?: React.ReactNode;
40
+ }
41
+
42
+ const CardContent: React.FC<CardContentProps> = ({
43
+ badge,
44
+ image,
45
+ imageAspectRatio,
46
+ selected,
47
+ checkCircleIcon,
48
+ paddingValue,
26
49
  title,
27
50
  subtitle,
28
51
  description,
29
- image,
30
- imageAspectRatio = 1.6,
31
- badge,
32
52
  leftIcon,
33
53
  rightIcon,
34
- selected = false,
35
- glowColor,
36
- glowIntensity = 1,
37
- onPress,
38
- disabled = false,
39
- style,
40
54
  titleStyle,
41
55
  subtitleStyle,
42
56
  descriptionStyle,
43
- testID,
57
+ children,
44
58
  }) => {
45
59
  const tokens = useAppDesignTokens();
46
- const checkCircleIcon = useIconName('checkCircle');
47
-
48
- const variantStyles = useMemo(() => {
49
- const base = getCardVariantStyles(variant, tokens);
50
- if (variant === 'glowing' && glowColor) {
51
- return {
52
- ...base,
53
- container: {
54
- ...base.container,
55
- borderColor: glowColor,
56
- borderWidth: 2 * glowIntensity,
57
- }
58
- };
59
- }
60
- return base;
61
- }, [variant, tokens, glowColor, glowIntensity]);
62
-
63
- const paddingValue = getCardPadding(padding, tokens);
64
60
 
65
- const containerStyle = [
66
- cardStyles.container,
67
- { borderRadius: tokens.borders.radius.lg },
68
- variantStyles.container,
69
- selected && { borderColor: tokens.colors.primary, borderWidth: 2 },
70
- style,
71
- ];
72
-
73
- const handlePress = (event: GestureResponderEvent) => {
74
- if (!disabled && onPress) {
75
- onPress(event);
76
- }
77
- };
78
-
79
- const renderContent = () => (
61
+ return (
80
62
  <>
81
- {/* Badge */}
82
63
  {badge && (
83
64
  <View style={[cardStyles.badge, { backgroundColor: tokens.colors.primary }]}>
84
65
  <AtomicText type="labelSmall" color="onPrimary">
@@ -87,7 +68,6 @@ const AtomicCardComponent: React.FC<AtomicCardProps> = ({
87
68
  </View>
88
69
  )}
89
70
 
90
- {/* Media */}
91
71
  {image && (
92
72
  <AtomicImage
93
73
  source={typeof image === 'string' ? { uri: image } : image}
@@ -96,14 +76,12 @@ const AtomicCardComponent: React.FC<AtomicCardProps> = ({
96
76
  />
97
77
  )}
98
78
 
99
- {/* Selected Indicator */}
100
79
  {selected && (
101
80
  <View style={cardStyles.selectedOverlay}>
102
81
  <AtomicIcon name={checkCircleIcon} color="primary" size="md" />
103
82
  </View>
104
83
  )}
105
84
 
106
- {/* Text Content */}
107
85
  <View style={{ padding: paddingValue }}>
108
86
  {(title || leftIcon || rightIcon) && (
109
87
  <View style={cardStyles.header}>
@@ -159,6 +137,85 @@ const AtomicCardComponent: React.FC<AtomicCardProps> = ({
159
137
  </View>
160
138
  </>
161
139
  );
140
+ };
141
+
142
+ const AtomicCardComponent: React.FC<AtomicCardProps> = ({
143
+ children,
144
+ variant = 'elevated',
145
+ padding = 'md',
146
+ title,
147
+ subtitle,
148
+ description,
149
+ image,
150
+ imageAspectRatio = 1.6,
151
+ badge,
152
+ leftIcon,
153
+ rightIcon,
154
+ selected = false,
155
+ glowColor,
156
+ glowIntensity = 1,
157
+ onPress,
158
+ disabled = false,
159
+ style,
160
+ titleStyle,
161
+ subtitleStyle,
162
+ descriptionStyle,
163
+ testID,
164
+ }) => {
165
+ const tokens = useAppDesignTokens();
166
+ const checkCircleIcon = useIconName('checkCircle');
167
+
168
+ const variantStyles = useMemo(() => {
169
+ const base = getCardVariantStyles(variant, tokens);
170
+ if (variant === 'glowing' && glowColor) {
171
+ return {
172
+ ...base,
173
+ container: {
174
+ ...base.container,
175
+ borderColor: glowColor,
176
+ borderWidth: 2 * glowIntensity,
177
+ }
178
+ };
179
+ }
180
+ return base;
181
+ }, [variant, tokens, glowColor, glowIntensity]);
182
+
183
+ const paddingValue = getCardPadding(padding, tokens);
184
+
185
+ const containerStyle = [
186
+ cardStyles.container,
187
+ { borderRadius: tokens.borders.radius.lg },
188
+ variantStyles.container,
189
+ selected && { borderColor: tokens.colors.primary, borderWidth: 2 },
190
+ style,
191
+ ];
192
+
193
+ const handlePress = (event: GestureResponderEvent) => {
194
+ if (!disabled && onPress) {
195
+ onPress(event);
196
+ }
197
+ };
198
+
199
+ const content = (
200
+ <CardContent
201
+ badge={badge}
202
+ image={image}
203
+ imageAspectRatio={imageAspectRatio}
204
+ selected={selected}
205
+ checkCircleIcon={checkCircleIcon}
206
+ paddingValue={paddingValue}
207
+ title={title}
208
+ subtitle={subtitle}
209
+ description={description}
210
+ leftIcon={leftIcon}
211
+ rightIcon={rightIcon}
212
+ titleStyle={titleStyle}
213
+ subtitleStyle={subtitleStyle}
214
+ descriptionStyle={descriptionStyle}
215
+ >
216
+ {children}
217
+ </CardContent>
218
+ );
162
219
 
163
220
  if (onPress) {
164
221
  return (
@@ -171,14 +228,14 @@ const AtomicCardComponent: React.FC<AtomicCardProps> = ({
171
228
  ]}
172
229
  testID={testID}
173
230
  >
174
- {renderContent()}
231
+ {content}
175
232
  </Pressable>
176
233
  );
177
234
  }
178
235
 
179
236
  return (
180
237
  <View style={containerStyle} testID={testID}>
181
- {renderContent()}
238
+ {content}
182
239
  </View>
183
240
  );
184
241
  };
@@ -45,8 +45,6 @@ export interface AtomicInputProps {
45
45
  onSubmitEditing?: () => void;
46
46
  /** Blur on submit */
47
47
  blurOnSubmit?: boolean;
48
- /** Auto focus */
49
- autoFocus?: boolean;
50
48
  /** Auto-capitalize */
51
49
  autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
52
50
  /** Auto-correct */
@@ -79,9 +79,9 @@ export const AtomicSkeleton: React.FC<AtomicSkeletonProps> = ({
79
79
 
80
80
  const renderSkeletonItem = (index: number) => (
81
81
  <View key={`skeleton-group-${index}`} style={styles.skeletonGroup}>
82
- {skeletonConfigs.map((config, configIndex) => (
82
+ {skeletonConfigs.map((config) => (
83
83
  <SkeletonItem
84
- key={`skeleton-${index}-${configIndex}`}
84
+ key={`skeleton-${index}-${config.width}-${config.height}`}
85
85
  config={config}
86
86
  baseColor={tokens.colors.surfaceSecondary}
87
87
  multiplier={tokens.spacingMultiplier}
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Presentation - Image Gallery Component
3
- *
3
+ *
4
4
  * High-performance, premium image gallery using expo-image.
5
5
  * Replaces slow standard image components for instant loading.
6
6
  */
7
7
 
8
- import React, { useCallback, useState, useEffect, useMemo } from 'react';
8
+ import React, { useCallback, useRef, useMemo } from 'react';
9
9
  import { Modal, View, StyleSheet, FlatList, useWindowDimensions, type NativeSyntheticEvent, type NativeScrollEvent } from 'react-native';
10
10
  import { Image } from 'expo-image';
11
11
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -35,7 +35,8 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
35
35
  }) => {
36
36
  const insets = useSafeAreaInsets();
37
37
  const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = useWindowDimensions();
38
- const [currentIndex, setCurrentIndex] = useState(index);
38
+ const currentIndexRef = useRef(index);
39
+ const [, forceRender] = React.useReducer((x: number) => x + 1, 0);
39
40
 
40
41
  const styles = useMemo(() => StyleSheet.create({
41
42
  container: {
@@ -63,23 +64,24 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
63
64
  }
64
65
  }), [SCREEN_WIDTH, SCREEN_HEIGHT]);
65
66
 
66
- useEffect(() => {
67
- if (visible) setCurrentIndex(index);
68
- }, [visible, index]);
67
+ if (visible) {
68
+ currentIndexRef.current = index;
69
+ }
69
70
 
70
71
  const handleEdit = useCallback(async () => {
71
- const currentImage = images[currentIndex];
72
+ const currentImage = images[currentIndexRef.current];
72
73
  if (!currentImage || !onImageChange) return;
73
- await onImageChange(currentImage.uri, currentIndex);
74
- }, [images, currentIndex, onImageChange]);
74
+ await onImageChange(currentImage.uri, currentIndexRef.current);
75
+ }, [images, onImageChange]);
75
76
 
76
77
  const handleScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
77
78
  const nextIndex = Math.round(event.nativeEvent.contentOffset.x / SCREEN_WIDTH);
78
- if (nextIndex !== currentIndex) {
79
- setCurrentIndex(nextIndex);
79
+ if (nextIndex !== currentIndexRef.current) {
80
+ currentIndexRef.current = nextIndex;
80
81
  onIndexChange?.(nextIndex);
82
+ forceRender();
81
83
  }
82
- }, [currentIndex, onIndexChange, SCREEN_WIDTH]);
84
+ }, [onIndexChange, SCREEN_WIDTH]);
83
85
 
84
86
  const renderItem = useCallback(({ item }: { item: ImageViewerItem }) => (
85
87
  <View style={styles.imageWrapper}>
@@ -98,7 +100,7 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
98
100
  index: i,
99
101
  }), [SCREEN_WIDTH]);
100
102
 
101
- if (!visible && !currentIndex) return null;
103
+ if (!visible) return null;
102
104
 
103
105
  return (
104
106
  <Modal
@@ -112,7 +114,7 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
112
114
  <GalleryHeader
113
115
  onClose={onDismiss}
114
116
  onEdit={enableEditing ? handleEdit : undefined}
115
- title={title || `${currentIndex + 1} / ${images.length}`}
117
+ title={title || `${currentIndexRef.current + 1} / ${images.length}`}
116
118
  />
117
119
 
118
120
  <FlatList
@@ -130,7 +132,6 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
130
132
  />
131
133
 
132
134
  <View style={[styles.footer, { paddingBottom: Math.max(insets.bottom, 20) }]}>
133
- {/* Potential for thumbnail strip or captions in future */}
134
135
  </View>
135
136
  </View>
136
137
  </Modal>
@@ -2,7 +2,7 @@
2
2
  * Presentation - Sticker Picker Sheet
3
3
  */
4
4
 
5
- import React, { forwardRef, useMemo } from 'react';
5
+ import React, { forwardRef } from 'react';
6
6
  import { View, TouchableOpacity, ScrollView, Image, useWindowDimensions } from 'react-native';
7
7
  import { BottomSheetModal } from '../../../../molecules/bottom-sheet/components/BottomSheetModal';
8
8
  import type { BottomSheetModalRef } from '../../../../molecules/bottom-sheet/types/BottomSheet';
@@ -22,10 +22,7 @@ export const StickerPickerSheet = forwardRef<BottomSheetModalRef, StickerPickerS
22
22
  const tokens = useAppDesignTokens();
23
23
  const { width: SCREEN_WIDTH } = useWindowDimensions();
24
24
 
25
- const stickerSize = useMemo(
26
- () => (SCREEN_WIDTH - 64) / 3,
27
- [SCREEN_WIDTH]
28
- );
25
+ const stickerSize = (SCREEN_WIDTH - 64) / 3;
29
26
 
30
27
  return (
31
28
  <BottomSheetModal ref={ref} snapPoints={snapPoints} onDismiss={onDismiss}>
@@ -36,9 +33,9 @@ export const StickerPickerSheet = forwardRef<BottomSheetModalRef, StickerPickerS
36
33
 
37
34
  <ScrollView showsVerticalScrollIndicator={false}>
38
35
  <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: tokens.spacing.md }}>
39
- {stickers.map((uri, index) => (
36
+ {stickers.map((uri) => (
40
37
  <TouchableOpacity
41
- key={index}
38
+ key={uri}
42
39
  onPress={() => onSelectSticker(uri)}
43
40
  style={{
44
41
  width: stickerSize,
package/src/index.ts CHANGED
@@ -109,3 +109,8 @@ export * from "./storage";
109
109
  // CAROUSEL EXPORTS
110
110
  // =============================================================================
111
111
  export * from "./carousel";
112
+
113
+ // =============================================================================
114
+ // OFFLINE EXPORTS
115
+ // =============================================================================
116
+ export * from "./offline";
@@ -166,5 +166,3 @@ export const ScreenHeader: React.FC<ScreenHeaderProps> = ({
166
166
  </View>
167
167
  );
168
168
  };
169
-
170
- export default ScreenHeader;
@@ -3,11 +3,20 @@
3
3
  * Global loading provider with auto-detection
4
4
  */
5
5
 
6
- import React, { useEffect, useRef, useState } from 'react';
6
+ import React, { useEffect, useReducer, useRef } from 'react';
7
7
  import { useIsFetching } from '@tanstack/react-query';
8
8
  import { LoadingOverlay } from '../components/LoadingOverlay';
9
9
  import type { LoadingProviderProps } from '../../domain/types/loading.types';
10
10
 
11
+ type LoadingAction = { type: 'SHOW' } | { type: 'HIDE' };
12
+
13
+ function loadingReducer(state: boolean, action: LoadingAction): boolean {
14
+ switch (action.type) {
15
+ case 'SHOW': return true;
16
+ case 'HIDE': return false;
17
+ }
18
+ }
19
+
11
20
  export const LoadingProvider: React.FC<LoadingProviderProps> = ({
12
21
  children,
13
22
  spinnerColor,
@@ -18,7 +27,7 @@ export const LoadingProvider: React.FC<LoadingProviderProps> = ({
18
27
  minDisplayTime = 300,
19
28
  }) => {
20
29
  const isFetching = useIsFetching();
21
- const [showFetchLoading, setShowFetchLoading] = useState(false);
30
+ const [showFetchLoading, dispatch] = useReducer(loadingReducer, false);
22
31
  const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
23
32
  const showTimeRef = useRef<number>(0);
24
33
 
@@ -31,13 +40,13 @@ export const LoadingProvider: React.FC<LoadingProviderProps> = ({
31
40
  hideTimeoutRef.current = null;
32
41
  }
33
42
  showTimeRef.current = Date.now();
34
- setShowFetchLoading(true);
43
+ dispatch({ type: 'SHOW' });
35
44
  } else if (showFetchLoading) {
36
45
  const elapsed = Date.now() - showTimeRef.current;
37
46
  const remaining = Math.max(0, minDisplayTime - elapsed);
38
47
 
39
48
  hideTimeoutRef.current = setTimeout(() => {
40
- setShowFetchLoading(false);
49
+ dispatch({ type: 'HIDE' });
41
50
  hideTimeoutRef.current = null;
42
51
  }, remaining);
43
52
  }
@@ -18,7 +18,6 @@ export const SearchBar: React.FC<SearchBarProps> = ({
18
18
  onFocus,
19
19
  onBlur,
20
20
  placeholder = 'Search...',
21
- autoFocus = false,
22
21
  loading = false,
23
22
  disabled = false,
24
23
  containerStyle,
@@ -67,7 +66,6 @@ export const SearchBar: React.FC<SearchBarProps> = ({
67
66
  onBlur={onBlur}
68
67
  placeholder={placeholder}
69
68
  placeholderTextColor={tokens.colors.textSecondary}
70
- autoFocus={autoFocus}
71
69
  editable={!disabled}
72
70
  returnKeyType="search"
73
71
  autoCapitalize="none"
@@ -41,7 +41,7 @@ export function SearchSuggestions<T>({
41
41
  >
42
42
  {displayedSuggestions.map((item, index) => (
43
43
  <TouchableOpacity
44
- key={index}
44
+ key={String(item)}
45
45
  onPress={() => onSelectSuggestion(item)}
46
46
  style={[
47
47
  styles.item,
@@ -14,7 +14,6 @@ export interface SearchBarProps {
14
14
  onFocus?: () => void;
15
15
  onBlur?: () => void;
16
16
  placeholder?: string;
17
- autoFocus?: boolean;
18
17
  loading?: boolean;
19
18
  disabled?: boolean;
20
19
  showCancelButton?: boolean;
@@ -102,7 +102,7 @@ export const StepHeader: React.FC<StepHeaderProps> = ({
102
102
  <View style={styles.stepIndicator}>
103
103
  {Array.from({ length: cfg.totalSteps }, (_, i) => (
104
104
  <View
105
- key={i}
105
+ key={`step-${i}`}
106
106
  style={[
107
107
  styles.stepDot,
108
108
  i + 1 <= cfg.currentStep!