react-native-ensys-camera-x 1.0.7
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/CustomCamera.podspec +20 -0
- package/LICENSE +20 -0
- package/README.md +833 -0
- package/android/build.gradle +135 -0
- package/android/consumer-rules.pro +47 -0
- package/android/src/main/AndroidManifest.xml +72 -0
- package/android/src/main/java/android/print/PdfPrint.java +51 -0
- package/android/src/main/java/com/customcamera/AnimatedImageViewManager.kt +199 -0
- package/android/src/main/java/com/customcamera/AudioPlayerView.kt +322 -0
- package/android/src/main/java/com/customcamera/AudioRecorder.kt +506 -0
- package/android/src/main/java/com/customcamera/BarcodeScannerActivity.kt +361 -0
- package/android/src/main/java/com/customcamera/BlurBackgroundView.kt +199 -0
- package/android/src/main/java/com/customcamera/CacheCleanupManager.kt +386 -0
- package/android/src/main/java/com/customcamera/CameraAspectRatioMode.kt +77 -0
- package/android/src/main/java/com/customcamera/CameraAsset.kt +211 -0
- package/android/src/main/java/com/customcamera/CameraTheme.kt +479 -0
- package/android/src/main/java/com/customcamera/CompressionCacheStore.kt +504 -0
- package/android/src/main/java/com/customcamera/CompressionFileNameUtils.kt +7 -0
- package/android/src/main/java/com/customcamera/CompressionProfile.kt +195 -0
- package/android/src/main/java/com/customcamera/CustomCameraActivity.kt +7291 -0
- package/android/src/main/java/com/customcamera/CustomCameraFileProvider.kt +9 -0
- package/android/src/main/java/com/customcamera/CustomCameraModule.kt +2479 -0
- package/android/src/main/java/com/customcamera/CustomCameraPackage.kt +53 -0
- package/android/src/main/java/com/customcamera/DisplayUtils.kt +9 -0
- package/android/src/main/java/com/customcamera/DocumentPreviewView.kt +846 -0
- package/android/src/main/java/com/customcamera/DrawingView.kt +214 -0
- package/android/src/main/java/com/customcamera/EmojiPickerData.kt +228 -0
- package/android/src/main/java/com/customcamera/FilterStripAdapter.kt +158 -0
- package/android/src/main/java/com/customcamera/GifSupport.kt +215 -0
- package/android/src/main/java/com/customcamera/GlobalAudioPlayer.kt +320 -0
- package/android/src/main/java/com/customcamera/ImageExporter.kt +1394 -0
- package/android/src/main/java/com/customcamera/ImageFormatSupport.kt +163 -0
- package/android/src/main/java/com/customcamera/Localization.kt +206 -0
- package/android/src/main/java/com/customcamera/MediaAdapters.kt +758 -0
- package/android/src/main/java/com/customcamera/MediaCacheManager.kt +782 -0
- package/android/src/main/java/com/customcamera/MediaExportFormats.kt +201 -0
- package/android/src/main/java/com/customcamera/MediaFileIo.kt +310 -0
- package/android/src/main/java/com/customcamera/MediaMetadataStore.kt +494 -0
- package/android/src/main/java/com/customcamera/MediaPreviewEditor.kt +14865 -0
- package/android/src/main/java/com/customcamera/MediaPublisher.kt +423 -0
- package/android/src/main/java/com/customcamera/MediaStorageWorkflow.kt +722 -0
- package/android/src/main/java/com/customcamera/MediaStoreRepository.kt +378 -0
- package/android/src/main/java/com/customcamera/MediaThumbnailView.kt +286 -0
- package/android/src/main/java/com/customcamera/MediaUtils.kt +37 -0
- package/android/src/main/java/com/customcamera/NativeVideoViewManager.kt +845 -0
- package/android/src/main/java/com/customcamera/OfficeDocumentConverter.kt +2791 -0
- package/android/src/main/java/com/customcamera/PendingCaptureCrops.kt +88 -0
- package/android/src/main/java/com/customcamera/PowerPointPreview.kt +4077 -0
- package/android/src/main/java/com/customcamera/RecentPickerStore.kt +74 -0
- package/android/src/main/java/com/customcamera/SecureFileEraser.kt +411 -0
- package/android/src/main/java/com/customcamera/SecureKeyModule.kt +88 -0
- package/android/src/main/java/com/customcamera/SecureMediaDeleter.kt +493 -0
- package/android/src/main/java/com/customcamera/StickerPickerData.kt +107 -0
- package/android/src/main/java/com/customcamera/SvgDrawable.kt +175 -0
- package/android/src/main/java/com/customcamera/SvgIconViewManager.kt +77 -0
- package/android/src/main/java/com/customcamera/TextEntryController.kt +553 -0
- package/android/src/main/java/com/customcamera/ThumbnailLoader.kt +455 -0
- package/android/src/main/java/com/customcamera/UploadService.kt +909 -0
- package/android/src/main/java/com/customcamera/VideoEditUtils.kt +210 -0
- package/android/src/main/java/com/customcamera/VideoRotationTranscoder.kt +1992 -0
- package/android/src/main/java/com/customcamera/gif/AnimatedGifEncoder.kt +473 -0
- package/android/src/main/java/com/customcamera/gif/GifDecoder.kt +690 -0
- package/android/src/main/java/com/customcamera/gif/GifFramesDrawable.kt +160 -0
- package/android/src/main/java/com/customcamera/gif/GifHttp.kt +112 -0
- package/android/src/main/java/com/customcamera/gif/GifImageLoader.kt +186 -0
- package/android/src/main/java/com/customcamera/gif/GifModels.kt +74 -0
- package/android/src/main/java/com/customcamera/gif/GifProvider.kt +49 -0
- package/android/src/main/java/com/customcamera/gif/GifProviders.kt +27 -0
- package/android/src/main/java/com/customcamera/gif/GifRepository.kt +124 -0
- package/android/src/main/java/com/customcamera/gif/GifSafety.kt +33 -0
- package/android/src/main/java/com/customcamera/gif/GiphyGifProvider.kt +146 -0
- package/android/src/main/java/com/customcamera/gif/LZWEncoder.kt +258 -0
- package/android/src/main/java/com/customcamera/gif/NeuQuant.kt +435 -0
- package/android/src/main/java/com/customcamera/gif/TenorGifProvider.kt +147 -0
- package/android/src/main/res/drawable/bg_brush_selected.xml +5 -0
- package/android/src/main/res/drawable/bg_color_swatch.xml +5 -0
- package/android/src/main/res/drawable/bg_editor_toolbar.xml +6 -0
- package/android/src/main/res/drawable/bg_icon_circle.xml +5 -0
- package/android/src/main/res/drawable/bg_icon_circle_light.xml +5 -0
- package/android/src/main/res/drawable/bg_mode_tab_active.xml +6 -0
- package/android/src/main/res/drawable/bg_pill.xml +6 -0
- package/android/src/main/res/drawable/bg_recording_dot.xml +6 -0
- package/android/src/main/res/drawable/bg_selection_badge.xml +8 -0
- package/android/src/main/res/drawable/bg_send_button.xml +5 -0
- package/android/src/main/res/drawable/bg_shutter_inner.xml +5 -0
- package/android/src/main/res/drawable/bg_shutter_recording.xml +6 -0
- package/android/src/main/res/drawable/bg_shutter_ring.xml +8 -0
- package/android/src/main/res/drawable/bg_shutter_video_idle.xml +5 -0
- package/android/src/main/res/drawable/bg_thumbnail.xml +9 -0
- package/android/src/main/res/drawable/ic_aspect_ratio.xml +17 -0
- package/android/src/main/res/drawable/ic_audio.xml +10 -0
- package/android/src/main/res/drawable/ic_back.xml +10 -0
- package/android/src/main/res/drawable/ic_brush_medium.xml +12 -0
- package/android/src/main/res/drawable/ic_brush_thick.xml +12 -0
- package/android/src/main/res/drawable/ic_brush_thin.xml +12 -0
- package/android/src/main/res/drawable/ic_check.xml +17 -0
- package/android/src/main/res/drawable/ic_chevron_down.xml +10 -0
- package/android/src/main/res/drawable/ic_chevron_up.xml +13 -0
- package/android/src/main/res/drawable/ic_clear_drawing.xml +10 -0
- package/android/src/main/res/drawable/ic_close.xml +10 -0
- package/android/src/main/res/drawable/ic_cover.xml +9 -0
- package/android/src/main/res/drawable/ic_crop.xml +10 -0
- package/android/src/main/res/drawable/ic_delete.xml +9 -0
- package/android/src/main/res/drawable/ic_document.xml +10 -0
- package/android/src/main/res/drawable/ic_done.xml +10 -0
- package/android/src/main/res/drawable/ic_download.xml +10 -0
- package/android/src/main/res/drawable/ic_emoji.xml +10 -0
- package/android/src/main/res/drawable/ic_filter.xml +10 -0
- package/android/src/main/res/drawable/ic_flash_auto.xml +7 -0
- package/android/src/main/res/drawable/ic_flash_off.xml +23 -0
- package/android/src/main/res/drawable/ic_flash_on.xml +16 -0
- package/android/src/main/res/drawable/ic_flip.xml +15 -0
- package/android/src/main/res/drawable/ic_gallery.xml +10 -0
- package/android/src/main/res/drawable/ic_gif.xml +24 -0
- package/android/src/main/res/drawable/ic_lock.xml +19 -0
- package/android/src/main/res/drawable/ic_lock_open.xml +22 -0
- package/android/src/main/res/drawable/ic_marker.xml +10 -0
- package/android/src/main/res/drawable/ic_more_vert.xml +18 -0
- package/android/src/main/res/drawable/ic_overlay_close.xml +19 -0
- package/android/src/main/res/drawable/ic_overlay_resize.xml +18 -0
- package/android/src/main/res/drawable/ic_pause.xml +10 -0
- package/android/src/main/res/drawable/ic_photo_mode.xml +10 -0
- package/android/src/main/res/drawable/ic_play.xml +10 -0
- package/android/src/main/res/drawable/ic_redo.xml +10 -0
- package/android/src/main/res/drawable/ic_rotate.xml +9 -0
- package/android/src/main/res/drawable/ic_send.xml +10 -0
- package/android/src/main/res/drawable/ic_share.xml +10 -0
- package/android/src/main/res/drawable/ic_stat_upload.xml +10 -0
- package/android/src/main/res/drawable/ic_sticker.xml +24 -0
- package/android/src/main/res/drawable/ic_text.xml +10 -0
- package/android/src/main/res/drawable/ic_text_align_center.xml +11 -0
- package/android/src/main/res/drawable/ic_text_align_left.xml +11 -0
- package/android/src/main/res/drawable/ic_text_align_right.xml +11 -0
- package/android/src/main/res/drawable/ic_text_bg.xml +11 -0
- package/android/src/main/res/drawable/ic_trim.xml +23 -0
- package/android/src/main/res/drawable/ic_undo.xml +10 -0
- package/android/src/main/res/drawable/ic_video_mode.xml +10 -0
- package/android/src/main/res/drawable/ic_video_note.xml +13 -0
- package/android/src/main/res/drawable/ic_volume_off.xml +9 -0
- package/android/src/main/res/drawable/ic_volume_on.xml +9 -0
- package/android/src/main/res/drawable/sticker_heart.xml +9 -0
- package/android/src/main/res/drawable/sticker_smile.xml +9 -0
- package/android/src/main/res/drawable/sticker_star.xml +9 -0
- package/android/src/main/res/drawable/sticker_thumbsup.xml +9 -0
- package/android/src/main/res/values/colors.xml +34 -0
- package/android/src/main/res/values/ids.xml +15 -0
- package/android/src/main/res/values/strings.xml +15 -0
- package/android/src/main/res/values/styles.xml +20 -0
- package/android/src/main/res/xml/file_paths.xml +34 -0
- package/ios/CustomCamera.h +5 -0
- package/ios/CustomCamera.mm +214 -0
- package/lib/module/AudioPlayerContext.js +233 -0
- package/lib/module/AudioPlayerContext.js.map +1 -0
- package/lib/module/CameraPreview.js +304 -0
- package/lib/module/CameraPreview.js.map +1 -0
- package/lib/module/NativeAnimatedImageView.js +41 -0
- package/lib/module/NativeAnimatedImageView.js.map +1 -0
- package/lib/module/NativeAudioPlayerView.js +33 -0
- package/lib/module/NativeAudioPlayerView.js.map +1 -0
- package/lib/module/NativeBlurView.js +35 -0
- package/lib/module/NativeBlurView.js.map +1 -0
- package/lib/module/NativeCustomCamera.js +128 -0
- package/lib/module/NativeCustomCamera.js.map +1 -0
- package/lib/module/NativeCustomCamera.web.js +143 -0
- package/lib/module/NativeCustomCamera.web.js.map +1 -0
- package/lib/module/NativeCustomVideoView.js +5 -0
- package/lib/module/NativeCustomVideoView.js.map +1 -0
- package/lib/module/NativeDocumentPreviewView.js +156 -0
- package/lib/module/NativeDocumentPreviewView.js.map +1 -0
- package/lib/module/NativeMediaThumbnailView.js +110 -0
- package/lib/module/NativeMediaThumbnailView.js.map +1 -0
- package/lib/module/NativeSvgIcon.js +32 -0
- package/lib/module/NativeSvgIcon.js.map +1 -0
- package/lib/module/ResponsivePreviewContainer.js +171 -0
- package/lib/module/ResponsivePreviewContainer.js.map +1 -0
- package/lib/module/accessibility.js +61 -0
- package/lib/module/accessibility.js.map +1 -0
- package/lib/module/config.js +352 -0
- package/lib/module/config.js.map +1 -0
- package/lib/module/cpuOptimization.js +296 -0
- package/lib/module/cpuOptimization.js.map +1 -0
- package/lib/module/db/DatabaseManager.js +191 -0
- package/lib/module/db/DatabaseManager.js.map +1 -0
- package/lib/module/db/MediaMapper.js +284 -0
- package/lib/module/db/MediaMapper.js.map +1 -0
- package/lib/module/db/MediaRepository.js +242 -0
- package/lib/module/db/MediaRepository.js.map +1 -0
- package/lib/module/db/index.js +41 -0
- package/lib/module/db/index.js.map +1 -0
- package/lib/module/db/migrations.js +180 -0
- package/lib/module/db/migrations.js.map +1 -0
- package/lib/module/db/react-native-sqlcipher.d.js +2 -0
- package/lib/module/db/react-native-sqlcipher.d.js.map +1 -0
- package/lib/module/db/secureKey.js +87 -0
- package/lib/module/db/secureKey.js.map +1 -0
- package/lib/module/db/types.js +57 -0
- package/lib/module/db/types.js.map +1 -0
- package/lib/module/db/useMediaDatabase.js +123 -0
- package/lib/module/db/useMediaDatabase.js.map +1 -0
- package/lib/module/defaultIcons.js +2259 -0
- package/lib/module/defaultIcons.js.map +1 -0
- package/lib/module/deviceCapabilities.js +408 -0
- package/lib/module/deviceCapabilities.js.map +1 -0
- package/lib/module/errors.js +267 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/iconConfig.js +796 -0
- package/lib/module/iconConfig.js.map +1 -0
- package/lib/module/icons.js +514 -0
- package/lib/module/icons.js.map +1 -0
- package/lib/module/index.js +251 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/jest-globals.d.js +2 -0
- package/lib/module/jest-globals.d.js.map +1 -0
- package/lib/module/library/MediaLibrary.js +107 -0
- package/lib/module/library/MediaLibrary.js.map +1 -0
- package/lib/module/library/MediaLibraryScreen.js +2597 -0
- package/lib/module/library/MediaLibraryScreen.js.map +1 -0
- package/lib/module/library/components/AudioPreview.js +74 -0
- package/lib/module/library/components/AudioPreview.js.map +1 -0
- package/lib/module/library/components/DocumentPreview.js +331 -0
- package/lib/module/library/components/DocumentPreview.js.map +1 -0
- package/lib/module/library/components/ExpandableFab.js +343 -0
- package/lib/module/library/components/ExpandableFab.js.map +1 -0
- package/lib/module/library/components/FileTiles.js +251 -0
- package/lib/module/library/components/FileTiles.js.map +1 -0
- package/lib/module/library/components/ImagePreview.js +70 -0
- package/lib/module/library/components/ImagePreview.js.map +1 -0
- package/lib/module/library/components/MediaMetadataHeader.js +175 -0
- package/lib/module/library/components/MediaMetadataHeader.js.map +1 -0
- package/lib/module/library/components/RecordAudioScreen.js +816 -0
- package/lib/module/library/components/RecordAudioScreen.js.map +1 -0
- package/lib/module/library/components/VideoPreview.js +453 -0
- package/lib/module/library/components/VideoPreview.js.map +1 -0
- package/lib/module/library/components/ViewerCounter.js +55 -0
- package/lib/module/library/components/ViewerCounter.js.map +1 -0
- package/lib/module/library/compressionEstimate.js +507 -0
- package/lib/module/library/compressionEstimate.js.map +1 -0
- package/lib/module/library/fileMetadata.js +196 -0
- package/lib/module/library/fileMetadata.js.map +1 -0
- package/lib/module/library/index.js +42 -0
- package/lib/module/library/index.js.map +1 -0
- package/lib/module/library/libraryIconRegistry.js +69 -0
- package/lib/module/library/libraryIconRegistry.js.map +1 -0
- package/lib/module/library/libraryIcons.js +858 -0
- package/lib/module/library/libraryIcons.js.map +1 -0
- package/lib/module/library/localizedFormat.js +181 -0
- package/lib/module/library/localizedFormat.js.map +1 -0
- package/lib/module/library/palette.js +225 -0
- package/lib/module/library/palette.js.map +1 -0
- package/lib/module/library/responsive.js +53 -0
- package/lib/module/library/responsive.js.map +1 -0
- package/lib/module/locales.js +416 -0
- package/lib/module/locales.js.map +1 -0
- package/lib/module/localization.js +1064 -0
- package/lib/module/localization.js.map +1 -0
- package/lib/module/logger.js +20 -0
- package/lib/module/logger.js.map +1 -0
- package/lib/module/lowMemoryCache.js +206 -0
- package/lib/module/lowMemoryCache.js.map +1 -0
- package/lib/module/mediaHistory.js +33 -0
- package/lib/module/mediaHistory.js.map +1 -0
- package/lib/module/mediaStorageService.js +402 -0
- package/lib/module/mediaStorageService.js.map +1 -0
- package/lib/module/memory.js +79 -0
- package/lib/module/memory.js.map +1 -0
- package/lib/module/nativeActions.js +307 -0
- package/lib/module/nativeActions.js.map +1 -0
- package/lib/module/nativeComponentSupport.js +61 -0
- package/lib/module/nativeComponentSupport.js.map +1 -0
- package/lib/module/networkPerformance.js +24 -0
- package/lib/module/networkPerformance.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/performance.js +104 -0
- package/lib/module/performance.js.map +1 -0
- package/lib/module/performanceMonitor.js +155 -0
- package/lib/module/performanceMonitor.js.map +1 -0
- package/lib/module/permissions.js +370 -0
- package/lib/module/permissions.js.map +1 -0
- package/lib/module/rendering.js +40 -0
- package/lib/module/rendering.js.map +1 -0
- package/lib/module/security.js +18 -0
- package/lib/module/security.js.map +1 -0
- package/lib/module/state.js +266 -0
- package/lib/module/state.js.map +1 -0
- package/lib/module/storage.js +400 -0
- package/lib/module/storage.js.map +1 -0
- package/lib/module/theme.js +390 -0
- package/lib/module/theme.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils.js +115 -0
- package/lib/module/utils.js.map +1 -0
- package/lib/module/validation.js +288 -0
- package/lib/module/validation.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/AudioPlayerContext.d.ts +27 -0
- package/lib/typescript/src/AudioPlayerContext.d.ts.map +1 -0
- package/lib/typescript/src/CameraPreview.d.ts +13 -0
- package/lib/typescript/src/CameraPreview.d.ts.map +1 -0
- package/lib/typescript/src/NativeAnimatedImageView.d.ts +33 -0
- package/lib/typescript/src/NativeAnimatedImageView.d.ts.map +1 -0
- package/lib/typescript/src/NativeAudioPlayerView.d.ts +12 -0
- package/lib/typescript/src/NativeAudioPlayerView.d.ts.map +1 -0
- package/lib/typescript/src/NativeBlurView.d.ts +12 -0
- package/lib/typescript/src/NativeBlurView.d.ts.map +1 -0
- package/lib/typescript/src/NativeCustomCamera.d.ts +167 -0
- package/lib/typescript/src/NativeCustomCamera.d.ts.map +1 -0
- package/lib/typescript/src/NativeCustomCamera.web.d.ts +47 -0
- package/lib/typescript/src/NativeCustomCamera.web.d.ts.map +1 -0
- package/lib/typescript/src/NativeCustomVideoView.d.ts +30 -0
- package/lib/typescript/src/NativeCustomVideoView.d.ts.map +1 -0
- package/lib/typescript/src/NativeDocumentPreviewView.d.ts +48 -0
- package/lib/typescript/src/NativeDocumentPreviewView.d.ts.map +1 -0
- package/lib/typescript/src/NativeMediaThumbnailView.d.ts +54 -0
- package/lib/typescript/src/NativeMediaThumbnailView.d.ts.map +1 -0
- package/lib/typescript/src/NativeSvgIcon.d.ts +25 -0
- package/lib/typescript/src/NativeSvgIcon.d.ts.map +1 -0
- package/lib/typescript/src/ResponsivePreviewContainer.d.ts +92 -0
- package/lib/typescript/src/ResponsivePreviewContainer.d.ts.map +1 -0
- package/lib/typescript/src/accessibility.d.ts +20 -0
- package/lib/typescript/src/accessibility.d.ts.map +1 -0
- package/lib/typescript/src/config.d.ts +145 -0
- package/lib/typescript/src/config.d.ts.map +1 -0
- package/lib/typescript/src/cpuOptimization.d.ts +63 -0
- package/lib/typescript/src/cpuOptimization.d.ts.map +1 -0
- package/lib/typescript/src/db/DatabaseManager.d.ts +48 -0
- package/lib/typescript/src/db/DatabaseManager.d.ts.map +1 -0
- package/lib/typescript/src/db/MediaMapper.d.ts +77 -0
- package/lib/typescript/src/db/MediaMapper.d.ts.map +1 -0
- package/lib/typescript/src/db/MediaRepository.d.ts +66 -0
- package/lib/typescript/src/db/MediaRepository.d.ts.map +1 -0
- package/lib/typescript/src/db/index.d.ts +23 -0
- package/lib/typescript/src/db/index.d.ts.map +1 -0
- package/lib/typescript/src/db/migrations.d.ts +31 -0
- package/lib/typescript/src/db/migrations.d.ts.map +1 -0
- package/lib/typescript/src/db/secureKey.d.ts +10 -0
- package/lib/typescript/src/db/secureKey.d.ts.map +1 -0
- package/lib/typescript/src/db/types.d.ts +115 -0
- package/lib/typescript/src/db/types.d.ts.map +1 -0
- package/lib/typescript/src/db/useMediaDatabase.d.ts +41 -0
- package/lib/typescript/src/db/useMediaDatabase.d.ts.map +1 -0
- package/lib/typescript/src/defaultIcons.d.ts +139 -0
- package/lib/typescript/src/defaultIcons.d.ts.map +1 -0
- package/lib/typescript/src/deviceCapabilities.d.ts +92 -0
- package/lib/typescript/src/deviceCapabilities.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +95 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/iconConfig.d.ts +176 -0
- package/lib/typescript/src/iconConfig.d.ts.map +1 -0
- package/lib/typescript/src/icons.d.ts +217 -0
- package/lib/typescript/src/icons.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +104 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/library/MediaLibrary.d.ts +57 -0
- package/lib/typescript/src/library/MediaLibrary.d.ts.map +1 -0
- package/lib/typescript/src/library/MediaLibraryScreen.d.ts +51 -0
- package/lib/typescript/src/library/MediaLibraryScreen.d.ts.map +1 -0
- package/lib/typescript/src/library/components/AudioPreview.d.ts +6 -0
- package/lib/typescript/src/library/components/AudioPreview.d.ts.map +1 -0
- package/lib/typescript/src/library/components/DocumentPreview.d.ts +10 -0
- package/lib/typescript/src/library/components/DocumentPreview.d.ts.map +1 -0
- package/lib/typescript/src/library/components/ExpandableFab.d.ts +39 -0
- package/lib/typescript/src/library/components/ExpandableFab.d.ts.map +1 -0
- package/lib/typescript/src/library/components/FileTiles.d.ts +8 -0
- package/lib/typescript/src/library/components/FileTiles.d.ts.map +1 -0
- package/lib/typescript/src/library/components/ImagePreview.d.ts +5 -0
- package/lib/typescript/src/library/components/ImagePreview.d.ts.map +1 -0
- package/lib/typescript/src/library/components/MediaMetadataHeader.d.ts +16 -0
- package/lib/typescript/src/library/components/MediaMetadataHeader.d.ts.map +1 -0
- package/lib/typescript/src/library/components/RecordAudioScreen.d.ts +8 -0
- package/lib/typescript/src/library/components/RecordAudioScreen.d.ts.map +1 -0
- package/lib/typescript/src/library/components/VideoPreview.d.ts +20 -0
- package/lib/typescript/src/library/components/VideoPreview.d.ts.map +1 -0
- package/lib/typescript/src/library/components/ViewerCounter.d.ts +13 -0
- package/lib/typescript/src/library/components/ViewerCounter.d.ts.map +1 -0
- package/lib/typescript/src/library/compressionEstimate.d.ts +313 -0
- package/lib/typescript/src/library/compressionEstimate.d.ts.map +1 -0
- package/lib/typescript/src/library/fileMetadata.d.ts +33 -0
- package/lib/typescript/src/library/fileMetadata.d.ts.map +1 -0
- package/lib/typescript/src/library/index.d.ts +40 -0
- package/lib/typescript/src/library/index.d.ts.map +1 -0
- package/lib/typescript/src/library/libraryIconRegistry.d.ts +35 -0
- package/lib/typescript/src/library/libraryIconRegistry.d.ts.map +1 -0
- package/lib/typescript/src/library/libraryIcons.d.ts +85 -0
- package/lib/typescript/src/library/libraryIcons.d.ts.map +1 -0
- package/lib/typescript/src/library/localizedFormat.d.ts +49 -0
- package/lib/typescript/src/library/localizedFormat.d.ts.map +1 -0
- package/lib/typescript/src/library/palette.d.ts +104 -0
- package/lib/typescript/src/library/palette.d.ts.map +1 -0
- package/lib/typescript/src/library/responsive.d.ts +15 -0
- package/lib/typescript/src/library/responsive.d.ts.map +1 -0
- package/lib/typescript/src/locales.d.ts +103 -0
- package/lib/typescript/src/locales.d.ts.map +1 -0
- package/lib/typescript/src/localization.d.ts +1051 -0
- package/lib/typescript/src/localization.d.ts.map +1 -0
- package/lib/typescript/src/logger.d.ts +4 -0
- package/lib/typescript/src/logger.d.ts.map +1 -0
- package/lib/typescript/src/lowMemoryCache.d.ts +64 -0
- package/lib/typescript/src/lowMemoryCache.d.ts.map +1 -0
- package/lib/typescript/src/mediaHistory.d.ts +3 -0
- package/lib/typescript/src/mediaHistory.d.ts.map +1 -0
- package/lib/typescript/src/mediaStorageService.d.ts +155 -0
- package/lib/typescript/src/mediaStorageService.d.ts.map +1 -0
- package/lib/typescript/src/memory.d.ts +21 -0
- package/lib/typescript/src/memory.d.ts.map +1 -0
- package/lib/typescript/src/nativeActions.d.ts +174 -0
- package/lib/typescript/src/nativeActions.d.ts.map +1 -0
- package/lib/typescript/src/nativeComponentSupport.d.ts +30 -0
- package/lib/typescript/src/nativeComponentSupport.d.ts.map +1 -0
- package/lib/typescript/src/networkPerformance.d.ts +12 -0
- package/lib/typescript/src/networkPerformance.d.ts.map +1 -0
- package/lib/typescript/src/performance.d.ts +32 -0
- package/lib/typescript/src/performance.d.ts.map +1 -0
- package/lib/typescript/src/performanceMonitor.d.ts +45 -0
- package/lib/typescript/src/performanceMonitor.d.ts.map +1 -0
- package/lib/typescript/src/permissions.d.ts +132 -0
- package/lib/typescript/src/permissions.d.ts.map +1 -0
- package/lib/typescript/src/rendering.d.ts +9 -0
- package/lib/typescript/src/rendering.d.ts.map +1 -0
- package/lib/typescript/src/security.d.ts +6 -0
- package/lib/typescript/src/security.d.ts.map +1 -0
- package/lib/typescript/src/state.d.ts +102 -0
- package/lib/typescript/src/state.d.ts.map +1 -0
- package/lib/typescript/src/storage.d.ts +117 -0
- package/lib/typescript/src/storage.d.ts.map +1 -0
- package/lib/typescript/src/theme.d.ts +15 -0
- package/lib/typescript/src/theme.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +802 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utils.d.ts +50 -0
- package/lib/typescript/src/utils.d.ts.map +1 -0
- package/lib/typescript/src/validation.d.ts +52 -0
- package/lib/typescript/src/validation.d.ts.map +1 -0
- package/package.json +204 -0
- package/react-native.config.js +17 -0
- package/src/AudioPlayerContext.tsx +220 -0
- package/src/CameraPreview.tsx +352 -0
- package/src/NativeAnimatedImageView.tsx +71 -0
- package/src/NativeAudioPlayerView.tsx +39 -0
- package/src/NativeBlurView.tsx +40 -0
- package/src/NativeCustomCamera.ts +305 -0
- package/src/NativeCustomCamera.web.ts +192 -0
- package/src/NativeCustomVideoView.ts +30 -0
- package/src/NativeDocumentPreviewView.tsx +196 -0
- package/src/NativeMediaThumbnailView.tsx +167 -0
- package/src/NativeSvgIcon.tsx +46 -0
- package/src/ResponsivePreviewContainer.tsx +227 -0
- package/src/accessibility.ts +80 -0
- package/src/config.ts +406 -0
- package/src/cpuOptimization.ts +365 -0
- package/src/db/DatabaseManager.ts +212 -0
- package/src/db/MediaMapper.ts +377 -0
- package/src/db/MediaRepository.ts +313 -0
- package/src/db/index.ts +73 -0
- package/src/db/migrations.ts +222 -0
- package/src/db/react-native-sqlcipher.d.ts +76 -0
- package/src/db/secureKey.ts +100 -0
- package/src/db/types.ts +123 -0
- package/src/db/useMediaDatabase.ts +191 -0
- package/src/defaultIcons.tsx +2390 -0
- package/src/deviceCapabilities.ts +438 -0
- package/src/errors.ts +350 -0
- package/src/iconConfig.tsx +962 -0
- package/src/icons.tsx +788 -0
- package/src/index.tsx +763 -0
- package/src/jest-globals.d.ts +9 -0
- package/src/library/MediaLibrary.tsx +141 -0
- package/src/library/MediaLibraryScreen.tsx +3003 -0
- package/src/library/components/AudioPreview.tsx +73 -0
- package/src/library/components/DocumentPreview.tsx +361 -0
- package/src/library/components/ExpandableFab.tsx +342 -0
- package/src/library/components/FileTiles.tsx +252 -0
- package/src/library/components/ImagePreview.tsx +72 -0
- package/src/library/components/MediaMetadataHeader.tsx +208 -0
- package/src/library/components/RecordAudioScreen.tsx +845 -0
- package/src/library/components/VideoPreview.tsx +478 -0
- package/src/library/components/ViewerCounter.tsx +71 -0
- package/src/library/compressionEstimate.ts +662 -0
- package/src/library/fileMetadata.ts +212 -0
- package/src/library/index.ts +66 -0
- package/src/library/libraryIconRegistry.tsx +86 -0
- package/src/library/libraryIcons.tsx +928 -0
- package/src/library/localizedFormat.ts +207 -0
- package/src/library/palette.ts +281 -0
- package/src/library/responsive.ts +49 -0
- package/src/locales.ts +381 -0
- package/src/localization.ts +1947 -0
- package/src/logger.ts +20 -0
- package/src/lowMemoryCache.ts +218 -0
- package/src/mediaHistory.ts +42 -0
- package/src/mediaStorageService.ts +546 -0
- package/src/memory.ts +102 -0
- package/src/nativeActions.ts +422 -0
- package/src/nativeComponentSupport.ts +60 -0
- package/src/networkPerformance.ts +30 -0
- package/src/performance.ts +137 -0
- package/src/performanceMonitor.ts +219 -0
- package/src/permissions.ts +403 -0
- package/src/rendering.ts +47 -0
- package/src/security.ts +19 -0
- package/src/state.ts +333 -0
- package/src/storage.ts +453 -0
- package/src/theme.tsx +467 -0
- package/src/types.ts +946 -0
- package/src/utils.ts +177 -0
- package/src/validation.ts +372 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExpandableFab — a bottom-right Floating Action Button that expands into a stack of labelled
|
|
3
|
+
* action rows (Camera / Document / Audio).
|
|
4
|
+
*
|
|
5
|
+
* Constraints honoured:
|
|
6
|
+
* • All icons are hand-authored from RN primitives (see ../icons/CustomIcons) — no icon library,
|
|
7
|
+
* no react-native-svg, no external assets.
|
|
8
|
+
* • All motion uses ONLY React Native's built-in Animated API (no reanimated / animatable / moti /
|
|
9
|
+
* lottie). A single Animated.Value drives everything, so there is one source of truth and the
|
|
10
|
+
* expand/collapse reverse is exact.
|
|
11
|
+
* • Transforms + opacity run on the native driver, so it stays smooth on low-end devices.
|
|
12
|
+
*
|
|
13
|
+
* State model: `open` (React state) is the source of truth; an effect animates `progress` toward
|
|
14
|
+
* it. Rapid taps just flip `open`; Animated.timing redirects from the current value, so there are
|
|
15
|
+
* no glitches or half-open states. The menu auto-collapses when an action is picked, when the
|
|
16
|
+
* backdrop is tapped, and whenever the FAB is disabled (e.g. a capture is in flight).
|
|
17
|
+
*/
|
|
18
|
+
import { useEffect, useMemo, useRef, useState, useCallback, type FC } from 'react';
|
|
19
|
+
import {
|
|
20
|
+
View,
|
|
21
|
+
Text,
|
|
22
|
+
Pressable,
|
|
23
|
+
TouchableOpacity,
|
|
24
|
+
Animated,
|
|
25
|
+
Easing,
|
|
26
|
+
StyleSheet,
|
|
27
|
+
ActivityIndicator,
|
|
28
|
+
type ColorValue,
|
|
29
|
+
} from 'react-native';
|
|
30
|
+
import { NativeBlurView } from '../../NativeBlurView';
|
|
31
|
+
import { useCameraLocalization } from '../../localization';
|
|
32
|
+
import { RenderIcon, type CameraIconKey } from '../../icons';
|
|
33
|
+
|
|
34
|
+
// ── Responsive scaling — shared with the whole app (portrait-normalized + clamped) ──
|
|
35
|
+
import { wp, hp, ms, fs } from '../responsive';
|
|
36
|
+
import { useLibraryPalette, type LibraryPalette } from '../palette';
|
|
37
|
+
|
|
38
|
+
interface FabAction {
|
|
39
|
+
key: string;
|
|
40
|
+
label: string;
|
|
41
|
+
/** Registry key — rendered via `<RenderIcon>` from the app's icon registry. */
|
|
42
|
+
iconName: CameraIconKey;
|
|
43
|
+
color: string;
|
|
44
|
+
onPress: () => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ExpandableFabProps {
|
|
48
|
+
onCamera: () => void;
|
|
49
|
+
/**
|
|
50
|
+
* Opens the device gallery for an existing image or video, then hands the pick to the same
|
|
51
|
+
* preview/editor screen a capture opens on (image editor for a photo, video editor for a clip).
|
|
52
|
+
*/
|
|
53
|
+
onImageVideo: () => void;
|
|
54
|
+
onDocument: () => void;
|
|
55
|
+
onAudio: () => void;
|
|
56
|
+
onRecordAudio: () => void;
|
|
57
|
+
/** Shows a spinner on the FAB and disables interaction (e.g. while capturing). */
|
|
58
|
+
busy?: boolean;
|
|
59
|
+
disabled?: boolean;
|
|
60
|
+
/** Distance from the right/bottom screen edges — defaults match the app's original FAB. */
|
|
61
|
+
right?: number;
|
|
62
|
+
bottom?: number;
|
|
63
|
+
accentColor?: ColorValue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const ANIM_IN = 240;
|
|
67
|
+
const ANIM_OUT = 200;
|
|
68
|
+
const ICON_SIZE = ms(22);
|
|
69
|
+
const ICON_STROKE = 2;
|
|
70
|
+
|
|
71
|
+
export const ExpandableFab: FC<ExpandableFabProps> = ({
|
|
72
|
+
onCamera,
|
|
73
|
+
onImageVideo,
|
|
74
|
+
onDocument,
|
|
75
|
+
onAudio,
|
|
76
|
+
onRecordAudio,
|
|
77
|
+
busy = false,
|
|
78
|
+
disabled = false,
|
|
79
|
+
right = wp(16),
|
|
80
|
+
bottom = hp(80),
|
|
81
|
+
accentColor,
|
|
82
|
+
}) => {
|
|
83
|
+
const { t } = useCameraLocalization();
|
|
84
|
+
const palette = useLibraryPalette();
|
|
85
|
+
const styles = useMemo(() => makeStyles(palette), [palette]);
|
|
86
|
+
const resolvedAccent = accentColor ?? palette.accentTeal;
|
|
87
|
+
const [open, setOpen] = useState(false);
|
|
88
|
+
// Keeps the frosted backdrop mounted/active through the collapse animation so the
|
|
89
|
+
// blur fades out with the menu instead of snapping away the instant `open` flips.
|
|
90
|
+
const [backdropVisible, setBackdropVisible] = useState(false);
|
|
91
|
+
const progress = useRef(new Animated.Value(0)).current;
|
|
92
|
+
|
|
93
|
+
// Follows the host-driven plugin theme (not the device setting), so the menu matches the
|
|
94
|
+
// rest of the shell: dark cards + light text in dark mode, white cards + dark text in light.
|
|
95
|
+
const isDark = palette.isDark;
|
|
96
|
+
const cardBgColor = isDark ? palette.chromeSystem : palette.white;
|
|
97
|
+
const cardPressedBgColor = isDark ? palette.chromeSystemAlt : palette.surfaceMuted;
|
|
98
|
+
const textColor = isDark ? palette.white : palette.black;
|
|
99
|
+
|
|
100
|
+
// Actions are declared top-of-menu → bottom-of-menu is handled by column-reverse below, so the
|
|
101
|
+
// FIRST entry (Camera) ends up nearest the FAB and reveals first.
|
|
102
|
+
const actions: FabAction[] = [
|
|
103
|
+
{ key: 'camera', label: t('camera'), iconName: 'camera', color: palette.accentBlue, onPress: onCamera },
|
|
104
|
+
{ key: 'imagevideo', label: t('gallery'), iconName: 'image_video', color: palette.accentIndigo, onPress: onImageVideo },
|
|
105
|
+
{ key: 'document', label: t('document'), iconName: 'document', color: palette.accentPurple, onPress: onDocument },
|
|
106
|
+
{ key: 'recordAudio', label: t('recordAudio'), iconName: 'mic', color: palette.accentOrange, onPress: onRecordAudio },
|
|
107
|
+
{ key: 'audio', label: t('browseAudio'), iconName: 'browse_audio', color: palette.accentOrange, onPress: onAudio },
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
// Single source of truth: animate `progress` toward the `open` state. Interrupting taps simply
|
|
111
|
+
// change `open`; the timing restarts from the live value → no glitches, exact reverse.
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
const animation = Animated.timing(progress, {
|
|
114
|
+
toValue: open ? 1 : 0,
|
|
115
|
+
duration: open ? ANIM_IN : ANIM_OUT,
|
|
116
|
+
easing: open ? Easing.out(Easing.cubic) : Easing.in(Easing.cubic),
|
|
117
|
+
useNativeDriver: true,
|
|
118
|
+
});
|
|
119
|
+
animation.start();
|
|
120
|
+
return () => animation.stop();
|
|
121
|
+
}, [open, progress]);
|
|
122
|
+
|
|
123
|
+
// A disabled FAB (e.g. capture in progress) must never stay expanded.
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (disabled) setOpen(false);
|
|
126
|
+
}, [disabled]);
|
|
127
|
+
|
|
128
|
+
// Snapshot-blur the background as soon as the menu opens; keep it active until the
|
|
129
|
+
// collapse animation has finished so it fades out smoothly rather than popping.
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (open) {
|
|
132
|
+
setBackdropVisible(true);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const timeout = setTimeout(() => setBackdropVisible(false), ANIM_OUT + 40);
|
|
136
|
+
return () => clearTimeout(timeout);
|
|
137
|
+
}, [open]);
|
|
138
|
+
|
|
139
|
+
const toggle = useCallback(() => {
|
|
140
|
+
if (disabled) return;
|
|
141
|
+
setOpen((prev) => !prev);
|
|
142
|
+
}, [disabled]);
|
|
143
|
+
|
|
144
|
+
const handleSelect = useCallback((action: FabAction) => {
|
|
145
|
+
// Collapse immediately, then run the existing flow — the menu animates away while the
|
|
146
|
+
// native picker/flow opens.
|
|
147
|
+
setOpen(false);
|
|
148
|
+
action.onPress();
|
|
149
|
+
}, []);
|
|
150
|
+
|
|
151
|
+
const backdropOpacity = progress.interpolate({
|
|
152
|
+
inputRange: [0, 1],
|
|
153
|
+
outputRange: [0, 1],
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<View style={[StyleSheet.absoluteFill, styles.overlay]} pointerEvents="box-none">
|
|
158
|
+
{/* Backdrop — fades in with the menu and collapses on tap. Non-interactive when closed so
|
|
159
|
+
the media library underneath stays fully usable. */}
|
|
160
|
+
<Animated.View
|
|
161
|
+
pointerEvents={open ? 'auto' : 'none'}
|
|
162
|
+
style={[styles.backdrop, { opacity: backdropOpacity }]}
|
|
163
|
+
>
|
|
164
|
+
{/* Native frosted-glass blur of the media library sitting behind the menu
|
|
165
|
+
(Android; a graceful no-op elsewhere). ONLY the background is blurred —
|
|
166
|
+
the menu rows and the FAB are rendered above this layer and stay crisp. */}
|
|
167
|
+
<NativeBlurView
|
|
168
|
+
active={backdropVisible}
|
|
169
|
+
blurRadius={6}
|
|
170
|
+
downsampleFactor={7}
|
|
171
|
+
style={StyleSheet.absoluteFill}
|
|
172
|
+
/>
|
|
173
|
+
{/* Subtle scrim over the blur for contrast; fades with the same progress. */}
|
|
174
|
+
<View style={styles.backdropTint} pointerEvents="none" />
|
|
175
|
+
<Pressable
|
|
176
|
+
style={StyleSheet.absoluteFill}
|
|
177
|
+
onPress={() => setOpen(false)}
|
|
178
|
+
accessibilityLabel={t('closeActionsMenu')}
|
|
179
|
+
/>
|
|
180
|
+
</Animated.View>
|
|
181
|
+
|
|
182
|
+
<View style={[styles.anchor, { right, bottom }]} pointerEvents="box-none">
|
|
183
|
+
{/* Menu — reversed so Camera sits at the bottom (closest to the FAB). */}
|
|
184
|
+
<View
|
|
185
|
+
style={styles.menu}
|
|
186
|
+
pointerEvents={open ? 'auto' : 'none'}
|
|
187
|
+
>
|
|
188
|
+
{actions.map((action, index) => {
|
|
189
|
+
// Stagger each row's reveal by its position; clamp keeps every item fully hidden
|
|
190
|
+
// before its window starts and fully shown after it ends.
|
|
191
|
+
const start = index * 0.16;
|
|
192
|
+
const end = Math.min(start + 0.6, 1);
|
|
193
|
+
const itemOpacity = progress.interpolate({
|
|
194
|
+
inputRange: [start, end],
|
|
195
|
+
outputRange: [0, 1],
|
|
196
|
+
extrapolate: 'clamp',
|
|
197
|
+
});
|
|
198
|
+
const translateY = progress.interpolate({
|
|
199
|
+
inputRange: [start, end],
|
|
200
|
+
outputRange: [ms(18), 0],
|
|
201
|
+
extrapolate: 'clamp',
|
|
202
|
+
});
|
|
203
|
+
const scale = progress.interpolate({
|
|
204
|
+
inputRange: [start, end],
|
|
205
|
+
outputRange: [0.9, 1],
|
|
206
|
+
extrapolate: 'clamp',
|
|
207
|
+
});
|
|
208
|
+
return (
|
|
209
|
+
<Animated.View
|
|
210
|
+
key={action.key}
|
|
211
|
+
style={{ opacity: itemOpacity, transform: [{ translateY }, { scale }] }}
|
|
212
|
+
>
|
|
213
|
+
<Pressable
|
|
214
|
+
style={({ pressed }) => [
|
|
215
|
+
styles.row,
|
|
216
|
+
{ backgroundColor: pressed ? cardPressedBgColor : cardBgColor },
|
|
217
|
+
]}
|
|
218
|
+
onPress={() => handleSelect(action)}
|
|
219
|
+
accessibilityRole="button"
|
|
220
|
+
accessibilityLabel={action.label}
|
|
221
|
+
>
|
|
222
|
+
<View style={[styles.iconWrap, { backgroundColor: action.color }]}>
|
|
223
|
+
<RenderIcon
|
|
224
|
+
name={action.iconName}
|
|
225
|
+
size={ICON_SIZE}
|
|
226
|
+
color={palette.white}
|
|
227
|
+
strokeWidth={ICON_STROKE}
|
|
228
|
+
/>
|
|
229
|
+
</View>
|
|
230
|
+
<Text style={[styles.label, { color: textColor }]} numberOfLines={1}>
|
|
231
|
+
{action.label}
|
|
232
|
+
</Text>
|
|
233
|
+
</Pressable>
|
|
234
|
+
</Animated.View>
|
|
235
|
+
);
|
|
236
|
+
})}
|
|
237
|
+
</View>
|
|
238
|
+
|
|
239
|
+
{/* Main FAB */}
|
|
240
|
+
<TouchableOpacity
|
|
241
|
+
activeOpacity={0.85}
|
|
242
|
+
onPress={toggle}
|
|
243
|
+
disabled={disabled}
|
|
244
|
+
accessibilityRole="button"
|
|
245
|
+
accessibilityState={{ expanded: open, disabled }}
|
|
246
|
+
accessibilityLabel={open ? t('closeActionsMenu') : t('openActionsMenu')}
|
|
247
|
+
style={[styles.fab, { backgroundColor: resolvedAccent }]}
|
|
248
|
+
>
|
|
249
|
+
{busy ? (
|
|
250
|
+
<ActivityIndicator color={palette.white} size="small" />
|
|
251
|
+
) : (
|
|
252
|
+
<RenderIcon name="plus_close" size={ms(24)} color={palette.white} strokeWidth={2.4} progress={progress} />
|
|
253
|
+
)}
|
|
254
|
+
</TouchableOpacity>
|
|
255
|
+
</View>
|
|
256
|
+
</View>
|
|
257
|
+
);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const ROW_HEIGHT = ms(46);
|
|
261
|
+
// Fixed width sized to accommodate the longest label ("Browse Audio") so all
|
|
262
|
+
// five menu options share one uniform width.
|
|
263
|
+
const ROW_WIDTH = ms(160);
|
|
264
|
+
|
|
265
|
+
const makeStyles = (palette: LibraryPalette) =>
|
|
266
|
+
StyleSheet.create({
|
|
267
|
+
backdrop: {
|
|
268
|
+
// Transparent container: the native blur layer paints the frosted background and
|
|
269
|
+
// `backdropTint` adds the darkening, so the two compose into a frosted scrim.
|
|
270
|
+
...StyleSheet.absoluteFill,
|
|
271
|
+
backgroundColor: 'transparent',
|
|
272
|
+
zIndex: 1,
|
|
273
|
+
elevation: 16,
|
|
274
|
+
},
|
|
275
|
+
backdropTint: {
|
|
276
|
+
...StyleSheet.absoluteFill,
|
|
277
|
+
backgroundColor: palette.scrimA22,
|
|
278
|
+
},
|
|
279
|
+
anchor: {
|
|
280
|
+
position: 'absolute',
|
|
281
|
+
alignItems: 'flex-end',
|
|
282
|
+
zIndex: 2,
|
|
283
|
+
elevation: 17,
|
|
284
|
+
},
|
|
285
|
+
overlay: {
|
|
286
|
+
zIndex: 100,
|
|
287
|
+
elevation: 16,
|
|
288
|
+
},
|
|
289
|
+
menu: {
|
|
290
|
+
flexDirection: 'column-reverse',
|
|
291
|
+
alignItems: 'flex-end',
|
|
292
|
+
gap: ms(12),
|
|
293
|
+
marginBottom: ms(14),
|
|
294
|
+
// Reserve space on the right so menu rows never overlap the FAB or overflow
|
|
295
|
+
// the screen edge. The FAB is ms(56) wide + ms(14) gap below the menu.
|
|
296
|
+
paddingRight: ms(20),
|
|
297
|
+
},
|
|
298
|
+
// Fixed row width so every icon shares one left edge and every label shares one left edge —
|
|
299
|
+
// the whole menu reads as one perfectly aligned column.
|
|
300
|
+
row: {
|
|
301
|
+
flexDirection: 'row',
|
|
302
|
+
alignItems: 'center',
|
|
303
|
+
width: ROW_WIDTH,
|
|
304
|
+
height: ROW_HEIGHT,
|
|
305
|
+
borderRadius: Math.round(ROW_HEIGHT / 2),
|
|
306
|
+
paddingLeft: ms(8),
|
|
307
|
+
paddingRight: ms(10),
|
|
308
|
+
// Subtle lift so the pills sit above the media library.
|
|
309
|
+
shadowColor: palette.blackShort,
|
|
310
|
+
shadowOffset: { width: 0, height: hp(2) },
|
|
311
|
+
shadowOpacity: 0.12,
|
|
312
|
+
shadowRadius: ms(6),
|
|
313
|
+
elevation: 4,
|
|
314
|
+
},
|
|
315
|
+
iconWrap: {
|
|
316
|
+
width: ms(34),
|
|
317
|
+
height: ms(34),
|
|
318
|
+
borderRadius: ms(17),
|
|
319
|
+
alignItems: 'center',
|
|
320
|
+
justifyContent: 'center',
|
|
321
|
+
},
|
|
322
|
+
label: {
|
|
323
|
+
flex: 1,
|
|
324
|
+
fontSize: fs(15),
|
|
325
|
+
fontWeight: '600',
|
|
326
|
+
textAlign: 'center',
|
|
327
|
+
includeFontPadding: false,
|
|
328
|
+
},
|
|
329
|
+
fab: {
|
|
330
|
+
width: ms(56),
|
|
331
|
+
height: ms(56),
|
|
332
|
+
borderRadius: ms(20),
|
|
333
|
+
alignItems: 'center',
|
|
334
|
+
justifyContent: 'center',
|
|
335
|
+
overflow: 'hidden',
|
|
336
|
+
shadowColor: palette.blackShort,
|
|
337
|
+
shadowOffset: { width: 0, height: hp(4) },
|
|
338
|
+
shadowOpacity: 0.3,
|
|
339
|
+
shadowRadius: ms(6),
|
|
340
|
+
elevation: 8,
|
|
341
|
+
},
|
|
342
|
+
});
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileTiles — modern, informative list thumbnails for media without a pixel frame
|
|
3
|
+
* (documents and audio) on the Media List screen.
|
|
4
|
+
*
|
|
5
|
+
* • DocumentTile — a colour-coded mini page with a folded corner and a solid
|
|
6
|
+
* file-type badge. The palette is keyed by file family (PDF red, Word blue,
|
|
7
|
+
* spreadsheet green, slides amber, archive violet), so a row is identifiable
|
|
8
|
+
* at a glance without reading the file name.
|
|
9
|
+
* • AudioTile — a waveform seeded deterministically from the asset id (stable
|
|
10
|
+
* across re-renders, unique per file) with the same style of format badge.
|
|
11
|
+
*
|
|
12
|
+
* Both are drawn purely from RN primitives — no icon library, no react-native-svg,
|
|
13
|
+
* no image assets — and degrade gracefully: an unknown or missing extension falls
|
|
14
|
+
* back to a neutral dark-theme palette and a generic FILE / AUD badge.
|
|
15
|
+
*/
|
|
16
|
+
import { useMemo } from 'react';
|
|
17
|
+
import { View, Text, StyleSheet } from 'react-native';
|
|
18
|
+
import type { CameraAsset } from '../../types';
|
|
19
|
+
import { RenderIcon } from '../../icons';
|
|
20
|
+
import { useCameraLocalization } from '../../localization';
|
|
21
|
+
|
|
22
|
+
// ── Responsive scaling — shared with the whole app (portrait-normalized + clamped) ──
|
|
23
|
+
import { ms, fs } from '../responsive';
|
|
24
|
+
import { useLibraryPalette, type LibraryPalette } from '../palette';
|
|
25
|
+
|
|
26
|
+
interface TilePalette {
|
|
27
|
+
/** Tile background — a tint of the accent hue for the active theme. */
|
|
28
|
+
bg: string;
|
|
29
|
+
/** Glyph strokes + badge fill. */
|
|
30
|
+
accent: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface TilePalettes {
|
|
34
|
+
/** Neutral fallback for unknown/missing extensions. */
|
|
35
|
+
default: TilePalette;
|
|
36
|
+
/** File-family palettes, keyed by lowercase extension. */
|
|
37
|
+
families: Array<{ exts: string[]; palette: TilePalette }>;
|
|
38
|
+
audio: TilePalette;
|
|
39
|
+
/** Alternating waveform bar tone (depth without gradients). */
|
|
40
|
+
audioBarSoft: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Built from the LIVE shell palette so every tile tint flips with the theme
|
|
44
|
+
// (deep navy shades in dark, soft pastels in light) — the neutral fallback keeps
|
|
45
|
+
// the hue of the old placeholder tile so unknown types still sit naturally.
|
|
46
|
+
const makeTilePalettes = (palette: LibraryPalette): TilePalettes => ({
|
|
47
|
+
default: { bg: palette.surfaceRaised, accent: palette.isDark ? palette.accentPeriwinkle : palette.accentIndigo },
|
|
48
|
+
families: [
|
|
49
|
+
{ exts: ['pdf'], palette: { bg: palette.surfaceRoseTint, accent: palette.accentRose } },
|
|
50
|
+
{
|
|
51
|
+
exts: ['doc', 'docx', 'rtf', 'odt', 'txt'],
|
|
52
|
+
palette: { bg: palette.surfaceNavy, accent: palette.accentSky },
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
exts: ['xls', 'xlsx', 'csv', 'ods'],
|
|
56
|
+
palette: { bg: palette.surfaceGreenTint, accent: palette.accentMint },
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
exts: ['ppt', 'pptx', 'odp'],
|
|
60
|
+
palette: { bg: palette.surfaceAmberTint, accent: palette.accentAmber },
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
exts: ['zip', 'rar', '7z', 'tar', 'gz'],
|
|
64
|
+
palette: { bg: palette.surfacePlum, accent: palette.accentLavender },
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
audio: { bg: palette.surfaceViolet, accent: palette.accentViolet },
|
|
68
|
+
audioBarSoft: palette.accentPurpleMuted,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const paletteForExtension = (palettes: TilePalettes, ext: string): TilePalette =>
|
|
72
|
+
palettes.families.find((family) => family.exts.includes(ext.toLowerCase()))
|
|
73
|
+
?.palette ?? palettes.default;
|
|
74
|
+
|
|
75
|
+
// MIME subtypes whose uppercase form would mislead as a badge (audio/mpeg is an
|
|
76
|
+
// .mp3, audio/x-wav a .wav, …). Applied only when no file extension is available.
|
|
77
|
+
const MIME_SUBTYPE_FIXUPS: Record<string, string> = {
|
|
78
|
+
MPEG: 'MP3',
|
|
79
|
+
MP4: 'M4A',
|
|
80
|
+
'X-M4A': 'M4A',
|
|
81
|
+
'X-WAV': 'WAV',
|
|
82
|
+
WAVE: 'WAV',
|
|
83
|
+
'X-FLAC': 'FLAC',
|
|
84
|
+
'X-OGG': 'OGG',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/** Uppercase extension from the file name, then the MIME subtype, else fallback. */
|
|
88
|
+
function extensionOf(
|
|
89
|
+
fileName: string | undefined,
|
|
90
|
+
mimeType: string | undefined,
|
|
91
|
+
fallback: string
|
|
92
|
+
): string {
|
|
93
|
+
if (fileName) {
|
|
94
|
+
const dot = fileName.lastIndexOf('.');
|
|
95
|
+
if (dot >= 0 && dot < fileName.length - 1) {
|
|
96
|
+
return fileName.slice(dot + 1).toUpperCase();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (mimeType) {
|
|
100
|
+
const slash = mimeType.lastIndexOf('/');
|
|
101
|
+
if (slash >= 0 && slash < mimeType.length - 1) {
|
|
102
|
+
const subtype = mimeType.slice(slash + 1).toUpperCase();
|
|
103
|
+
return MIME_SUBTYPE_FIXUPS[subtype] ?? subtype;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return fallback;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Deterministic pseudo-waveform: heights (0..1) derived from the asset key via a
|
|
111
|
+
* tiny LCG, so every audio file gets its own bar pattern that never changes
|
|
112
|
+
* between renders or app launches — no Math.random, no re-render flicker.
|
|
113
|
+
*/
|
|
114
|
+
function waveformFor(key: string, count = 7): number[] {
|
|
115
|
+
let seed = 0;
|
|
116
|
+
for (let i = 0; i < key.length; i++) {
|
|
117
|
+
seed = (seed * 31 + key.charCodeAt(i)) >>> 0;
|
|
118
|
+
}
|
|
119
|
+
const heights: number[] = [];
|
|
120
|
+
for (let i = 0; i < count; i++) {
|
|
121
|
+
seed = (seed * 1664525 + 1013904223) >>> 0;
|
|
122
|
+
heights.push(0.25 + ((seed >>> 8) % 1000) / 1000 * 0.75);
|
|
123
|
+
}
|
|
124
|
+
return heights;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Solid extension chip anchored to the tile's bottom edge. */
|
|
128
|
+
const TypeBadge = ({ label, accent }: { label: string; accent: string }) => {
|
|
129
|
+
const palette = useLibraryPalette();
|
|
130
|
+
const styles = useMemo(() => makeStyles(palette), [palette]);
|
|
131
|
+
return (
|
|
132
|
+
<View style={[styles.badge, { backgroundColor: accent }]}>
|
|
133
|
+
<Text style={styles.badgeText} numberOfLines={1}>
|
|
134
|
+
{label.slice(0, 4)}
|
|
135
|
+
</Text>
|
|
136
|
+
</View>
|
|
137
|
+
);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/** The name the published file actually has on disk, when the path carries one. */
|
|
141
|
+
function fileNameFromUri(asset: CameraAsset): string | undefined {
|
|
142
|
+
const uri = asset.documentUri || asset.uri;
|
|
143
|
+
if (!uri) return undefined;
|
|
144
|
+
const segment = (uri.split(/[?#]/)[0] ?? '').split(/[\\/]/).pop();
|
|
145
|
+
if (!segment) return undefined;
|
|
146
|
+
const dot = segment.lastIndexOf('.');
|
|
147
|
+
return dot > 0 && dot < segment.length - 1 ? segment : undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const DocumentTile = ({ asset }: { asset: CameraAsset }) => {
|
|
151
|
+
const { t } = useCameraLocalization();
|
|
152
|
+
const palette = useLibraryPalette();
|
|
153
|
+
const styles = useMemo(() => makeStyles(palette), [palette]);
|
|
154
|
+
const palettes = useMemo(() => makeTilePalettes(palette), [palette]);
|
|
155
|
+
// The FILE names the badge, ahead of `fileName`/`mimeType`: an image exported to PDF can still
|
|
156
|
+
// carry its source photo's name and type, and a PDF badged "JPG" describes the wrong file.
|
|
157
|
+
const ext = extensionOf(
|
|
158
|
+
fileNameFromUri(asset) ?? asset.fileName,
|
|
159
|
+
asset.mimeType,
|
|
160
|
+
t('fileTypeGeneric')
|
|
161
|
+
);
|
|
162
|
+
const tile = paletteForExtension(palettes, ext);
|
|
163
|
+
return (
|
|
164
|
+
<View style={[styles.tile, { backgroundColor: tile.bg }]}>
|
|
165
|
+
{/* Page glyph sourced from the centralized icon registry (host-overridable
|
|
166
|
+
by the `document` key); the family accent colour is applied as tint so
|
|
167
|
+
the tile stays colour-coded per file type. */}
|
|
168
|
+
<View style={styles.glyphWrap}>
|
|
169
|
+
<RenderIcon
|
|
170
|
+
name="document"
|
|
171
|
+
size={ms(26)}
|
|
172
|
+
color={tile.accent}
|
|
173
|
+
strokeWidth={STROKE}
|
|
174
|
+
/>
|
|
175
|
+
</View>
|
|
176
|
+
<TypeBadge label={ext} accent={tile.accent} />
|
|
177
|
+
</View>
|
|
178
|
+
);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export const AudioTile = ({ asset }: { asset: CameraAsset }) => {
|
|
182
|
+
const { t } = useCameraLocalization();
|
|
183
|
+
const palette = useLibraryPalette();
|
|
184
|
+
const styles = useMemo(() => makeStyles(palette), [palette]);
|
|
185
|
+
const palettes = useMemo(() => makeTilePalettes(palette), [palette]);
|
|
186
|
+
const audio = palettes.audio;
|
|
187
|
+
const ext = extensionOf(asset.fileName, asset.mimeType, t('audioTypeGeneric'));
|
|
188
|
+
const bars = waveformFor(asset.id || asset.fileName || 'audio');
|
|
189
|
+
return (
|
|
190
|
+
<View style={[styles.tile, { backgroundColor: audio.bg }]}>
|
|
191
|
+
<View style={styles.waveRow}>
|
|
192
|
+
{bars.map((height, index) => (
|
|
193
|
+
<View
|
|
194
|
+
key={index}
|
|
195
|
+
style={[
|
|
196
|
+
styles.waveBar,
|
|
197
|
+
{
|
|
198
|
+
height: ms(7) + Math.round(height * ms(18)),
|
|
199
|
+
backgroundColor:
|
|
200
|
+
index % 2 === 0 ? audio.accent : palettes.audioBarSoft,
|
|
201
|
+
},
|
|
202
|
+
]}
|
|
203
|
+
/>
|
|
204
|
+
))}
|
|
205
|
+
</View>
|
|
206
|
+
<TypeBadge label={ext} accent={audio.accent} />
|
|
207
|
+
</View>
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const STROKE = 1.5;
|
|
212
|
+
|
|
213
|
+
const makeStyles = (palette: LibraryPalette) =>
|
|
214
|
+
StyleSheet.create({
|
|
215
|
+
// Fills the list's fixed thumbnailWrapper (which clips to its own radius).
|
|
216
|
+
tile: {
|
|
217
|
+
flex: 1,
|
|
218
|
+
width: '100%',
|
|
219
|
+
alignItems: 'center',
|
|
220
|
+
justifyContent: 'center',
|
|
221
|
+
},
|
|
222
|
+
// Lifts the registry glyph so it + the bottom badge read balanced.
|
|
223
|
+
glyphWrap: {
|
|
224
|
+
marginBottom: ms(12),
|
|
225
|
+
},
|
|
226
|
+
waveRow: {
|
|
227
|
+
flexDirection: 'row',
|
|
228
|
+
alignItems: 'center',
|
|
229
|
+
gap: ms(3),
|
|
230
|
+
marginBottom: ms(12),
|
|
231
|
+
},
|
|
232
|
+
waveBar: {
|
|
233
|
+
width: ms(3),
|
|
234
|
+
borderRadius: ms(2),
|
|
235
|
+
},
|
|
236
|
+
badge: {
|
|
237
|
+
position: 'absolute',
|
|
238
|
+
bottom: ms(5),
|
|
239
|
+
paddingHorizontal: ms(5),
|
|
240
|
+
paddingVertical: ms(1.5),
|
|
241
|
+
borderRadius: ms(4),
|
|
242
|
+
},
|
|
243
|
+
badgeText: {
|
|
244
|
+
// Dark glyph on the bright accent chip in BOTH themes: the accent fills are light
|
|
245
|
+
// pastels, so a light label (appBackground in light mode) would wash out on them.
|
|
246
|
+
color: palette.isDark ? palette.appBackground : palette.inkDark,
|
|
247
|
+
fontSize: fs(8),
|
|
248
|
+
fontWeight: '800',
|
|
249
|
+
letterSpacing: 0.4,
|
|
250
|
+
includeFontPadding: false,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImagePreview — in-app image preview for an image asset in the full-screen viewer.
|
|
3
|
+
*
|
|
4
|
+
* Displays a metadata header (file name, size, date, time, dimensions) using the
|
|
5
|
+
* standardized metadata header component, with the full image below using contain mode.
|
|
6
|
+
* Handles all image types including HEIF/AVIF and animated GIFs through native decoders.
|
|
7
|
+
*/
|
|
8
|
+
import { View, Image, StyleSheet } from 'react-native';
|
|
9
|
+
import type { CameraAsset } from '../../types';
|
|
10
|
+
import { NativeAnimatedImageView } from '../../NativeAnimatedImageView';
|
|
11
|
+
import { isGifAsset, requiresNativeImageDecoder } from '../../utils';
|
|
12
|
+
|
|
13
|
+
export const ImagePreview = ({
|
|
14
|
+
asset,
|
|
15
|
+
}: {
|
|
16
|
+
asset: CameraAsset;
|
|
17
|
+
}) => {
|
|
18
|
+
const previewUri = asset.documentUri || asset.uri;
|
|
19
|
+
// Determine which renderer to use based on image format
|
|
20
|
+
const isGif = isGifAsset(asset);
|
|
21
|
+
const needsNativeDecoder = requiresNativeImageDecoder(asset);
|
|
22
|
+
const useNativeDecoder = isGif || needsNativeDecoder;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<View style={styles.wrap}>
|
|
26
|
+
<View style={styles.imageWrapper}>
|
|
27
|
+
{useNativeDecoder ? (
|
|
28
|
+
<NativeAnimatedImageView
|
|
29
|
+
src={previewUri}
|
|
30
|
+
style={styles.image}
|
|
31
|
+
resizeMode="contain"
|
|
32
|
+
active={true}
|
|
33
|
+
/>
|
|
34
|
+
) : (
|
|
35
|
+
<Image
|
|
36
|
+
source={{ uri: previewUri }}
|
|
37
|
+
style={styles.image}
|
|
38
|
+
resizeMode="contain"
|
|
39
|
+
// Decode to the slide's size instead of the file's full resolution: a 12MP still
|
|
40
|
+
// decoded whole is ~48MB of bitmap, and the viewer keeps neighbouring slides
|
|
41
|
+
// mounted. No-op on iOS.
|
|
42
|
+
resizeMethod="resize"
|
|
43
|
+
/>
|
|
44
|
+
)}
|
|
45
|
+
</View>
|
|
46
|
+
</View>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// No backgroundColor anywhere in this tree: the slide sits directly on the viewer's single
|
|
51
|
+
// screen background (s.viewerContainer). The header used to sit on `surface` and the media on
|
|
52
|
+
// `surfaceRaised`, which read as two stacked colour blocks — and because the image is fitted
|
|
53
|
+
// with `contain`, the raised block showed as empty strips above and below it. Painting nothing
|
|
54
|
+
// lets the one screen colour run edge to edge behind both.
|
|
55
|
+
const styles = StyleSheet.create({
|
|
56
|
+
wrap: {
|
|
57
|
+
flex: 1,
|
|
58
|
+
width: '100%',
|
|
59
|
+
overflow: 'hidden',
|
|
60
|
+
},
|
|
61
|
+
imageWrapper: {
|
|
62
|
+
flex: 1,
|
|
63
|
+
justifyContent: 'center',
|
|
64
|
+
alignItems: 'center',
|
|
65
|
+
overflow: 'hidden',
|
|
66
|
+
},
|
|
67
|
+
image: {
|
|
68
|
+
flex: 1,
|
|
69
|
+
width: '100%',
|
|
70
|
+
height: '100%',
|
|
71
|
+
},
|
|
72
|
+
});
|