@umituz/react-native-design-system 2.8.8 → 2.8.9
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 +6 -1
- package/src/exports/media.ts +1 -0
- package/src/index.ts +5 -0
- package/src/media/domain/entities/CardMultimedia.types.README.md +129 -0
- package/src/media/domain/entities/CardMultimedia.types.ts +105 -0
- package/src/media/domain/entities/Media.README.md +80 -0
- package/src/media/domain/entities/Media.ts +139 -0
- package/src/media/domain/entities/MultimediaFlashcardTypes.README.md +144 -0
- package/src/media/domain/entities/MultimediaFlashcardTypes.ts +105 -0
- package/src/media/domain/utils/MediaUtils.README.md +178 -0
- package/src/media/domain/utils/MediaUtils.ts +82 -0
- package/src/media/index.ts +70 -0
- package/src/media/index.ts.README.md +191 -0
- package/src/media/infrastructure/services/CardMediaGenerationService.README.md +99 -0
- package/src/media/infrastructure/services/CardMediaGenerationService.ts +101 -0
- package/src/media/infrastructure/services/CardMediaOptimizerService.README.md +167 -0
- package/src/media/infrastructure/services/CardMediaOptimizerService.ts +36 -0
- package/src/media/infrastructure/services/CardMediaUploadService.README.md +123 -0
- package/src/media/infrastructure/services/CardMediaUploadService.ts +67 -0
- package/src/media/infrastructure/services/CardMediaValidationService.README.md +134 -0
- package/src/media/infrastructure/services/CardMediaValidationService.ts +81 -0
- package/src/media/infrastructure/services/CardMultimediaService.README.md +176 -0
- package/src/media/infrastructure/services/CardMultimediaService.ts +97 -0
- package/src/media/infrastructure/services/MediaGenerationService.README.md +142 -0
- package/src/media/infrastructure/services/MediaGenerationService.ts +80 -0
- package/src/media/infrastructure/services/MediaOptimizerService.README.md +145 -0
- package/src/media/infrastructure/services/MediaOptimizerService.ts +32 -0
- package/src/media/infrastructure/services/MediaPickerService.README.md +106 -0
- package/src/media/infrastructure/services/MediaPickerService.ts +173 -0
- package/src/media/infrastructure/services/MediaSaveService.README.md +120 -0
- package/src/media/infrastructure/services/MediaSaveService.ts +154 -0
- package/src/media/infrastructure/services/MediaUploadService.README.md +135 -0
- package/src/media/infrastructure/services/MediaUploadService.ts +62 -0
- package/src/media/infrastructure/services/MediaValidationService.README.md +135 -0
- package/src/media/infrastructure/services/MediaValidationService.ts +61 -0
- package/src/media/infrastructure/services/MultimediaFlashcardService.README.md +142 -0
- package/src/media/infrastructure/services/MultimediaFlashcardService.ts +95 -0
- package/src/media/infrastructure/utils/mediaHelpers.README.md +96 -0
- package/src/media/infrastructure/utils/mediaHelpers.ts +82 -0
- package/src/media/infrastructure/utils/mediaPickerMappers.README.md +129 -0
- package/src/media/infrastructure/utils/mediaPickerMappers.ts +76 -0
- package/src/media/presentation/hooks/card-multimedia.types.README.md +177 -0
- package/src/media/presentation/hooks/card-multimedia.types.ts +51 -0
- package/src/media/presentation/hooks/multimedia.types.README.md +201 -0
- package/src/media/presentation/hooks/multimedia.types.ts +51 -0
- package/src/media/presentation/hooks/useCardMediaGeneration.README.md +164 -0
- package/src/media/presentation/hooks/useCardMediaGeneration.ts +124 -0
- package/src/media/presentation/hooks/useCardMediaUpload.README.md +153 -0
- package/src/media/presentation/hooks/useCardMediaUpload.ts +86 -0
- package/src/media/presentation/hooks/useCardMediaValidation.README.md +176 -0
- package/src/media/presentation/hooks/useCardMediaValidation.ts +101 -0
- package/src/media/presentation/hooks/useCardMultimediaFlashcard.README.md +158 -0
- package/src/media/presentation/hooks/useCardMultimediaFlashcard.ts +104 -0
- package/src/media/presentation/hooks/useMedia.README.md +94 -0
- package/src/media/presentation/hooks/useMedia.ts +186 -0
- package/src/media/presentation/hooks/useMediaGeneration.README.md +118 -0
- package/src/media/presentation/hooks/useMediaGeneration.ts +101 -0
- package/src/media/presentation/hooks/useMediaUpload.README.md +108 -0
- package/src/media/presentation/hooks/useMediaUpload.ts +86 -0
- package/src/media/presentation/hooks/useMediaValidation.README.md +134 -0
- package/src/media/presentation/hooks/useMediaValidation.ts +93 -0
- package/src/media/presentation/hooks/useMultimediaFlashcard.README.md +141 -0
- package/src/media/presentation/hooks/useMultimediaFlashcard.ts +103 -0
- package/src/storage/infrastructure/repositories/__tests__/AsyncStorageRepository.test.ts +1 -1
- package/src/storage/infrastructure/repositories/__tests__/BaseStorageOperations.test.ts +1 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media Generation Service
|
|
3
|
+
* Handles AI media generation (text-to-image, text-to-audio)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
MediaAttachment,
|
|
8
|
+
MediaGenerationRequest,
|
|
9
|
+
MediaGenerationResult,
|
|
10
|
+
MediaType,
|
|
11
|
+
MediaPosition,
|
|
12
|
+
} from "../../domain/entities/MultimediaFlashcardTypes";
|
|
13
|
+
|
|
14
|
+
export class MediaGenerationService {
|
|
15
|
+
/**
|
|
16
|
+
* Generate media from AI (text-to-image, text-to-audio, etc.)
|
|
17
|
+
*/
|
|
18
|
+
async generateMedia(
|
|
19
|
+
request: MediaGenerationRequest,
|
|
20
|
+
): Promise<MediaGenerationResult> {
|
|
21
|
+
try {
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
|
|
24
|
+
// Simulate AI generation
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
26
|
+
|
|
27
|
+
const attachments: MediaAttachment[] = [];
|
|
28
|
+
|
|
29
|
+
switch (request.type) {
|
|
30
|
+
case "text_to_image":
|
|
31
|
+
for (let i = 0; i < (request.options.maxResults || 1); i++) {
|
|
32
|
+
attachments.push({
|
|
33
|
+
id: `ai_img_${Date.now()}_${i}`,
|
|
34
|
+
type: "image" as MediaType,
|
|
35
|
+
position: "both" as MediaPosition,
|
|
36
|
+
url: `https://picsum.photos/400/300?random=${Date.now() + i}`,
|
|
37
|
+
filename: `ai_generated_${i}.jpg`,
|
|
38
|
+
fileSize: 150000,
|
|
39
|
+
mimeType: "image/jpeg",
|
|
40
|
+
isDownloaded: false,
|
|
41
|
+
createdAt: new Date().toISOString(),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case "text_to_audio":
|
|
47
|
+
attachments.push({
|
|
48
|
+
id: `ai_audio_${Date.now()}`,
|
|
49
|
+
type: "audio" as MediaType,
|
|
50
|
+
position: "back" as MediaPosition,
|
|
51
|
+
url: `https://example.com/audio_${Date.now()}.mp3`,
|
|
52
|
+
filename: `ai_generated_${Date.now()}.mp3`,
|
|
53
|
+
fileSize: 80000,
|
|
54
|
+
mimeType: "audio/mp3",
|
|
55
|
+
duration: 10,
|
|
56
|
+
isDownloaded: false,
|
|
57
|
+
createdAt: new Date().toISOString(),
|
|
58
|
+
});
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
attachments,
|
|
65
|
+
creditsUsed: request.type === "text_to_image" ? 5 : 3,
|
|
66
|
+
processingTime: Date.now() - startTime,
|
|
67
|
+
requestId: `req_${Date.now()}`,
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
attachments: [],
|
|
73
|
+
creditsUsed: 0,
|
|
74
|
+
processingTime: 0,
|
|
75
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
76
|
+
requestId: "",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# MediaOptimizerService
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
Optimizes and compresses media files to reduce file size while maintaining acceptable quality, and manages media deletion operations.
|
|
5
|
+
|
|
6
|
+
## File Location
|
|
7
|
+
`/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-media/src/infrastructure/services/MediaOptimizerService`
|
|
8
|
+
|
|
9
|
+
## Strategy
|
|
10
|
+
|
|
11
|
+
### Core Purpose
|
|
12
|
+
- Reduce media file size through compression
|
|
13
|
+
- Maintain acceptable quality after optimization
|
|
14
|
+
- Support quality and dimension constraints
|
|
15
|
+
- Delete media from server and local storage
|
|
16
|
+
- Generate optimized versions of existing media
|
|
17
|
+
|
|
18
|
+
### Usage Scenarios
|
|
19
|
+
- Pre-upload optimization
|
|
20
|
+
- Storage cost reduction
|
|
21
|
+
- Performance improvement
|
|
22
|
+
- Bandwidth optimization
|
|
23
|
+
- Media cleanup workflows
|
|
24
|
+
|
|
25
|
+
### Integration Points
|
|
26
|
+
- After upload, before storage
|
|
27
|
+
- In media management workflows
|
|
28
|
+
- Storage optimization processes
|
|
29
|
+
- Media cleanup routines
|
|
30
|
+
- Performance tuning operations
|
|
31
|
+
|
|
32
|
+
## Forbidden
|
|
33
|
+
|
|
34
|
+
### MUST NOT
|
|
35
|
+
- Optimize media beyond usable quality
|
|
36
|
+
- Delete media without proper authorization
|
|
37
|
+
- Assume optimization always succeeds
|
|
38
|
+
- Modify original media files
|
|
39
|
+
- Ignore quality settings
|
|
40
|
+
- Delete without confirmation in critical workflows
|
|
41
|
+
|
|
42
|
+
### MUST NEVER
|
|
43
|
+
- Optimize already optimized media repeatedly
|
|
44
|
+
- Use quality below 0.3 without explicit request
|
|
45
|
+
- Delete media without backup when required
|
|
46
|
+
- Assume optimization is lossless
|
|
47
|
+
- Ignore file size after optimization
|
|
48
|
+
- Return different media ID after optimization
|
|
49
|
+
|
|
50
|
+
## Rules
|
|
51
|
+
|
|
52
|
+
### Optimization Operations
|
|
53
|
+
- MUST accept media attachment and options
|
|
54
|
+
- MUST preserve original media (create new version)
|
|
55
|
+
- MUST return optimized media attachment
|
|
56
|
+
- MUST maintain same media ID
|
|
57
|
+
- MUST update file size accurately
|
|
58
|
+
- MUST add optimization indicator to URL
|
|
59
|
+
|
|
60
|
+
### Compression Options
|
|
61
|
+
- MUST support quality adjustment (0-1)
|
|
62
|
+
- MUST support max width constraint
|
|
63
|
+
- MUST support max height constraint
|
|
64
|
+
- MUST support format conversion
|
|
65
|
+
- MUST validate compression parameters
|
|
66
|
+
- MUST respect quality vs size tradeoff
|
|
67
|
+
|
|
68
|
+
### Quality Levels
|
|
69
|
+
- High quality (0.9-1.0): 10-20% size reduction
|
|
70
|
+
- Medium quality (0.7-0.8): 30-50% size reduction
|
|
71
|
+
- Low quality (0.5-0.6): 50-70% size reduction
|
|
72
|
+
- Preview quality (0.3-0.4): 70-80% size reduction
|
|
73
|
+
- MUST document quality tradeoffs
|
|
74
|
+
|
|
75
|
+
### Deletion Operations
|
|
76
|
+
- MUST accept media ID for deletion
|
|
77
|
+
- MUST delete from both server and local storage
|
|
78
|
+
- MUST handle non-existent media gracefully
|
|
79
|
+
- MUST confirm deletion success
|
|
80
|
+
- MUST be permanent and irreversible
|
|
81
|
+
|
|
82
|
+
### Return Values
|
|
83
|
+
- MUST preserve original media ID
|
|
84
|
+
- MUST preserve media type
|
|
85
|
+
- MUST update file size
|
|
86
|
+
- MUST preserve MIME type
|
|
87
|
+
- MUST preserve creation date
|
|
88
|
+
- MUST update URL with optimization indicator
|
|
89
|
+
|
|
90
|
+
### Error Handling
|
|
91
|
+
- MUST handle optimization failures
|
|
92
|
+
- MUST handle deletion failures
|
|
93
|
+
- MUST handle invalid media IDs
|
|
94
|
+
- MUST handle invalid compression options
|
|
95
|
+
- MUST provide meaningful error messages
|
|
96
|
+
- MUST allow retry on transient failures
|
|
97
|
+
|
|
98
|
+
## AI Agent Guidelines
|
|
99
|
+
|
|
100
|
+
### When Implementing Optimization
|
|
101
|
+
1. Always validate compression options
|
|
102
|
+
2. Calculate expected file size reduction
|
|
103
|
+
3. Apply compression with specified quality
|
|
104
|
+
4. Update metadata accurately
|
|
105
|
+
5. Return optimized version with same ID
|
|
106
|
+
|
|
107
|
+
### When Working with Quality Settings
|
|
108
|
+
- Use 0.7-0.8 for general use (recommended)
|
|
109
|
+
- Use 0.9-1.0 for high-quality requirements
|
|
110
|
+
- Use 0.5-0.6 for previews and thumbnails
|
|
111
|
+
- Avoid quality below 0.3 unless specifically needed
|
|
112
|
+
- Consider content type when setting quality
|
|
113
|
+
|
|
114
|
+
### When Performing Deletion
|
|
115
|
+
- Confirm media ID exists before deletion
|
|
116
|
+
- Handle deletion from both storage locations
|
|
117
|
+
- Provide confirmation in critical workflows
|
|
118
|
+
- Log deletion operations for audit
|
|
119
|
+
- Handle non-existent media gracefully
|
|
120
|
+
|
|
121
|
+
### When Adding Features
|
|
122
|
+
- Add new compression formats carefully
|
|
123
|
+
- Support new dimension constraints
|
|
124
|
+
- Add batch optimization if needed
|
|
125
|
+
- Maintain backward compatibility
|
|
126
|
+
- Add optimization presets for common cases
|
|
127
|
+
|
|
128
|
+
### When Refactoring
|
|
129
|
+
- Keep optimization API stable
|
|
130
|
+
- Preserve ID generation logic
|
|
131
|
+
- Maintain URL structure
|
|
132
|
+
- Don't change return value structure
|
|
133
|
+
- Add deprecation warnings for breaking changes
|
|
134
|
+
|
|
135
|
+
### Common Patterns to Follow
|
|
136
|
+
- Receive attachment -> Validate options -> Optimize -> Return optimized
|
|
137
|
+
- Calculate size -> Apply quality -> Update metadata -> Return
|
|
138
|
+
- Receive ID -> Validate -> Delete -> Confirm
|
|
139
|
+
- Always wrap in try-catch for unexpected errors
|
|
140
|
+
- Provide user feedback for optimization progress
|
|
141
|
+
|
|
142
|
+
## Dependencies
|
|
143
|
+
|
|
144
|
+
- Domain types: MediaAttachment, MediaCompressionOptions from MultimediaFlashcardTypes
|
|
145
|
+
- No external library dependencies (uses native APIs)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media Optimizer Service
|
|
3
|
+
* Handles media optimization and deletion
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
MediaAttachment,
|
|
8
|
+
MediaCompressionOptions,
|
|
9
|
+
} from "../../domain/entities/MultimediaFlashcardTypes";
|
|
10
|
+
|
|
11
|
+
export class MediaOptimizerService {
|
|
12
|
+
/**
|
|
13
|
+
* Optimize media file
|
|
14
|
+
*/
|
|
15
|
+
async optimizeMedia(
|
|
16
|
+
attachment: MediaAttachment,
|
|
17
|
+
options: MediaCompressionOptions,
|
|
18
|
+
): Promise<MediaAttachment> {
|
|
19
|
+
return {
|
|
20
|
+
...attachment,
|
|
21
|
+
fileSize: Math.floor(attachment.fileSize * options.quality),
|
|
22
|
+
url: `${attachment.url}?optimized=true`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Delete media attachment
|
|
28
|
+
*/
|
|
29
|
+
async deleteMedia(_attachmentId: string): Promise<void> {
|
|
30
|
+
// Mock implementation
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# MediaPickerService
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
Provides image and video selection functionality from device camera and gallery, including permission management and media type filtering.
|
|
5
|
+
|
|
6
|
+
## File Location
|
|
7
|
+
`/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-media/src/infrastructure/services/MediaPickerService`
|
|
8
|
+
|
|
9
|
+
## Strategy
|
|
10
|
+
|
|
11
|
+
### Core Purpose
|
|
12
|
+
- Enable users to select media from device gallery or capture using camera
|
|
13
|
+
- Support single and multiple media selection
|
|
14
|
+
- Handle platform-specific permission requirements
|
|
15
|
+
- Provide customizable selection options
|
|
16
|
+
|
|
17
|
+
### Usage Scenarios
|
|
18
|
+
- User profile picture selection
|
|
19
|
+
- Content creation workflows
|
|
20
|
+
- Multi-image selection for galleries
|
|
21
|
+
- Video recording for user-generated content
|
|
22
|
+
- Mixed media selection (images and videos)
|
|
23
|
+
|
|
24
|
+
### Integration Points
|
|
25
|
+
- Forms requiring media attachments
|
|
26
|
+
- Content management systems
|
|
27
|
+
- Social media features
|
|
28
|
+
- Document upload workflows
|
|
29
|
+
|
|
30
|
+
## Forbidden
|
|
31
|
+
|
|
32
|
+
### MUST NOT
|
|
33
|
+
- Bypass permission checks or force camera/gallery access
|
|
34
|
+
- Assume permissions are always granted
|
|
35
|
+
- Hardcode file paths or URIs
|
|
36
|
+
- Access media without explicit user action
|
|
37
|
+
- Automatically launch camera without user consent
|
|
38
|
+
- Store selected media URIs in permanent storage without validation
|
|
39
|
+
|
|
40
|
+
### MUST NEVER
|
|
41
|
+
- Allow infinite selection limits without boundaries
|
|
42
|
+
- Use base64 conversion by default (performance impact)
|
|
43
|
+
- Assume all devices support the same media types
|
|
44
|
+
- Ignore permission denial states
|
|
45
|
+
- Access camera roll without proper permissions
|
|
46
|
+
|
|
47
|
+
## Rules
|
|
48
|
+
|
|
49
|
+
### Permission Management
|
|
50
|
+
- MUST request camera permission before launching camera
|
|
51
|
+
- MUST request media library permission before accessing gallery
|
|
52
|
+
- MUST handle permission denial gracefully
|
|
53
|
+
- MUST check permission status before operations
|
|
54
|
+
- MUST provide clear error messages when permissions are denied
|
|
55
|
+
|
|
56
|
+
### Media Selection
|
|
57
|
+
- MUST respect selection limits
|
|
58
|
+
- MUST validate selected media before processing
|
|
59
|
+
- MUST handle user cancellation (canceled: true)
|
|
60
|
+
- MUST support both single and multiple selection modes
|
|
61
|
+
- MUST filter media types appropriately
|
|
62
|
+
|
|
63
|
+
### Platform Considerations
|
|
64
|
+
- MUST handle iOS and Android permission differences
|
|
65
|
+
- MUST account for platform-specific media type restrictions
|
|
66
|
+
- MUST test on both platforms for compatibility
|
|
67
|
+
- MUST handle aspect ratio options correctly (only works with allowsEditing: true)
|
|
68
|
+
|
|
69
|
+
### Performance
|
|
70
|
+
- MUST avoid base64 conversion unless explicitly required
|
|
71
|
+
- MUST set reasonable quality defaults
|
|
72
|
+
- MUST handle large media files appropriately
|
|
73
|
+
- MUST provide loading states during selection
|
|
74
|
+
|
|
75
|
+
## AI Agent Guidelines
|
|
76
|
+
|
|
77
|
+
### When Implementing Media Selection
|
|
78
|
+
1. Always check permissions before launching picker
|
|
79
|
+
2. Handle both success (canceled: false) and cancellation (canceled: true)
|
|
80
|
+
3. Validate returned assets array exists and has items
|
|
81
|
+
4. Consider media type (image vs video) for different handling
|
|
82
|
+
5. Use appropriate quality settings based on use case
|
|
83
|
+
|
|
84
|
+
### When Adding Features
|
|
85
|
+
- Add new permission types only if required by new functionality
|
|
86
|
+
- Extend options interface without breaking existing functionality
|
|
87
|
+
- Maintain backward compatibility with existing selection methods
|
|
88
|
+
- Add error handling for new edge cases
|
|
89
|
+
|
|
90
|
+
### When Refactoring
|
|
91
|
+
- Keep public API stable
|
|
92
|
+
- Preserve permission handling logic
|
|
93
|
+
- Maintain platform-specific behavior differences
|
|
94
|
+
- Don't remove existing selection methods without deprecation
|
|
95
|
+
|
|
96
|
+
### Common Patterns to Follow
|
|
97
|
+
- Request permission -> Check status -> Launch picker
|
|
98
|
+
- Handle result -> Validate -> Return or process
|
|
99
|
+
- Always wrap in try-catch for unexpected errors
|
|
100
|
+
- Provide user feedback for permission states
|
|
101
|
+
|
|
102
|
+
## Dependencies
|
|
103
|
+
|
|
104
|
+
- Domain types: MediaPickerOptions, MediaPickerResult, CameraOptions, MediaType, MediaLibraryPermission, MEDIA_CONSTANTS from Media
|
|
105
|
+
- External libraries: expo-image-picker
|
|
106
|
+
- Internal utilities: mediaPickerMappers (mapMediaType, mapPermissionStatus, mapPickerResult)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media Domain - Media Picker Service
|
|
3
|
+
*
|
|
4
|
+
* Service for picking images/videos using expo-image-picker.
|
|
5
|
+
* Handles camera, gallery, and media library permissions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as ImagePicker from "expo-image-picker";
|
|
9
|
+
import type {
|
|
10
|
+
MediaPickerOptions,
|
|
11
|
+
MediaPickerResult,
|
|
12
|
+
CameraOptions,
|
|
13
|
+
} from "../../domain/entities/Media";
|
|
14
|
+
import {
|
|
15
|
+
MediaLibraryPermission,
|
|
16
|
+
MediaType,
|
|
17
|
+
MEDIA_CONSTANTS,
|
|
18
|
+
} from "../../domain/entities/Media";
|
|
19
|
+
import {
|
|
20
|
+
mapMediaType,
|
|
21
|
+
mapPermissionStatus,
|
|
22
|
+
mapPickerResult,
|
|
23
|
+
} from "../utils/mediaPickerMappers";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Media picker service for selecting images/videos
|
|
27
|
+
*/
|
|
28
|
+
export class MediaPickerService {
|
|
29
|
+
static async requestCameraPermission(): Promise<MediaLibraryPermission> {
|
|
30
|
+
try {
|
|
31
|
+
const { status } = await ImagePicker.requestCameraPermissionsAsync();
|
|
32
|
+
return mapPermissionStatus(status);
|
|
33
|
+
} catch {
|
|
34
|
+
return MediaLibraryPermission.DENIED;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static async requestMediaLibraryPermission(): Promise<MediaLibraryPermission> {
|
|
39
|
+
try {
|
|
40
|
+
const { status } =
|
|
41
|
+
await ImagePicker.requestMediaLibraryPermissionsAsync();
|
|
42
|
+
return mapPermissionStatus(status);
|
|
43
|
+
} catch {
|
|
44
|
+
return MediaLibraryPermission.DENIED;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static async getCameraPermissionStatus(): Promise<MediaLibraryPermission> {
|
|
49
|
+
try {
|
|
50
|
+
const { status } = await ImagePicker.getCameraPermissionsAsync();
|
|
51
|
+
return mapPermissionStatus(status);
|
|
52
|
+
} catch {
|
|
53
|
+
return MediaLibraryPermission.DENIED;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static async getMediaLibraryPermissionStatus(): Promise<MediaLibraryPermission> {
|
|
58
|
+
try {
|
|
59
|
+
const { status } = await ImagePicker.getMediaLibraryPermissionsAsync();
|
|
60
|
+
return mapPermissionStatus(status);
|
|
61
|
+
} catch {
|
|
62
|
+
return MediaLibraryPermission.DENIED;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static async launchCamera(
|
|
67
|
+
options?: CameraOptions
|
|
68
|
+
): Promise<MediaPickerResult> {
|
|
69
|
+
try {
|
|
70
|
+
const permission = await MediaPickerService.requestCameraPermission();
|
|
71
|
+
if (permission === MediaLibraryPermission.DENIED) {
|
|
72
|
+
return { canceled: true };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const result = await ImagePicker.launchCameraAsync({
|
|
76
|
+
mediaTypes: ["images"],
|
|
77
|
+
allowsEditing: options?.allowsEditing ?? false,
|
|
78
|
+
aspect: options?.aspect,
|
|
79
|
+
quality: options?.quality ?? MEDIA_CONSTANTS.DEFAULT_QUALITY,
|
|
80
|
+
base64: options?.base64 ?? false,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return mapPickerResult(result);
|
|
84
|
+
} catch {
|
|
85
|
+
return { canceled: true };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static async launchCameraForVideo(
|
|
90
|
+
options?: CameraOptions
|
|
91
|
+
): Promise<MediaPickerResult> {
|
|
92
|
+
try {
|
|
93
|
+
const permission = await MediaPickerService.requestCameraPermission();
|
|
94
|
+
if (permission === MediaLibraryPermission.DENIED) {
|
|
95
|
+
return { canceled: true };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const result = await ImagePicker.launchCameraAsync({
|
|
99
|
+
mediaTypes: ["videos"],
|
|
100
|
+
allowsEditing: options?.allowsEditing ?? false,
|
|
101
|
+
quality: options?.quality ?? MEDIA_CONSTANTS.DEFAULT_QUALITY,
|
|
102
|
+
videoMaxDuration: options?.videoMaxDuration,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return mapPickerResult(result);
|
|
106
|
+
} catch {
|
|
107
|
+
return { canceled: true };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static async pickImage(
|
|
112
|
+
options?: MediaPickerOptions
|
|
113
|
+
): Promise<MediaPickerResult> {
|
|
114
|
+
try {
|
|
115
|
+
const permission =
|
|
116
|
+
await MediaPickerService.requestMediaLibraryPermission();
|
|
117
|
+
if (permission === MediaLibraryPermission.DENIED) {
|
|
118
|
+
return { canceled: true };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const result = await ImagePicker.launchImageLibraryAsync({
|
|
122
|
+
mediaTypes: mapMediaType(options?.mediaTypes),
|
|
123
|
+
allowsEditing: options?.allowsEditing ?? false,
|
|
124
|
+
allowsMultipleSelection: options?.allowsMultipleSelection ?? false,
|
|
125
|
+
aspect: options?.aspect,
|
|
126
|
+
quality: options?.quality ?? MEDIA_CONSTANTS.DEFAULT_QUALITY,
|
|
127
|
+
selectionLimit:
|
|
128
|
+
options?.selectionLimit ?? MEDIA_CONSTANTS.DEFAULT_SELECTION_LIMIT,
|
|
129
|
+
base64: options?.base64 ?? false,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return mapPickerResult(result);
|
|
133
|
+
} catch {
|
|
134
|
+
return { canceled: true };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static async pickSingleImage(
|
|
139
|
+
options?: Omit<MediaPickerOptions, "allowsMultipleSelection">
|
|
140
|
+
): Promise<MediaPickerResult> {
|
|
141
|
+
return MediaPickerService.pickImage({
|
|
142
|
+
...options,
|
|
143
|
+
allowsMultipleSelection: false,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static async pickMultipleImages(
|
|
148
|
+
options?: Omit<MediaPickerOptions, "allowsMultipleSelection">
|
|
149
|
+
): Promise<MediaPickerResult> {
|
|
150
|
+
return MediaPickerService.pickImage({
|
|
151
|
+
...options,
|
|
152
|
+
allowsMultipleSelection: true,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static async pickVideo(
|
|
157
|
+
options?: Omit<MediaPickerOptions, "mediaTypes">
|
|
158
|
+
): Promise<MediaPickerResult> {
|
|
159
|
+
return MediaPickerService.pickImage({
|
|
160
|
+
...options,
|
|
161
|
+
mediaTypes: MediaType.VIDEO,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
static async pickMedia(
|
|
166
|
+
options?: MediaPickerOptions
|
|
167
|
+
): Promise<MediaPickerResult> {
|
|
168
|
+
return MediaPickerService.pickImage({
|
|
169
|
+
...options,
|
|
170
|
+
mediaTypes: MediaType.ALL,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# MediaSaveService
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
Handles saving image and video files to the device's photo gallery, including album management and permission handling.
|
|
5
|
+
|
|
6
|
+
## File Location
|
|
7
|
+
`/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-media/src/infrastructure/services/MediaSaveService`
|
|
8
|
+
|
|
9
|
+
## Strategy
|
|
10
|
+
|
|
11
|
+
### Core Purpose
|
|
12
|
+
- Save media files to device gallery
|
|
13
|
+
- Create and manage custom albums
|
|
14
|
+
- Handle platform-specific save requirements
|
|
15
|
+
- Manage write permissions for media library
|
|
16
|
+
|
|
17
|
+
### Usage Scenarios
|
|
18
|
+
- Saving downloaded images to gallery
|
|
19
|
+
- Creating app-specific albums
|
|
20
|
+
- Saving captured media to gallery
|
|
21
|
+
- Organizing media into custom collections
|
|
22
|
+
- Backup workflows
|
|
23
|
+
|
|
24
|
+
### Integration Points
|
|
25
|
+
- After media download completes
|
|
26
|
+
- After media generation/creation
|
|
27
|
+
- User-initiated save actions
|
|
28
|
+
- Batch media save operations
|
|
29
|
+
|
|
30
|
+
## Forbidden
|
|
31
|
+
|
|
32
|
+
### MUST NOT
|
|
33
|
+
- Save files without proper write permissions
|
|
34
|
+
- Assume album creation always succeeds
|
|
35
|
+
- Create duplicate albums with same name
|
|
36
|
+
- Overwrite existing media without user consent
|
|
37
|
+
- Save invalid or corrupted media files
|
|
38
|
+
- Ignore save operation results
|
|
39
|
+
|
|
40
|
+
### MUST NEVER
|
|
41
|
+
- Attempt to save when permission is denied
|
|
42
|
+
- Assume save operation is instant
|
|
43
|
+
- Save to private app storage using this service
|
|
44
|
+
- Modify saved media after save operation completes
|
|
45
|
+
- Bypass platform-specific save restrictions
|
|
46
|
+
|
|
47
|
+
## Rules
|
|
48
|
+
|
|
49
|
+
### Permission Management
|
|
50
|
+
- MUST request write permission before saving
|
|
51
|
+
- MUST check permission status before operations
|
|
52
|
+
- MUST handle permission denial gracefully
|
|
53
|
+
- MUST provide clear error messages for permission issues
|
|
54
|
+
- MUST respect user's permission decisions
|
|
55
|
+
|
|
56
|
+
### Save Operations
|
|
57
|
+
- MUST validate file URI/path before saving
|
|
58
|
+
- MUST determine media type (image/video) correctly
|
|
59
|
+
- MUST handle save failures appropriately
|
|
60
|
+
- MUST return success/failure status
|
|
61
|
+
- MUST include asset ID in successful results
|
|
62
|
+
|
|
63
|
+
### Album Management
|
|
64
|
+
- MUST create album automatically if it doesn't exist
|
|
65
|
+
- MUST NOT create duplicate albums with same name
|
|
66
|
+
- MUST save to default gallery if no album specified
|
|
67
|
+
- MUST handle album creation failures
|
|
68
|
+
- MUST validate album names
|
|
69
|
+
|
|
70
|
+
### Error Handling
|
|
71
|
+
- MUST catch and report save failures
|
|
72
|
+
- MUST handle invalid file paths
|
|
73
|
+
- MUST handle insufficient storage scenarios
|
|
74
|
+
- MUST provide meaningful error messages
|
|
75
|
+
- MUST allow retry after permission grant
|
|
76
|
+
|
|
77
|
+
### Platform Considerations
|
|
78
|
+
- MUST handle iOS save requirements
|
|
79
|
+
- MUST handle Android scoped storage (API 10+)
|
|
80
|
+
- MUST account for platform-specific restrictions
|
|
81
|
+
- MUST test on both platforms
|
|
82
|
+
|
|
83
|
+
## AI Agent Guidelines
|
|
84
|
+
|
|
85
|
+
### When Implementing Save Operations
|
|
86
|
+
1. Always check permissions before attempting save
|
|
87
|
+
2. Validate file URI/path exists and is accessible
|
|
88
|
+
3. Determine correct media type for the file
|
|
89
|
+
4. Handle both success and failure cases
|
|
90
|
+
5. Provide user feedback for save operations
|
|
91
|
+
|
|
92
|
+
### When Working with Albums
|
|
93
|
+
- Check if album exists before creating
|
|
94
|
+
- Use consistent album naming
|
|
95
|
+
- Handle album creation failures gracefully
|
|
96
|
+
- Allow saving to default gallery as fallback
|
|
97
|
+
|
|
98
|
+
### When Adding Features
|
|
99
|
+
- Add new media types with proper validation
|
|
100
|
+
- Extend save options without breaking changes
|
|
101
|
+
- Maintain backward compatibility
|
|
102
|
+
- Add error handling for new edge cases
|
|
103
|
+
|
|
104
|
+
### When Refactoring
|
|
105
|
+
- Keep public API stable
|
|
106
|
+
- Preserve permission handling logic
|
|
107
|
+
- Maintain platform-specific behavior
|
|
108
|
+
- Don't remove existing save methods without deprecation
|
|
109
|
+
|
|
110
|
+
### Common Patterns to Follow
|
|
111
|
+
- Request permission -> Check status -> Validate file -> Save
|
|
112
|
+
- Handle result -> Check success -> Return asset ID or error
|
|
113
|
+
- Always wrap in try-catch for unexpected errors
|
|
114
|
+
- Provide user feedback for save status
|
|
115
|
+
|
|
116
|
+
## Dependencies
|
|
117
|
+
|
|
118
|
+
- Domain types: MediaType, MediaLibraryPermission from Media
|
|
119
|
+
- External libraries: expo-media-library
|
|
120
|
+
- No internal utility dependencies
|