@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.
- package/package.json +1 -1
- package/src/atoms/index.ts +31 -17
- package/src/atoms/picker/{types/index.ts → types.ts} +1 -1
- package/src/device/index.ts +0 -1
- package/src/device/infrastructure/services/PersistentDeviceIdService.ts +3 -49
- package/src/device/presentation/hooks/useDeviceInfo.ts +49 -15
- package/src/exception/presentation/components/ErrorBoundary.tsx +2 -2
- package/src/image/index.ts +0 -1
- package/src/image/infrastructure/utils/LayerManager.ts +0 -1
- package/src/image/presentation/components/ImageGallery.tsx +38 -37
- package/src/image/presentation/components/editor/StickerPickerSheet.tsx +9 -5
- package/src/index.ts +25 -25
- package/src/media/infrastructure/utils/file-media-utils.ts +2 -0
- package/src/molecules/bottom-sheet/components/BottomSheet.tsx +2 -2
- package/src/molecules/calendar/infrastructure/stores/useCalendarEvents.ts +16 -3
- package/src/molecules/filter-group/FilterGroup.tsx +1 -1
- package/src/molecules/navigation/index.ts +0 -3
- package/src/molecules/splash/index.ts +3 -3
- package/src/offline/presentation/hooks/useOffline.ts +16 -5
- package/src/onboarding/hooks/useOnboardingFlow.ts +13 -4
- package/src/presentation/utils/variants/index.ts +6 -0
- package/src/storage/cache/presentation/useCachedValue.ts +0 -3
- package/src/storage/domain/utils/ValidationUtils.ts +44 -0
- package/src/storage/infrastructure/adapters/StorageService.ts +18 -3
- package/src/storage/infrastructure/repositories/BaseStorageOperations.ts +6 -1
- package/src/storage/presentation/hooks/CacheStorageOperations.ts +13 -2
- package/src/storage/presentation/hooks/usePersistentCache.ts +30 -12
- package/src/theme/infrastructure/storage/ThemeStorage.ts +17 -1
- package/src/atoms/GlassView/index.ts +0 -1
- package/src/atoms/badge/index.ts +0 -6
- package/src/atoms/button/index.ts +0 -6
- package/src/atoms/card/index.ts +0 -2
- package/src/atoms/chip/index.ts +0 -6
- package/src/atoms/skeleton/index.ts +0 -9
- package/src/atoms/status-bar/index.ts +0 -6
- package/src/device/infrastructure/repositories/LegacyDeviceIdRepository.ts +0 -30
- package/src/exports/atoms.ts +0 -81
- package/src/exports/device.ts +0 -68
- package/src/exports/exception.ts +0 -7
- package/src/exports/filesystem.ts +0 -1
- package/src/exports/haptics.ts +0 -7
- package/src/exports/image.ts +0 -83
- package/src/exports/infinite-scroll.ts +0 -7
- package/src/exports/init.ts +0 -5
- package/src/exports/layouts.ts +0 -19
- package/src/exports/loading.ts +0 -20
- package/src/exports/media.ts +0 -1
- package/src/exports/molecules/alerts.ts +0 -22
- package/src/exports/molecules/bottom-sheet.ts +0 -19
- package/src/exports/molecules/calendar.ts +0 -23
- package/src/exports/molecules/core.ts +0 -29
- package/src/exports/molecules/countdown.ts +0 -23
- package/src/exports/molecules/emoji.ts +0 -17
- package/src/exports/molecules/index.ts +0 -13
- package/src/exports/molecules/misc.ts +0 -42
- package/src/exports/molecules/navigation.ts +0 -44
- package/src/exports/molecules/swipe-actions.ts +0 -15
- package/src/exports/offline.ts +0 -7
- package/src/exports/onboarding.ts +0 -6
- package/src/exports/organisms.ts +0 -9
- package/src/exports/responsive.ts +0 -36
- package/src/exports/safe-area.ts +0 -6
- package/src/exports/storage.ts +0 -1
- package/src/exports/tanstack.ts +0 -1
- package/src/exports/theme.ts +0 -44
- package/src/exports/timezone.ts +0 -7
- package/src/exports/typography.ts +0 -22
- package/src/exports/utilities.ts +0 -6
- package/src/exports/uuid.ts +0 -7
- package/src/exports/variants.ts +0 -22
- package/src/image/presentation/components/image/AtomicImage.tsx +0 -24
- package/src/molecules/splash/components/index.ts +0 -1
- package/src/molecules/splash/hooks/index.ts +0 -6
- /package/src/atoms/fab/{types/index.ts → types.ts} +0 -0
- /package/src/molecules/splash/{constants/index.ts → constants.ts} +0 -0
- /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.
|
|
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",
|
package/src/atoms/index.ts
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
// Button
|
|
7
7
|
export {
|
|
8
8
|
AtomicButton,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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 {
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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';
|
package/src/device/index.ts
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
|
|
41
|
+
if (isMountedRef.current) {
|
|
42
|
+
setSystemInfo(system);
|
|
43
|
+
setDeviceInfo(system.device);
|
|
44
|
+
setAppInfo(system.application);
|
|
45
|
+
}
|
|
39
46
|
} catch (err) {
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
69
|
+
|
|
70
|
+
if (isMountedRef.current) {
|
|
71
|
+
setDeviceInfo(info);
|
|
72
|
+
}
|
|
57
73
|
} catch (err) {
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
+
|
|
97
|
+
if (isMountedRef.current) {
|
|
98
|
+
setAppInfo(info);
|
|
99
|
+
}
|
|
75
100
|
} catch (err) {
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
+
render(): ReactNode {
|
|
60
60
|
if (this.state.hasError) {
|
|
61
61
|
if (this.props.fallback) {
|
|
62
62
|
return this.props.fallback;
|
package/src/image/index.ts
CHANGED
|
@@ -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,
|
|
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={
|
|
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,
|
|
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:
|
|
44
|
+
width: stickerSize,
|
|
41
45
|
aspectRatio: 1,
|
|
42
46
|
backgroundColor: tokens.colors.surfaceVariant,
|
|
43
47
|
borderRadius: tokens.radius.md,
|