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

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 (76) hide show
  1. package/package.json +1 -1
  2. package/src/atoms/index.ts +31 -17
  3. package/src/atoms/picker/{types/index.ts → types.ts} +1 -1
  4. package/src/device/index.ts +0 -1
  5. package/src/device/infrastructure/services/PersistentDeviceIdService.ts +3 -49
  6. package/src/device/presentation/hooks/useDeviceInfo.ts +49 -15
  7. package/src/exception/presentation/components/ErrorBoundary.tsx +2 -2
  8. package/src/image/index.ts +0 -1
  9. package/src/image/infrastructure/utils/LayerManager.ts +0 -1
  10. package/src/image/presentation/components/ImageGallery.tsx +38 -37
  11. package/src/image/presentation/components/editor/StickerPickerSheet.tsx +9 -5
  12. package/src/index.ts +25 -25
  13. package/src/media/infrastructure/utils/file-media-utils.ts +2 -0
  14. package/src/molecules/bottom-sheet/components/BottomSheet.tsx +2 -2
  15. package/src/molecules/calendar/infrastructure/stores/useCalendarEvents.ts +16 -3
  16. package/src/molecules/filter-group/FilterGroup.tsx +1 -1
  17. package/src/molecules/navigation/index.ts +0 -3
  18. package/src/molecules/splash/index.ts +3 -3
  19. package/src/offline/presentation/hooks/useOffline.ts +16 -5
  20. package/src/onboarding/hooks/useOnboardingFlow.ts +13 -4
  21. package/src/presentation/utils/variants/index.ts +6 -0
  22. package/src/storage/cache/presentation/useCachedValue.ts +0 -3
  23. package/src/storage/domain/utils/ValidationUtils.ts +44 -0
  24. package/src/storage/infrastructure/adapters/StorageService.ts +18 -3
  25. package/src/storage/infrastructure/repositories/BaseStorageOperations.ts +6 -1
  26. package/src/storage/presentation/hooks/CacheStorageOperations.ts +13 -2
  27. package/src/storage/presentation/hooks/usePersistentCache.ts +30 -12
  28. package/src/theme/infrastructure/storage/ThemeStorage.ts +17 -1
  29. package/src/atoms/GlassView/index.ts +0 -1
  30. package/src/atoms/badge/index.ts +0 -6
  31. package/src/atoms/button/index.ts +0 -6
  32. package/src/atoms/card/index.ts +0 -2
  33. package/src/atoms/chip/index.ts +0 -6
  34. package/src/atoms/skeleton/index.ts +0 -9
  35. package/src/atoms/status-bar/index.ts +0 -6
  36. package/src/device/infrastructure/repositories/LegacyDeviceIdRepository.ts +0 -30
  37. package/src/exports/atoms.ts +0 -81
  38. package/src/exports/device.ts +0 -68
  39. package/src/exports/exception.ts +0 -7
  40. package/src/exports/filesystem.ts +0 -1
  41. package/src/exports/haptics.ts +0 -7
  42. package/src/exports/image.ts +0 -83
  43. package/src/exports/infinite-scroll.ts +0 -7
  44. package/src/exports/init.ts +0 -5
  45. package/src/exports/layouts.ts +0 -19
  46. package/src/exports/loading.ts +0 -20
  47. package/src/exports/media.ts +0 -1
  48. package/src/exports/molecules/alerts.ts +0 -22
  49. package/src/exports/molecules/bottom-sheet.ts +0 -19
  50. package/src/exports/molecules/calendar.ts +0 -23
  51. package/src/exports/molecules/core.ts +0 -29
  52. package/src/exports/molecules/countdown.ts +0 -23
  53. package/src/exports/molecules/emoji.ts +0 -17
  54. package/src/exports/molecules/index.ts +0 -13
  55. package/src/exports/molecules/misc.ts +0 -42
  56. package/src/exports/molecules/navigation.ts +0 -44
  57. package/src/exports/molecules/swipe-actions.ts +0 -15
  58. package/src/exports/offline.ts +0 -7
  59. package/src/exports/onboarding.ts +0 -6
  60. package/src/exports/organisms.ts +0 -9
  61. package/src/exports/responsive.ts +0 -36
  62. package/src/exports/safe-area.ts +0 -6
  63. package/src/exports/storage.ts +0 -1
  64. package/src/exports/tanstack.ts +0 -1
  65. package/src/exports/theme.ts +0 -44
  66. package/src/exports/timezone.ts +0 -7
  67. package/src/exports/typography.ts +0 -22
  68. package/src/exports/utilities.ts +0 -6
  69. package/src/exports/uuid.ts +0 -7
  70. package/src/exports/variants.ts +0 -22
  71. package/src/image/presentation/components/image/AtomicImage.tsx +0 -24
  72. package/src/molecules/splash/components/index.ts +0 -1
  73. package/src/molecules/splash/hooks/index.ts +0 -6
  74. /package/src/atoms/fab/{types/index.ts → types.ts} +0 -0
  75. /package/src/molecules/splash/{constants/index.ts → constants.ts} +0 -0
  76. /package/src/molecules/splash/{types/index.ts → types.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "4.23.65",
3
+ "version": "4.23.67",
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",
@@ -6,10 +6,12 @@
6
6
  // Button
7
7
  export {
8
8
  AtomicButton,
9
- type AtomicButtonProps,
10
- type ButtonVariant,
11
- type ButtonSize,
12
- } from './button';
9
+ } from './button/AtomicButton';
10
+ export type {
11
+ AtomicButtonProps,
12
+ ButtonVariant,
13
+ ButtonSize,
14
+ } from './button/types';
13
15
 
14
16
  // Text
15
17
  export { AtomicText, type AtomicTextProps } from './AtomicText';
@@ -17,10 +19,12 @@ export { AtomicText, type AtomicTextProps } from './AtomicText';
17
19
  // Card
18
20
  export {
19
21
  AtomicCard,
20
- type AtomicCardProps,
21
- type CardVariant as AtomicCardVariant,
22
- type CardPadding as AtomicCardPadding,
23
- } from './card';
22
+ } from './card/AtomicCard';
23
+ export type {
24
+ AtomicCardProps,
25
+ CardVariant as AtomicCardVariant,
26
+ CardPadding as AtomicCardPadding,
27
+ } from './card/types';
24
28
 
25
29
  // Input
26
30
  export {
@@ -54,14 +58,22 @@ export {
54
58
  ICON_SIZES,
55
59
  getIconSize,
56
60
  isIconSizePreset,
57
- } from './icon';
61
+ } from './icon/index';
58
62
 
59
63
 
60
64
  // Avatar
61
65
  export { AtomicAvatar, type AtomicAvatarProps } from './AtomicAvatar';
62
66
 
63
67
  // Chip
64
- export { AtomicChip, type AtomicChipProps } from './chip';
68
+ export {
69
+ AtomicChip,
70
+ } from './chip/AtomicChip';
71
+ export type {
72
+ AtomicChipProps,
73
+ ChipVariant,
74
+ ChipSize,
75
+ ChipColor,
76
+ } from './chip/types';
65
77
 
66
78
  // Progress
67
79
  export { AtomicProgress, type AtomicProgressProps } from './AtomicProgress';
@@ -90,10 +102,12 @@ export { AtomicDatePicker, type AtomicDatePickerProps } from './AtomicDatePicker
90
102
  export {
91
103
  AtomicSkeleton,
92
104
  type AtomicSkeletonProps,
93
- type SkeletonPattern,
94
- type SkeletonConfig,
95
- SKELETON_PATTERNS,
96
- } from './skeleton';
105
+ } from './skeleton/AtomicSkeleton';
106
+ export type {
107
+ SkeletonPattern,
108
+ SkeletonConfig,
109
+ } from './skeleton/AtomicSkeleton.types';
110
+ export { SKELETON_PATTERNS } from './skeleton/AtomicSkeleton.types';
97
111
 
98
112
  // Badge
99
113
  export {
@@ -101,7 +115,7 @@ export {
101
115
  type AtomicBadgeProps,
102
116
  type BadgeVariant,
103
117
  type BadgeSize,
104
- } from './badge';
118
+ } from './badge/AtomicBadge';
105
119
 
106
120
  // Spinner
107
121
  export {
@@ -124,13 +138,13 @@ export { AtomicSwitch, type AtomicSwitchProps } from './AtomicSwitch';
124
138
  export { AtomicTouchable, type AtomicTouchableProps } from './AtomicTouchable';
125
139
 
126
140
  // StatusBar
127
- export { AtomicStatusBar, type AtomicStatusBarProps } from './status-bar';
141
+ export { AtomicStatusBar, type AtomicStatusBarProps } from './status-bar/AtomicStatusBar';
128
142
 
129
143
  // Keyboard Avoiding
130
144
  export { AtomicKeyboardAvoidingView, type AtomicKeyboardAvoidingViewProps } from './AtomicKeyboardAvoidingView';
131
145
 
132
146
  // GlassView
133
- export { GlassView, type GlassViewProps } from './GlassView';
147
+ export { GlassView, type GlassViewProps } from './GlassView/GlassView';
134
148
 
135
149
  // Image
136
150
  export { AtomicImage, type AtomicImageProps } from './image/AtomicImage';
@@ -1,5 +1,5 @@
1
1
  import { ViewStyle, TextStyle } from 'react-native';
2
- import { IconColor } from '../../icon';
2
+ import { IconColor } from '../icon';
3
3
 
4
4
  /**
5
5
  * Picker option item
@@ -89,7 +89,6 @@ export { DeviceFeatureService } from './infrastructure/services/DeviceFeatureSer
89
89
  export { collectDeviceExtras } from './infrastructure/services/DeviceExtrasCollector';
90
90
  export type { DeviceExtras } from './infrastructure/services/DeviceExtrasCollector';
91
91
  export { SecureDeviceIdRepository } from './infrastructure/repositories/SecureDeviceIdRepository';
92
- export { LegacyDeviceIdRepository } from './infrastructure/repositories/LegacyDeviceIdRepository';
93
92
 
94
93
  // ============================================================================
95
94
  // PRESENTATION - Device hooks
@@ -1,6 +1,5 @@
1
1
  import { generateUUID } from '../../../uuid';
2
2
  import { SecureDeviceIdRepository } from '../repositories/SecureDeviceIdRepository';
3
- import { LegacyDeviceIdRepository } from '../repositories/LegacyDeviceIdRepository';
4
3
  import { DeviceIdService } from './DeviceIdService';
5
4
 
6
5
  let cachedDeviceId: string | null = null;
@@ -17,7 +16,6 @@ let initializationPromise: Promise<string> | null = null;
17
16
  */
18
17
  export class PersistentDeviceIdService {
19
18
  private static secureRepo = new SecureDeviceIdRepository();
20
- private static legacyRepo = new LegacyDeviceIdRepository();
21
19
 
22
20
  /**
23
21
  * Get device ID with caching and concurrent request handling
@@ -51,24 +49,9 @@ export class PersistentDeviceIdService {
51
49
  return secureId;
52
50
  }
53
51
 
54
- // 2. Try migration from legacy storage
55
- const migrated = await this.migrateFromLegacy();
56
- if (migrated) {
57
- if (__DEV__) {
58
- console.log('[PersistentDeviceIdService] Migrated ID from legacy storage:', migrated);
59
- }
60
- cachedDeviceId = migrated;
61
- return migrated;
62
- }
63
-
64
- // 3. Create brand new ID
52
+ // 2. Create brand new ID
65
53
  const newId = await this.createNewDeviceId();
66
-
67
- // Save to both for safety during transition
68
- await Promise.all([
69
- this.secureRepo.set(newId),
70
- this.legacyRepo.set(newId),
71
- ]);
54
+ await this.secureRepo.set(newId);
72
55
 
73
56
  if (__DEV__) {
74
57
  console.log('[PersistentDeviceIdService] Created new persistent ID:', newId);
@@ -86,32 +69,6 @@ export class PersistentDeviceIdService {
86
69
  }
87
70
  }
88
71
 
89
- /**
90
- * Handles migration while ensuring it only happens once
91
- */
92
- private static async migrateFromLegacy(): Promise<string | null> {
93
- try {
94
- const hasMigrated = await this.secureRepo.hasMigrated();
95
- if (hasMigrated) {
96
- return null;
97
- }
98
-
99
- const legacyId = await this.legacyRepo.get();
100
- if (!legacyId) {
101
- await this.secureRepo.setMigrated();
102
- return null;
103
- }
104
-
105
- // Upgrade legacy to secure storage
106
- await this.secureRepo.set(legacyId);
107
- await this.secureRepo.setMigrated();
108
-
109
- return legacyId;
110
- } catch {
111
- return null;
112
- }
113
- }
114
-
115
72
  /**
116
73
  * Generates a new ID based on platform info if possible
117
74
  */
@@ -142,10 +99,7 @@ export class PersistentDeviceIdService {
142
99
  */
143
100
  static async clearStoredId(): Promise<void> {
144
101
  try {
145
- await Promise.all([
146
- this.secureRepo.remove(),
147
- this.legacyRepo.remove(),
148
- ]);
102
+ await this.secureRepo.remove();
149
103
  cachedDeviceId = null;
150
104
  initializationPromise = null;
151
105
  if (__DEV__) {
@@ -8,7 +8,7 @@
8
8
  * @layer presentation/hooks
9
9
  */
10
10
 
11
- import { useState, useEffect, useCallback } from 'react';
11
+ import { useState, useEffect, useCallback, useRef } from 'react';
12
12
  import { DeviceService } from '../../infrastructure/services/DeviceService';
13
13
  import { PersistentDeviceIdService } from '../../infrastructure/services/PersistentDeviceIdService';
14
14
  import type { DeviceInfo, ApplicationInfo, SystemInfo } from '../../domain/entities/Device';
@@ -24,23 +24,34 @@ export const useDeviceInfo = () => {
24
24
  const [isLoading, setIsLoading] = useState(true);
25
25
  const [error, setError] = useState<string | null>(null);
26
26
 
27
+ const isMountedRef = useRef(true);
28
+
27
29
  /**
28
30
  * Load all device and app information
29
31
  */
30
32
  const loadInfo = useCallback(async () => {
33
+ if (!isMountedRef.current) return;
34
+
31
35
  setIsLoading(true);
32
36
  setError(null);
33
37
 
34
38
  try {
35
39
  const system = await DeviceService.getSystemInfo();
36
- setSystemInfo(system);
37
- setDeviceInfo(system.device);
38
- setAppInfo(system.application);
40
+
41
+ if (isMountedRef.current) {
42
+ setSystemInfo(system);
43
+ setDeviceInfo(system.device);
44
+ setAppInfo(system.application);
45
+ }
39
46
  } catch (err) {
40
- const errorMessage = err instanceof Error ? err.message : 'Failed to load device info';
41
- setError(errorMessage);
47
+ if (isMountedRef.current) {
48
+ const errorMessage = err instanceof Error ? err.message : 'Failed to load device info';
49
+ setError(errorMessage);
50
+ }
42
51
  } finally {
43
- setIsLoading(false);
52
+ if (isMountedRef.current) {
53
+ setIsLoading(false);
54
+ }
44
55
  }
45
56
  }, []);
46
57
 
@@ -48,17 +59,26 @@ export const useDeviceInfo = () => {
48
59
  * Load device info only
49
60
  */
50
61
  const loadDeviceInfo = useCallback(async () => {
62
+ if (!isMountedRef.current) return;
63
+
51
64
  setIsLoading(true);
52
65
  setError(null);
53
66
 
54
67
  try {
55
68
  const info = await DeviceService.getDeviceInfo();
56
- setDeviceInfo(info);
69
+
70
+ if (isMountedRef.current) {
71
+ setDeviceInfo(info);
72
+ }
57
73
  } catch (err) {
58
- const errorMessage = err instanceof Error ? err.message : 'Failed to load device info';
59
- setError(errorMessage);
74
+ if (isMountedRef.current) {
75
+ const errorMessage = err instanceof Error ? err.message : 'Failed to load device info';
76
+ setError(errorMessage);
77
+ }
60
78
  } finally {
61
- setIsLoading(false);
79
+ if (isMountedRef.current) {
80
+ setIsLoading(false);
81
+ }
62
82
  }
63
83
  }, []);
64
84
 
@@ -66,17 +86,26 @@ export const useDeviceInfo = () => {
66
86
  * Load app info only
67
87
  */
68
88
  const loadAppInfo = useCallback(async () => {
89
+ if (!isMountedRef.current) return;
90
+
69
91
  setIsLoading(true);
70
92
  setError(null);
71
93
 
72
94
  try {
73
95
  const info = await DeviceService.getApplicationInfo();
74
- setAppInfo(info);
96
+
97
+ if (isMountedRef.current) {
98
+ setAppInfo(info);
99
+ }
75
100
  } catch (err) {
76
- const errorMessage = err instanceof Error ? err.message : 'Failed to load app info';
77
- setError(errorMessage);
101
+ if (isMountedRef.current) {
102
+ const errorMessage = err instanceof Error ? err.message : 'Failed to load app info';
103
+ setError(errorMessage);
104
+ }
78
105
  } finally {
79
- setIsLoading(false);
106
+ if (isMountedRef.current) {
107
+ setIsLoading(false);
108
+ }
80
109
  }
81
110
  }, []);
82
111
 
@@ -91,7 +120,12 @@ export const useDeviceInfo = () => {
91
120
  * Load info on mount
92
121
  */
93
122
  useEffect(() => {
123
+ isMountedRef.current = true;
94
124
  loadInfo();
125
+
126
+ return () => {
127
+ isMountedRef.current = false;
128
+ };
95
129
  }, [loadInfo]);
96
130
 
97
131
  return {
@@ -36,7 +36,7 @@ export class ErrorBoundary extends Component<Props, State> {
36
36
  };
37
37
  }
38
38
 
39
- override componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
39
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
40
40
  // Log error to exception service
41
41
  exceptionService.handleFatalError(error, {
42
42
  componentStack: errorInfo.componentStack ?? undefined,
@@ -56,7 +56,7 @@ export class ErrorBoundary extends Component<Props, State> {
56
56
  });
57
57
  };
58
58
 
59
- override render(): ReactNode {
59
+ render(): ReactNode {
60
60
  if (this.state.hasError) {
61
61
  if (this.props.fallback) {
62
62
  return this.props.fallback;
@@ -81,7 +81,6 @@ export { ImageGallery, type ImageGalleryProps } from './presentation/components/
81
81
  export { TextEditorSheet, type TextEditorSheetProps } from './presentation/components/editor/TextEditorSheet';
82
82
  export { StickerPickerSheet, type StickerPickerSheetProps } from './presentation/components/editor/StickerPickerSheet';
83
83
  export { FilterPickerSheet, type FilterPickerSheetProps } from './presentation/components/editor/FilterPickerSheet';
84
- export { AtomicImage, type AtomicImageProps } from './presentation/components/image/AtomicImage';
85
84
 
86
85
  export { useImage } from './presentation/hooks/useImage';
87
86
  export { useImageTransform } from './presentation/hooks/useImageTransform';
@@ -7,7 +7,6 @@
7
7
  import type { EditorLayer } from '../../domain/entities/EditorTypes';
8
8
  import { ImageEditorService } from '../services/ImageEditorService';
9
9
 
10
- type LayerElement = EditorLayer['elements'][number];
11
10
  export type LayerOperation = 'add' | 'remove' | 'move' | 'merge' | 'duplicate';
12
11
 
13
12
  export interface LayerComposition {
@@ -5,15 +5,13 @@
5
5
  * Replaces slow standard image components for instant loading.
6
6
  */
7
7
 
8
- import React, { useCallback, useState, useEffect } from 'react';
9
- import { Modal, View, StyleSheet, FlatList, Dimensions, type NativeSyntheticEvent, type NativeScrollEvent } from 'react-native';
8
+ import React, { useCallback, useState, useEffect, useMemo } from 'react';
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';
12
12
  import type { ImageViewerItem, ImageGalleryOptions } from '../../domain/entities/ImageTypes';
13
13
  import { GalleryHeader } from './GalleryHeader';
14
14
 
15
- const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
16
-
17
15
  export interface ImageGalleryProps extends ImageGalleryOptions {
18
16
  images: ImageViewerItem[];
19
17
  visible: boolean;
@@ -36,8 +34,35 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
36
34
  title,
37
35
  }) => {
38
36
  const insets = useSafeAreaInsets();
37
+ const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = useWindowDimensions();
39
38
  const [currentIndex, setCurrentIndex] = useState(index);
40
39
 
40
+ const styles = useMemo(() => StyleSheet.create({
41
+ container: {
42
+ flex: 1,
43
+ },
44
+ list: {
45
+ flex: 1,
46
+ },
47
+ imageWrapper: {
48
+ width: SCREEN_WIDTH,
49
+ height: SCREEN_HEIGHT,
50
+ justifyContent: 'center',
51
+ alignItems: 'center',
52
+ },
53
+ fullImage: {
54
+ width: '100%',
55
+ height: '100%',
56
+ },
57
+ footer: {
58
+ position: 'absolute',
59
+ bottom: 0,
60
+ left: 0,
61
+ right: 0,
62
+ alignItems: 'center',
63
+ }
64
+ }), [SCREEN_WIDTH, SCREEN_HEIGHT]);
65
+
41
66
  useEffect(() => {
42
67
  if (visible) setCurrentIndex(index);
43
68
  }, [visible, index]);
@@ -54,7 +79,7 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
54
79
  setCurrentIndex(nextIndex);
55
80
  onIndexChange?.(nextIndex);
56
81
  }
57
- }, [currentIndex, onIndexChange]);
82
+ }, [currentIndex, onIndexChange, SCREEN_WIDTH]);
58
83
 
59
84
  const renderItem = useCallback(({ item }: { item: ImageViewerItem }) => (
60
85
  <View style={styles.imageWrapper}>
@@ -65,7 +90,13 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
65
90
  cachePolicy="memory-disk"
66
91
  />
67
92
  </View>
68
- ), []);
93
+ ), [styles]);
94
+
95
+ const getItemLayout = useCallback((_: unknown, i: number) => ({
96
+ length: SCREEN_WIDTH,
97
+ offset: SCREEN_WIDTH * i,
98
+ index: i,
99
+ }), [SCREEN_WIDTH]);
69
100
 
70
101
  if (!visible && !currentIndex) return null;
71
102
 
@@ -91,11 +122,7 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
91
122
  pagingEnabled
92
123
  showsHorizontalScrollIndicator={false}
93
124
  initialScrollIndex={index}
94
- getItemLayout={(_, i) => ({
95
- length: SCREEN_WIDTH,
96
- offset: SCREEN_WIDTH * i,
97
- index: i,
98
- })}
125
+ getItemLayout={getItemLayout}
99
126
  onScroll={handleScroll}
100
127
  scrollEventThrottle={16}
101
128
  keyExtractor={(item, i) => `${item.uri}-${i}`}
@@ -109,29 +136,3 @@ export const ImageGallery: React.FC<ImageGalleryProps> = ({
109
136
  </Modal>
110
137
  );
111
138
  };
112
-
113
- const styles = StyleSheet.create({
114
- container: {
115
- flex: 1,
116
- },
117
- list: {
118
- flex: 1,
119
- },
120
- imageWrapper: {
121
- width: SCREEN_WIDTH,
122
- height: SCREEN_HEIGHT,
123
- justifyContent: 'center',
124
- alignItems: 'center',
125
- },
126
- fullImage: {
127
- width: '100%',
128
- height: '100%',
129
- },
130
- footer: {
131
- position: 'absolute',
132
- bottom: 0,
133
- left: 0,
134
- right: 0,
135
- alignItems: 'center',
136
- }
137
- });
@@ -2,15 +2,13 @@
2
2
  * Presentation - Sticker Picker Sheet
3
3
  */
4
4
 
5
- import React, { forwardRef } from 'react';
6
- import { View, TouchableOpacity, ScrollView, Image, Dimensions } from 'react-native';
5
+ import React, { forwardRef, useMemo } from 'react';
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';
9
9
  import { AtomicText } from '../../../../atoms/AtomicText';
10
10
  import { useAppDesignTokens } from '../../../../theme/hooks/useAppDesignTokens';
11
11
 
12
- const { width: SCREEN_WIDTH } = Dimensions.get('window');
13
-
14
12
  export interface StickerPickerSheetProps {
15
13
  stickers: string[];
16
14
  onSelectSticker: (uri: string) => void;
@@ -22,6 +20,12 @@ export interface StickerPickerSheetProps {
22
20
  export const StickerPickerSheet = forwardRef<BottomSheetModalRef, StickerPickerSheetProps>(
23
21
  ({ stickers, onSelectSticker, onDismiss, title = 'Select Sticker', snapPoints = ['60%'] }, ref) => {
24
22
  const tokens = useAppDesignTokens();
23
+ const { width: SCREEN_WIDTH } = useWindowDimensions();
24
+
25
+ const stickerSize = useMemo(
26
+ () => (SCREEN_WIDTH - 64) / 3,
27
+ [SCREEN_WIDTH]
28
+ );
25
29
 
26
30
  return (
27
31
  <BottomSheetModal ref={ref} snapPoints={snapPoints} onDismiss={onDismiss}>
@@ -37,7 +41,7 @@ export const StickerPickerSheet = forwardRef<BottomSheetModalRef, StickerPickerS
37
41
  key={index}
38
42
  onPress={() => onSelectSticker(uri)}
39
43
  style={{
40
- width: (SCREEN_WIDTH - 64) / 3,
44
+ width: stickerSize,
41
45
  aspectRatio: 1,
42
46
  backgroundColor: tokens.colors.surfaceVariant,
43
47
  borderRadius: tokens.radius.md,