@umituz/react-native-design-system 2.3.14 → 2.3.16
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 +19 -2
- package/src/index.ts +105 -0
- package/src/layouts/ScreenLayout/ScreenLayout.example.tsx +2 -2
- package/src/layouts/ScreenLayout/ScreenLayout.tsx +1 -1
- package/src/molecules/animation/core/AnimationCore.ts +29 -0
- package/src/molecules/animation/domain/entities/Animation.ts +81 -0
- package/src/molecules/animation/domain/entities/Fireworks.ts +44 -0
- package/src/molecules/animation/domain/entities/Theme.ts +76 -0
- package/src/molecules/animation/index.ts +146 -0
- package/src/molecules/animation/infrastructure/services/AnimationConfigService.ts +35 -0
- package/src/molecules/animation/infrastructure/services/SpringAnimationConfigService.ts +67 -0
- package/src/molecules/animation/infrastructure/services/TimingAnimationConfigService.ts +57 -0
- package/src/molecules/animation/infrastructure/services/__tests__/SpringAnimationConfigService.test.ts +114 -0
- package/src/molecules/animation/infrastructure/services/__tests__/TimingAnimationConfigService.test.ts +105 -0
- package/src/molecules/animation/presentation/components/Fireworks.tsx +126 -0
- package/src/molecules/animation/presentation/components/__tests__/Fireworks.test.tsx +189 -0
- package/src/molecules/animation/presentation/hooks/__tests__/useAnimation.integration.test.ts +216 -0
- package/src/molecules/animation/presentation/hooks/__tests__/useFireworks.test.ts +242 -0
- package/src/molecules/animation/presentation/hooks/__tests__/useGesture.test.ts +111 -0
- package/src/molecules/animation/presentation/hooks/__tests__/useSpringAnimation.test.ts +131 -0
- package/src/molecules/animation/presentation/hooks/__tests__/useTimingAnimation.test.ts +175 -0
- package/src/molecules/animation/presentation/hooks/__tests__/useTransformAnimation.test.ts +137 -0
- package/src/molecules/animation/presentation/hooks/useAnimation.ts +77 -0
- package/src/molecules/animation/presentation/hooks/useFireworks.ts +141 -0
- package/src/molecules/animation/presentation/hooks/useGesture.ts +61 -0
- package/src/molecules/animation/presentation/hooks/useGestureCreators.ts +163 -0
- package/src/molecules/animation/presentation/hooks/useGestureState.ts +53 -0
- package/src/molecules/animation/presentation/hooks/useIconAnimations.ts +119 -0
- package/src/molecules/animation/presentation/hooks/useModalAnimations.ts +124 -0
- package/src/molecules/animation/presentation/hooks/useReanimatedReady.ts +60 -0
- package/src/molecules/animation/presentation/hooks/useSpringAnimation.ts +69 -0
- package/src/molecules/animation/presentation/hooks/useTimingAnimation.ts +111 -0
- package/src/molecules/animation/presentation/hooks/useTransformAnimation.ts +57 -0
- package/src/molecules/animation/presentation/providers/AnimationThemeProvider.tsx +62 -0
- package/src/molecules/animation/presentation/providers/__tests__/AnimationThemeProvider.test.tsx +165 -0
- package/src/molecules/animation/types/global.d.ts +97 -0
- package/src/molecules/celebration/domain/entities/CelebrationConfig.ts +17 -0
- package/src/molecules/celebration/domain/entities/FireworksConfig.ts +32 -0
- package/src/molecules/celebration/index.ts +93 -0
- package/src/molecules/celebration/infrastructure/services/FireworksConfigService.ts +49 -0
- package/src/molecules/celebration/presentation/components/CelebrationFireworksOverlay.tsx +33 -0
- package/src/molecules/celebration/presentation/components/CelebrationModal.tsx +78 -0
- package/src/molecules/celebration/presentation/components/CelebrationModalContent.tsx +90 -0
- package/src/molecules/celebration/presentation/hooks/useCelebrationModalAnimation.ts +49 -0
- package/src/molecules/celebration/presentation/hooks/useCelebrationState.ts +45 -0
- package/src/molecules/celebration/presentation/styles/CelebrationModalStyles.ts +65 -0
- package/src/molecules/countdown/components/Countdown.tsx +128 -0
- package/src/molecules/countdown/components/CountdownHeader.tsx +84 -0
- package/src/molecules/countdown/components/TimeUnit.tsx +73 -0
- package/src/molecules/countdown/hooks/useCountdown.ts +107 -0
- package/src/molecules/countdown/index.ts +25 -0
- package/src/molecules/countdown/types/CountdownTypes.ts +31 -0
- package/src/molecules/countdown/utils/TimeCalculator.ts +46 -0
- package/src/molecules/emoji/domain/entities/Emoji.ts +129 -0
- package/src/molecules/emoji/index.ts +177 -0
- package/src/molecules/emoji/presentation/components/EmojiPicker.tsx +102 -0
- package/src/molecules/emoji/presentation/hooks/useEmojiPicker.ts +171 -0
- package/src/molecules/index.ts +21 -0
- package/src/molecules/long-press-menu/domain/entities/MenuAction.ts +37 -0
- package/src/molecules/long-press-menu/index.ts +16 -0
- package/src/molecules/navigation/StackNavigator.tsx +75 -0
- package/src/molecules/navigation/TabsNavigator.tsx +94 -0
- package/src/molecules/navigation/components/FabButton.tsx +45 -0
- package/src/molecules/navigation/components/TabLabel.tsx +47 -0
- package/src/molecules/navigation/createStackNavigator.ts +20 -0
- package/src/molecules/navigation/createTabNavigator.ts +20 -0
- package/src/molecules/navigation/hooks/useTabBarStyles.ts +54 -0
- package/src/molecules/navigation/index.ts +37 -0
- package/src/molecules/navigation/types.ts +118 -0
- package/src/molecules/navigation/utils/AppNavigation.ts +101 -0
- package/src/molecules/navigation/utils/IconRenderer.ts +50 -0
- package/src/molecules/navigation/utils/LabelProcessor.ts +70 -0
- package/src/molecules/navigation/utils/NavigationCleanup.ts +62 -0
- package/src/molecules/navigation/utils/NavigationTheme.ts +21 -0
- package/src/molecules/navigation/utils/NavigationValidator.ts +61 -0
- package/src/molecules/navigation/utils/ScreenFactory.ts +115 -0
- package/src/molecules/navigation/utils/__tests__/IconRenderer.getIconName.test.ts +109 -0
- package/src/molecules/navigation/utils/__tests__/IconRenderer.renderIcon.test.ts +116 -0
- package/src/molecules/navigation/utils/__tests__/LabelProcessor.processLabel.test.ts +116 -0
- package/src/molecules/navigation/utils/__tests__/LabelProcessor.processTitle.test.ts +59 -0
- package/src/molecules/navigation/utils/__tests__/NavigationCleanup.test.ts +271 -0
- package/src/molecules/navigation/utils/__tests__/NavigationValidator.test.ts +252 -0
- package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +194 -0
- package/src/molecules/swipe-actions/index.ts +6 -0
- package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +131 -0
- package/src/theme/hooks/useResponsiveDesignTokens.ts +1 -1
- package/src/utilities/clipboard/ClipboardUtils.ts +71 -0
- package/src/utilities/clipboard/index.ts +5 -0
- package/src/utilities/index.ts +6 -0
- package/src/utilities/sharing/domain/entities/Share.ts +210 -0
- package/src/utilities/sharing/index.ts +205 -0
- package/src/utilities/sharing/infrastructure/services/SharingService.ts +165 -0
- package/src/utilities/sharing/presentation/hooks/useSharing.ts +154 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sharing Domain - Barrel Export
|
|
3
|
+
*
|
|
4
|
+
* Provides system share sheet functionality using expo-sharing.
|
|
5
|
+
* Optional domain - disabled by default (opt-in).
|
|
6
|
+
*
|
|
7
|
+
* @domain sharing
|
|
8
|
+
* @enabled false (Apps that need file sharing - Opt-in)
|
|
9
|
+
*
|
|
10
|
+
* ARCHITECTURE:
|
|
11
|
+
* - Domain Layer: Entities (share types, MIME types, utilities)
|
|
12
|
+
* - Infrastructure Layer: Services (SharingService)
|
|
13
|
+
* - Presentation Layer: Hooks (useSharing)
|
|
14
|
+
*
|
|
15
|
+
* DEPENDENCIES:
|
|
16
|
+
* - expo-sharing (system share sheet)
|
|
17
|
+
*
|
|
18
|
+
* FEATURES:
|
|
19
|
+
* - Share files via system share sheet
|
|
20
|
+
* - Automatic MIME type detection
|
|
21
|
+
* - Platform-specific options (UTI for iOS, dialogTitle for Android)
|
|
22
|
+
* - Share availability check
|
|
23
|
+
* - Multiple file sharing (sequential)
|
|
24
|
+
*
|
|
25
|
+
* USAGE:
|
|
26
|
+
*
|
|
27
|
+
* Basic Sharing:
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { useSharing } from '@umituz/react-native-sharing';
|
|
30
|
+
*
|
|
31
|
+
* const { share, isAvailable, isSharing } = useSharing();
|
|
32
|
+
*
|
|
33
|
+
* if (!isAvailable) {
|
|
34
|
+
* return <Text>Sharing not available</Text>;
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* const handleShare = async () => {
|
|
38
|
+
* const success = await share('file:///path/to/file.jpg', {
|
|
39
|
+
* dialogTitle: 'Share Photo',
|
|
40
|
+
* mimeType: 'image/jpeg',
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* if (success) {
|
|
44
|
+
* console.log('Shared successfully');
|
|
45
|
+
* }
|
|
46
|
+
* };
|
|
47
|
+
*
|
|
48
|
+
* <AtomicButton onPress={handleShare} loading={isSharing}>
|
|
49
|
+
* Share
|
|
50
|
+
* </AtomicButton>
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* Auto-Detect MIME Type:
|
|
54
|
+
* ```typescript
|
|
55
|
+
* import { useSharing } from '@umituz/react-native-sharing';
|
|
56
|
+
*
|
|
57
|
+
* const { shareWithAutoType } = useSharing();
|
|
58
|
+
*
|
|
59
|
+
* // MIME type auto-detected from .pdf extension
|
|
60
|
+
* await shareWithAutoType(
|
|
61
|
+
* 'file:///path/to/document.pdf',
|
|
62
|
+
* 'document.pdf',
|
|
63
|
+
* 'Share Document'
|
|
64
|
+
* );
|
|
65
|
+
*
|
|
66
|
+
* // MIME type auto-detected from .jpg extension
|
|
67
|
+
* await shareWithAutoType(
|
|
68
|
+
* 'file:///path/to/photo.jpg',
|
|
69
|
+
* 'photo.jpg',
|
|
70
|
+
* 'Share Photo'
|
|
71
|
+
* );
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* Share with media Integration:
|
|
75
|
+
* ```typescript
|
|
76
|
+
* import { useMedia } from '@umituz/react-native-media';
|
|
77
|
+
* import { useSharing } from '@umituz/react-native-sharing';
|
|
78
|
+
* import { FileSystemService } from '@umituz/react-native-filesystem';
|
|
79
|
+
*
|
|
80
|
+
* const { pickImage } = useMedia();
|
|
81
|
+
* const { shareWithAutoType } = useSharing();
|
|
82
|
+
*
|
|
83
|
+
* const handlePickAndShare = async () => {
|
|
84
|
+
* // Pick image
|
|
85
|
+
* const result = await pickImage();
|
|
86
|
+
* if (result.canceled || !result.assets?.[0]?.uri) return;
|
|
87
|
+
*
|
|
88
|
+
* // Save to filesystem
|
|
89
|
+
* const savedPath = await FileSystemService.copyToDocuments(
|
|
90
|
+
* result.assets[0].uri,
|
|
91
|
+
* 'shared_image.jpg'
|
|
92
|
+
* );
|
|
93
|
+
*
|
|
94
|
+
* if (!savedPath.success || !savedPath.uri) return;
|
|
95
|
+
*
|
|
96
|
+
* // Share via system sheet
|
|
97
|
+
* await shareWithAutoType(savedPath.uri, 'shared_image.jpg', 'Share Photo');
|
|
98
|
+
* };
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* Share Multiple Files:
|
|
102
|
+
* ```typescript
|
|
103
|
+
* import { useSharing } from '@umituz/react-native-sharing';
|
|
104
|
+
*
|
|
105
|
+
* const { shareMultiple } = useSharing();
|
|
106
|
+
*
|
|
107
|
+
* const handleShareMultiple = async () => {
|
|
108
|
+
* const uris = [
|
|
109
|
+
* 'file:///path/to/file1.jpg',
|
|
110
|
+
* 'file:///path/to/file2.jpg',
|
|
111
|
+
* 'file:///path/to/file3.jpg',
|
|
112
|
+
* ];
|
|
113
|
+
*
|
|
114
|
+
* await shareMultiple(uris, {
|
|
115
|
+
* dialogTitle: 'Share Photos',
|
|
116
|
+
* mimeType: 'image/jpeg',
|
|
117
|
+
* });
|
|
118
|
+
* };
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* MIME Type Utilities:
|
|
122
|
+
* ```typescript
|
|
123
|
+
* import { SharingUtils, MIME_TYPES } from '@umituz/react-native-sharing';
|
|
124
|
+
*
|
|
125
|
+
* // Get MIME type from extension
|
|
126
|
+
* const mimeType = SharingUtils.getMimeTypeFromExtension('document.pdf');
|
|
127
|
+
* // Returns: 'application/pdf'
|
|
128
|
+
*
|
|
129
|
+
* // Get UTI for iOS
|
|
130
|
+
* const uti = SharingUtils.getUTIFromExtension('photo.jpg');
|
|
131
|
+
* // Returns: 'public.jpeg'
|
|
132
|
+
*
|
|
133
|
+
* // Prepare full options
|
|
134
|
+
* const options = SharingUtils.prepareShareOptions('file.pdf', 'Share Document');
|
|
135
|
+
* // Returns: { dialogTitle, mimeType, UTI }
|
|
136
|
+
*
|
|
137
|
+
* // Use predefined MIME types
|
|
138
|
+
* await share(uri, { mimeType: MIME_TYPES.PDF });
|
|
139
|
+
* await share(uri, { mimeType: MIME_TYPES.IMAGE_JPEG });
|
|
140
|
+
* await share(uri, { mimeType: MIME_TYPES.VIDEO_MP4 });
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* Direct Service Usage (Rare):
|
|
144
|
+
* ```typescript
|
|
145
|
+
* import { SharingService } from '@umituz/react-native-sharing';
|
|
146
|
+
*
|
|
147
|
+
* // Check availability
|
|
148
|
+
* const isAvailable = await SharingService.isAvailable();
|
|
149
|
+
*
|
|
150
|
+
* // Share file
|
|
151
|
+
* const result = await SharingService.shareFile(uri, options);
|
|
152
|
+
* if (result.success) {
|
|
153
|
+
* console.log('Shared');
|
|
154
|
+
* } else {
|
|
155
|
+
* console.error(result.error);
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*
|
|
159
|
+
* BENEFITS:
|
|
160
|
+
* - Native system share sheet (OS-provided UI)
|
|
161
|
+
* - Automatic MIME type detection from filename
|
|
162
|
+
* - Platform-specific options (iOS UTI, Android dialogTitle)
|
|
163
|
+
* - Share availability check before attempting
|
|
164
|
+
* - Type-safe share operations
|
|
165
|
+
* - Error handling with state management
|
|
166
|
+
* - Works with all file types
|
|
167
|
+
*
|
|
168
|
+
* USE CASES:
|
|
169
|
+
* - Share photos/videos from gallery
|
|
170
|
+
* - Export reports/documents
|
|
171
|
+
* - Share app-generated content
|
|
172
|
+
* - Social media sharing
|
|
173
|
+
* - File backup/export
|
|
174
|
+
* - Collaborative file sharing
|
|
175
|
+
*
|
|
176
|
+
* @see https://docs.expo.dev/versions/latest/sdk/sharing/
|
|
177
|
+
*/
|
|
178
|
+
|
|
179
|
+
// ============================================================================
|
|
180
|
+
// DOMAIN LAYER - ENTITIES
|
|
181
|
+
// ============================================================================
|
|
182
|
+
|
|
183
|
+
export type {
|
|
184
|
+
ShareOptions,
|
|
185
|
+
ShareResult,
|
|
186
|
+
} from './domain/entities/Share';
|
|
187
|
+
|
|
188
|
+
export {
|
|
189
|
+
MIME_TYPES,
|
|
190
|
+
UTI_TYPES,
|
|
191
|
+
SHARING_CONSTANTS,
|
|
192
|
+
SharingUtils,
|
|
193
|
+
} from './domain/entities/Share';
|
|
194
|
+
|
|
195
|
+
// ============================================================================
|
|
196
|
+
// INFRASTRUCTURE LAYER - SERVICES
|
|
197
|
+
// ============================================================================
|
|
198
|
+
|
|
199
|
+
export { SharingService } from './infrastructure/services/SharingService';
|
|
200
|
+
|
|
201
|
+
// ============================================================================
|
|
202
|
+
// PRESENTATION LAYER - HOOKS
|
|
203
|
+
// ============================================================================
|
|
204
|
+
|
|
205
|
+
export { useSharing } from './presentation/hooks/useSharing';
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sharing Domain - Sharing Service
|
|
3
|
+
*
|
|
4
|
+
* Service for sharing files using expo-sharing.
|
|
5
|
+
* Provides abstraction layer for system share sheet.
|
|
6
|
+
*
|
|
7
|
+
* @domain sharing
|
|
8
|
+
* @layer infrastructure/services
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as Sharing from 'expo-sharing';
|
|
12
|
+
import * as FileSystem from 'expo-file-system/legacy';
|
|
13
|
+
import type { ShareOptions, ShareResult } from '../../domain/entities/Share';
|
|
14
|
+
import { SharingUtils } from '../../domain/entities/Share';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sharing service for sharing files via system share sheet
|
|
18
|
+
*/
|
|
19
|
+
export class SharingService {
|
|
20
|
+
/**
|
|
21
|
+
* Check if sharing is available on device
|
|
22
|
+
*/
|
|
23
|
+
static async isAvailable(): Promise<boolean> {
|
|
24
|
+
try {
|
|
25
|
+
return await Sharing.isAvailableAsync();
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Share a file via system share sheet
|
|
33
|
+
*
|
|
34
|
+
* @param uri - File URI to share (can be local or remote http/https)
|
|
35
|
+
* @param options - Share options (dialog title, MIME type, UTI)
|
|
36
|
+
* @returns ShareResult with success status
|
|
37
|
+
*
|
|
38
|
+
* USAGE:
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Basic share (local)
|
|
41
|
+
* await SharingService.shareFile('file:///path/to/image.jpg');
|
|
42
|
+
*
|
|
43
|
+
* // Remote file (will be downloaded)
|
|
44
|
+
* await SharingService.shareFile('https://example.com/image.jpg');
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
static async shareFile(uri: string, options?: ShareOptions): Promise<ShareResult> {
|
|
48
|
+
try {
|
|
49
|
+
// Check availability
|
|
50
|
+
const available = await SharingService.isAvailable();
|
|
51
|
+
if (!available) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: 'Sharing is not available on this device',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let shareUri = uri;
|
|
59
|
+
|
|
60
|
+
// Handle remote URLs
|
|
61
|
+
if (uri.startsWith('http://') || uri.startsWith('https://')) {
|
|
62
|
+
try {
|
|
63
|
+
const filename = uri.split('/').pop()?.split('?')[0] || `share-${Date.now()}`;
|
|
64
|
+
// Ensure we have an extension if possible, or default to bin/jpg?
|
|
65
|
+
// Better to rely on what we have, or let the caller specify mimeType to infer extension?
|
|
66
|
+
// For now, nice and simple:
|
|
67
|
+
const localUri = `${FileSystem.cacheDirectory}${filename}`;
|
|
68
|
+
const { uri: downloadedUri, status } = await FileSystem.downloadAsync(uri, localUri);
|
|
69
|
+
|
|
70
|
+
if (status !== 200) {
|
|
71
|
+
return { success: false, error: `Failed to download file: ${status}` };
|
|
72
|
+
}
|
|
73
|
+
shareUri = downloadedUri;
|
|
74
|
+
} catch (downloadError) {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: downloadError instanceof Error ? downloadError.message : 'Failed to download file'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Share file
|
|
83
|
+
await Sharing.shareAsync(shareUri, options);
|
|
84
|
+
|
|
85
|
+
return { success: true };
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: error instanceof Error ? error.message : 'Failed to share file',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Share a file with automatic MIME type detection
|
|
96
|
+
*
|
|
97
|
+
* @param uri - File URI to share
|
|
98
|
+
* @param filename - Filename (used for MIME type detection)
|
|
99
|
+
* @param dialogTitle - Optional dialog title (Android)
|
|
100
|
+
* @returns ShareResult with success status
|
|
101
|
+
*
|
|
102
|
+
* USAGE:
|
|
103
|
+
* ```typescript
|
|
104
|
+
* // Auto-detect MIME type from filename
|
|
105
|
+
* await SharingService.shareWithAutoType(
|
|
106
|
+
* 'file:///path/to/file.jpg',
|
|
107
|
+
* 'photo.jpg',
|
|
108
|
+
* 'Share Photo'
|
|
109
|
+
* );
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
static async shareWithAutoType(
|
|
113
|
+
uri: string,
|
|
114
|
+
filename: string,
|
|
115
|
+
dialogTitle?: string
|
|
116
|
+
): Promise<ShareResult> {
|
|
117
|
+
const options = SharingUtils.prepareShareOptions(filename, dialogTitle);
|
|
118
|
+
return await SharingService.shareFile(uri, options);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Share multiple files (if supported)
|
|
123
|
+
*
|
|
124
|
+
* NOTE: expo-sharing currently only supports sharing one file at a time.
|
|
125
|
+
* This method shares files sequentially.
|
|
126
|
+
*
|
|
127
|
+
* @param uris - Array of file URIs to share
|
|
128
|
+
* @param options - Share options
|
|
129
|
+
* @returns ShareResult with success status
|
|
130
|
+
*/
|
|
131
|
+
static async shareMultipleFiles(
|
|
132
|
+
uris: string[],
|
|
133
|
+
options?: ShareOptions
|
|
134
|
+
): Promise<ShareResult> {
|
|
135
|
+
try {
|
|
136
|
+
if (uris.length === 0) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
error: 'No files to share',
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check availability
|
|
144
|
+
const available = await SharingService.isAvailable();
|
|
145
|
+
if (!available) {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
error: 'Sharing is not available on this device',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Share files sequentially
|
|
153
|
+
for (const uri of uris) {
|
|
154
|
+
await Sharing.shareAsync(uri, options);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { success: true };
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
error: error instanceof Error ? error.message : 'Failed to share files',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sharing Domain - useSharing Hook
|
|
3
|
+
*
|
|
4
|
+
* React hook for sharing files.
|
|
5
|
+
* Provides system share sheet functionality with state management.
|
|
6
|
+
*
|
|
7
|
+
* @domain sharing
|
|
8
|
+
* @layer presentation/hooks
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
12
|
+
import { SharingService } from '../../infrastructure/services/SharingService';
|
|
13
|
+
import type { ShareOptions } from '../../domain/entities/Share';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* useSharing hook for sharing files via system share sheet
|
|
17
|
+
*
|
|
18
|
+
* USAGE:
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const { share, shareWithAutoType, isAvailable, isSharing } = useSharing();
|
|
21
|
+
*
|
|
22
|
+
* // Check availability
|
|
23
|
+
* if (!isAvailable) {
|
|
24
|
+
* return <Text>Sharing not available</Text>;
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* // Basic share
|
|
28
|
+
* const handleShare = async () => {
|
|
29
|
+
* await share('file:///path/to/file.jpg', {
|
|
30
|
+
* dialogTitle: 'Share Photo',
|
|
31
|
+
* mimeType: 'image/jpeg',
|
|
32
|
+
* });
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* // Auto-detect MIME type
|
|
36
|
+
* const handleShareAuto = async () => {
|
|
37
|
+
* await shareWithAutoType(
|
|
38
|
+
* 'file:///path/to/document.pdf',
|
|
39
|
+
* 'document.pdf',
|
|
40
|
+
* 'Share Document'
|
|
41
|
+
* );
|
|
42
|
+
* };
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export const useSharing = () => {
|
|
46
|
+
const [isAvailable, setIsAvailable] = useState(false);
|
|
47
|
+
const [isSharing, setIsSharing] = useState(false);
|
|
48
|
+
const [error, setError] = useState<string | null>(null);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check sharing availability on mount
|
|
52
|
+
*/
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const checkAvailability = async () => {
|
|
55
|
+
const available = await SharingService.isAvailable();
|
|
56
|
+
setIsAvailable(available);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
checkAvailability();
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Share a file via system share sheet
|
|
64
|
+
*/
|
|
65
|
+
const share = useCallback(async (uri: string, options?: ShareOptions): Promise<boolean> => {
|
|
66
|
+
setIsSharing(true);
|
|
67
|
+
setError(null);
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const result = await SharingService.shareFile(uri, options);
|
|
71
|
+
|
|
72
|
+
if (!result.success) {
|
|
73
|
+
setError(result.error || 'Failed to share file');
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return true;
|
|
78
|
+
} catch (err) {
|
|
79
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to share file';
|
|
80
|
+
setError(errorMessage);
|
|
81
|
+
return false;
|
|
82
|
+
} finally {
|
|
83
|
+
setIsSharing(false);
|
|
84
|
+
}
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Share file with automatic MIME type detection
|
|
89
|
+
*/
|
|
90
|
+
const shareWithAutoType = useCallback(
|
|
91
|
+
async (uri: string, filename: string, dialogTitle?: string): Promise<boolean> => {
|
|
92
|
+
setIsSharing(true);
|
|
93
|
+
setError(null);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const result = await SharingService.shareWithAutoType(uri, filename, dialogTitle);
|
|
97
|
+
|
|
98
|
+
if (!result.success) {
|
|
99
|
+
setError(result.error || 'Failed to share file');
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return true;
|
|
104
|
+
} catch (err) {
|
|
105
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to share file';
|
|
106
|
+
setError(errorMessage);
|
|
107
|
+
return false;
|
|
108
|
+
} finally {
|
|
109
|
+
setIsSharing(false);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
[]
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Share multiple files
|
|
117
|
+
*/
|
|
118
|
+
const shareMultiple = useCallback(
|
|
119
|
+
async (uris: string[], options?: ShareOptions): Promise<boolean> => {
|
|
120
|
+
setIsSharing(true);
|
|
121
|
+
setError(null);
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const result = await SharingService.shareMultipleFiles(uris, options);
|
|
125
|
+
|
|
126
|
+
if (!result.success) {
|
|
127
|
+
setError(result.error || 'Failed to share files');
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return true;
|
|
132
|
+
} catch (err) {
|
|
133
|
+
const errorMessage = err instanceof Error ? err.message : 'Failed to share files';
|
|
134
|
+
setError(errorMessage);
|
|
135
|
+
return false;
|
|
136
|
+
} finally {
|
|
137
|
+
setIsSharing(false);
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
[]
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
// Functions
|
|
145
|
+
share,
|
|
146
|
+
shareWithAutoType,
|
|
147
|
+
shareMultiple,
|
|
148
|
+
|
|
149
|
+
// State
|
|
150
|
+
isAvailable,
|
|
151
|
+
isSharing,
|
|
152
|
+
error,
|
|
153
|
+
};
|
|
154
|
+
};
|