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.
Files changed (506) hide show
  1. package/CustomCamera.podspec +20 -0
  2. package/LICENSE +20 -0
  3. package/README.md +833 -0
  4. package/android/build.gradle +135 -0
  5. package/android/consumer-rules.pro +47 -0
  6. package/android/src/main/AndroidManifest.xml +72 -0
  7. package/android/src/main/java/android/print/PdfPrint.java +51 -0
  8. package/android/src/main/java/com/customcamera/AnimatedImageViewManager.kt +199 -0
  9. package/android/src/main/java/com/customcamera/AudioPlayerView.kt +322 -0
  10. package/android/src/main/java/com/customcamera/AudioRecorder.kt +506 -0
  11. package/android/src/main/java/com/customcamera/BarcodeScannerActivity.kt +361 -0
  12. package/android/src/main/java/com/customcamera/BlurBackgroundView.kt +199 -0
  13. package/android/src/main/java/com/customcamera/CacheCleanupManager.kt +386 -0
  14. package/android/src/main/java/com/customcamera/CameraAspectRatioMode.kt +77 -0
  15. package/android/src/main/java/com/customcamera/CameraAsset.kt +211 -0
  16. package/android/src/main/java/com/customcamera/CameraTheme.kt +479 -0
  17. package/android/src/main/java/com/customcamera/CompressionCacheStore.kt +504 -0
  18. package/android/src/main/java/com/customcamera/CompressionFileNameUtils.kt +7 -0
  19. package/android/src/main/java/com/customcamera/CompressionProfile.kt +195 -0
  20. package/android/src/main/java/com/customcamera/CustomCameraActivity.kt +7291 -0
  21. package/android/src/main/java/com/customcamera/CustomCameraFileProvider.kt +9 -0
  22. package/android/src/main/java/com/customcamera/CustomCameraModule.kt +2479 -0
  23. package/android/src/main/java/com/customcamera/CustomCameraPackage.kt +53 -0
  24. package/android/src/main/java/com/customcamera/DisplayUtils.kt +9 -0
  25. package/android/src/main/java/com/customcamera/DocumentPreviewView.kt +846 -0
  26. package/android/src/main/java/com/customcamera/DrawingView.kt +214 -0
  27. package/android/src/main/java/com/customcamera/EmojiPickerData.kt +228 -0
  28. package/android/src/main/java/com/customcamera/FilterStripAdapter.kt +158 -0
  29. package/android/src/main/java/com/customcamera/GifSupport.kt +215 -0
  30. package/android/src/main/java/com/customcamera/GlobalAudioPlayer.kt +320 -0
  31. package/android/src/main/java/com/customcamera/ImageExporter.kt +1394 -0
  32. package/android/src/main/java/com/customcamera/ImageFormatSupport.kt +163 -0
  33. package/android/src/main/java/com/customcamera/Localization.kt +206 -0
  34. package/android/src/main/java/com/customcamera/MediaAdapters.kt +758 -0
  35. package/android/src/main/java/com/customcamera/MediaCacheManager.kt +782 -0
  36. package/android/src/main/java/com/customcamera/MediaExportFormats.kt +201 -0
  37. package/android/src/main/java/com/customcamera/MediaFileIo.kt +310 -0
  38. package/android/src/main/java/com/customcamera/MediaMetadataStore.kt +494 -0
  39. package/android/src/main/java/com/customcamera/MediaPreviewEditor.kt +14865 -0
  40. package/android/src/main/java/com/customcamera/MediaPublisher.kt +423 -0
  41. package/android/src/main/java/com/customcamera/MediaStorageWorkflow.kt +722 -0
  42. package/android/src/main/java/com/customcamera/MediaStoreRepository.kt +378 -0
  43. package/android/src/main/java/com/customcamera/MediaThumbnailView.kt +286 -0
  44. package/android/src/main/java/com/customcamera/MediaUtils.kt +37 -0
  45. package/android/src/main/java/com/customcamera/NativeVideoViewManager.kt +845 -0
  46. package/android/src/main/java/com/customcamera/OfficeDocumentConverter.kt +2791 -0
  47. package/android/src/main/java/com/customcamera/PendingCaptureCrops.kt +88 -0
  48. package/android/src/main/java/com/customcamera/PowerPointPreview.kt +4077 -0
  49. package/android/src/main/java/com/customcamera/RecentPickerStore.kt +74 -0
  50. package/android/src/main/java/com/customcamera/SecureFileEraser.kt +411 -0
  51. package/android/src/main/java/com/customcamera/SecureKeyModule.kt +88 -0
  52. package/android/src/main/java/com/customcamera/SecureMediaDeleter.kt +493 -0
  53. package/android/src/main/java/com/customcamera/StickerPickerData.kt +107 -0
  54. package/android/src/main/java/com/customcamera/SvgDrawable.kt +175 -0
  55. package/android/src/main/java/com/customcamera/SvgIconViewManager.kt +77 -0
  56. package/android/src/main/java/com/customcamera/TextEntryController.kt +553 -0
  57. package/android/src/main/java/com/customcamera/ThumbnailLoader.kt +455 -0
  58. package/android/src/main/java/com/customcamera/UploadService.kt +909 -0
  59. package/android/src/main/java/com/customcamera/VideoEditUtils.kt +210 -0
  60. package/android/src/main/java/com/customcamera/VideoRotationTranscoder.kt +1992 -0
  61. package/android/src/main/java/com/customcamera/gif/AnimatedGifEncoder.kt +473 -0
  62. package/android/src/main/java/com/customcamera/gif/GifDecoder.kt +690 -0
  63. package/android/src/main/java/com/customcamera/gif/GifFramesDrawable.kt +160 -0
  64. package/android/src/main/java/com/customcamera/gif/GifHttp.kt +112 -0
  65. package/android/src/main/java/com/customcamera/gif/GifImageLoader.kt +186 -0
  66. package/android/src/main/java/com/customcamera/gif/GifModels.kt +74 -0
  67. package/android/src/main/java/com/customcamera/gif/GifProvider.kt +49 -0
  68. package/android/src/main/java/com/customcamera/gif/GifProviders.kt +27 -0
  69. package/android/src/main/java/com/customcamera/gif/GifRepository.kt +124 -0
  70. package/android/src/main/java/com/customcamera/gif/GifSafety.kt +33 -0
  71. package/android/src/main/java/com/customcamera/gif/GiphyGifProvider.kt +146 -0
  72. package/android/src/main/java/com/customcamera/gif/LZWEncoder.kt +258 -0
  73. package/android/src/main/java/com/customcamera/gif/NeuQuant.kt +435 -0
  74. package/android/src/main/java/com/customcamera/gif/TenorGifProvider.kt +147 -0
  75. package/android/src/main/res/drawable/bg_brush_selected.xml +5 -0
  76. package/android/src/main/res/drawable/bg_color_swatch.xml +5 -0
  77. package/android/src/main/res/drawable/bg_editor_toolbar.xml +6 -0
  78. package/android/src/main/res/drawable/bg_icon_circle.xml +5 -0
  79. package/android/src/main/res/drawable/bg_icon_circle_light.xml +5 -0
  80. package/android/src/main/res/drawable/bg_mode_tab_active.xml +6 -0
  81. package/android/src/main/res/drawable/bg_pill.xml +6 -0
  82. package/android/src/main/res/drawable/bg_recording_dot.xml +6 -0
  83. package/android/src/main/res/drawable/bg_selection_badge.xml +8 -0
  84. package/android/src/main/res/drawable/bg_send_button.xml +5 -0
  85. package/android/src/main/res/drawable/bg_shutter_inner.xml +5 -0
  86. package/android/src/main/res/drawable/bg_shutter_recording.xml +6 -0
  87. package/android/src/main/res/drawable/bg_shutter_ring.xml +8 -0
  88. package/android/src/main/res/drawable/bg_shutter_video_idle.xml +5 -0
  89. package/android/src/main/res/drawable/bg_thumbnail.xml +9 -0
  90. package/android/src/main/res/drawable/ic_aspect_ratio.xml +17 -0
  91. package/android/src/main/res/drawable/ic_audio.xml +10 -0
  92. package/android/src/main/res/drawable/ic_back.xml +10 -0
  93. package/android/src/main/res/drawable/ic_brush_medium.xml +12 -0
  94. package/android/src/main/res/drawable/ic_brush_thick.xml +12 -0
  95. package/android/src/main/res/drawable/ic_brush_thin.xml +12 -0
  96. package/android/src/main/res/drawable/ic_check.xml +17 -0
  97. package/android/src/main/res/drawable/ic_chevron_down.xml +10 -0
  98. package/android/src/main/res/drawable/ic_chevron_up.xml +13 -0
  99. package/android/src/main/res/drawable/ic_clear_drawing.xml +10 -0
  100. package/android/src/main/res/drawable/ic_close.xml +10 -0
  101. package/android/src/main/res/drawable/ic_cover.xml +9 -0
  102. package/android/src/main/res/drawable/ic_crop.xml +10 -0
  103. package/android/src/main/res/drawable/ic_delete.xml +9 -0
  104. package/android/src/main/res/drawable/ic_document.xml +10 -0
  105. package/android/src/main/res/drawable/ic_done.xml +10 -0
  106. package/android/src/main/res/drawable/ic_download.xml +10 -0
  107. package/android/src/main/res/drawable/ic_emoji.xml +10 -0
  108. package/android/src/main/res/drawable/ic_filter.xml +10 -0
  109. package/android/src/main/res/drawable/ic_flash_auto.xml +7 -0
  110. package/android/src/main/res/drawable/ic_flash_off.xml +23 -0
  111. package/android/src/main/res/drawable/ic_flash_on.xml +16 -0
  112. package/android/src/main/res/drawable/ic_flip.xml +15 -0
  113. package/android/src/main/res/drawable/ic_gallery.xml +10 -0
  114. package/android/src/main/res/drawable/ic_gif.xml +24 -0
  115. package/android/src/main/res/drawable/ic_lock.xml +19 -0
  116. package/android/src/main/res/drawable/ic_lock_open.xml +22 -0
  117. package/android/src/main/res/drawable/ic_marker.xml +10 -0
  118. package/android/src/main/res/drawable/ic_more_vert.xml +18 -0
  119. package/android/src/main/res/drawable/ic_overlay_close.xml +19 -0
  120. package/android/src/main/res/drawable/ic_overlay_resize.xml +18 -0
  121. package/android/src/main/res/drawable/ic_pause.xml +10 -0
  122. package/android/src/main/res/drawable/ic_photo_mode.xml +10 -0
  123. package/android/src/main/res/drawable/ic_play.xml +10 -0
  124. package/android/src/main/res/drawable/ic_redo.xml +10 -0
  125. package/android/src/main/res/drawable/ic_rotate.xml +9 -0
  126. package/android/src/main/res/drawable/ic_send.xml +10 -0
  127. package/android/src/main/res/drawable/ic_share.xml +10 -0
  128. package/android/src/main/res/drawable/ic_stat_upload.xml +10 -0
  129. package/android/src/main/res/drawable/ic_sticker.xml +24 -0
  130. package/android/src/main/res/drawable/ic_text.xml +10 -0
  131. package/android/src/main/res/drawable/ic_text_align_center.xml +11 -0
  132. package/android/src/main/res/drawable/ic_text_align_left.xml +11 -0
  133. package/android/src/main/res/drawable/ic_text_align_right.xml +11 -0
  134. package/android/src/main/res/drawable/ic_text_bg.xml +11 -0
  135. package/android/src/main/res/drawable/ic_trim.xml +23 -0
  136. package/android/src/main/res/drawable/ic_undo.xml +10 -0
  137. package/android/src/main/res/drawable/ic_video_mode.xml +10 -0
  138. package/android/src/main/res/drawable/ic_video_note.xml +13 -0
  139. package/android/src/main/res/drawable/ic_volume_off.xml +9 -0
  140. package/android/src/main/res/drawable/ic_volume_on.xml +9 -0
  141. package/android/src/main/res/drawable/sticker_heart.xml +9 -0
  142. package/android/src/main/res/drawable/sticker_smile.xml +9 -0
  143. package/android/src/main/res/drawable/sticker_star.xml +9 -0
  144. package/android/src/main/res/drawable/sticker_thumbsup.xml +9 -0
  145. package/android/src/main/res/values/colors.xml +34 -0
  146. package/android/src/main/res/values/ids.xml +15 -0
  147. package/android/src/main/res/values/strings.xml +15 -0
  148. package/android/src/main/res/values/styles.xml +20 -0
  149. package/android/src/main/res/xml/file_paths.xml +34 -0
  150. package/ios/CustomCamera.h +5 -0
  151. package/ios/CustomCamera.mm +214 -0
  152. package/lib/module/AudioPlayerContext.js +233 -0
  153. package/lib/module/AudioPlayerContext.js.map +1 -0
  154. package/lib/module/CameraPreview.js +304 -0
  155. package/lib/module/CameraPreview.js.map +1 -0
  156. package/lib/module/NativeAnimatedImageView.js +41 -0
  157. package/lib/module/NativeAnimatedImageView.js.map +1 -0
  158. package/lib/module/NativeAudioPlayerView.js +33 -0
  159. package/lib/module/NativeAudioPlayerView.js.map +1 -0
  160. package/lib/module/NativeBlurView.js +35 -0
  161. package/lib/module/NativeBlurView.js.map +1 -0
  162. package/lib/module/NativeCustomCamera.js +128 -0
  163. package/lib/module/NativeCustomCamera.js.map +1 -0
  164. package/lib/module/NativeCustomCamera.web.js +143 -0
  165. package/lib/module/NativeCustomCamera.web.js.map +1 -0
  166. package/lib/module/NativeCustomVideoView.js +5 -0
  167. package/lib/module/NativeCustomVideoView.js.map +1 -0
  168. package/lib/module/NativeDocumentPreviewView.js +156 -0
  169. package/lib/module/NativeDocumentPreviewView.js.map +1 -0
  170. package/lib/module/NativeMediaThumbnailView.js +110 -0
  171. package/lib/module/NativeMediaThumbnailView.js.map +1 -0
  172. package/lib/module/NativeSvgIcon.js +32 -0
  173. package/lib/module/NativeSvgIcon.js.map +1 -0
  174. package/lib/module/ResponsivePreviewContainer.js +171 -0
  175. package/lib/module/ResponsivePreviewContainer.js.map +1 -0
  176. package/lib/module/accessibility.js +61 -0
  177. package/lib/module/accessibility.js.map +1 -0
  178. package/lib/module/config.js +352 -0
  179. package/lib/module/config.js.map +1 -0
  180. package/lib/module/cpuOptimization.js +296 -0
  181. package/lib/module/cpuOptimization.js.map +1 -0
  182. package/lib/module/db/DatabaseManager.js +191 -0
  183. package/lib/module/db/DatabaseManager.js.map +1 -0
  184. package/lib/module/db/MediaMapper.js +284 -0
  185. package/lib/module/db/MediaMapper.js.map +1 -0
  186. package/lib/module/db/MediaRepository.js +242 -0
  187. package/lib/module/db/MediaRepository.js.map +1 -0
  188. package/lib/module/db/index.js +41 -0
  189. package/lib/module/db/index.js.map +1 -0
  190. package/lib/module/db/migrations.js +180 -0
  191. package/lib/module/db/migrations.js.map +1 -0
  192. package/lib/module/db/react-native-sqlcipher.d.js +2 -0
  193. package/lib/module/db/react-native-sqlcipher.d.js.map +1 -0
  194. package/lib/module/db/secureKey.js +87 -0
  195. package/lib/module/db/secureKey.js.map +1 -0
  196. package/lib/module/db/types.js +57 -0
  197. package/lib/module/db/types.js.map +1 -0
  198. package/lib/module/db/useMediaDatabase.js +123 -0
  199. package/lib/module/db/useMediaDatabase.js.map +1 -0
  200. package/lib/module/defaultIcons.js +2259 -0
  201. package/lib/module/defaultIcons.js.map +1 -0
  202. package/lib/module/deviceCapabilities.js +408 -0
  203. package/lib/module/deviceCapabilities.js.map +1 -0
  204. package/lib/module/errors.js +267 -0
  205. package/lib/module/errors.js.map +1 -0
  206. package/lib/module/iconConfig.js +796 -0
  207. package/lib/module/iconConfig.js.map +1 -0
  208. package/lib/module/icons.js +514 -0
  209. package/lib/module/icons.js.map +1 -0
  210. package/lib/module/index.js +251 -0
  211. package/lib/module/index.js.map +1 -0
  212. package/lib/module/jest-globals.d.js +2 -0
  213. package/lib/module/jest-globals.d.js.map +1 -0
  214. package/lib/module/library/MediaLibrary.js +107 -0
  215. package/lib/module/library/MediaLibrary.js.map +1 -0
  216. package/lib/module/library/MediaLibraryScreen.js +2597 -0
  217. package/lib/module/library/MediaLibraryScreen.js.map +1 -0
  218. package/lib/module/library/components/AudioPreview.js +74 -0
  219. package/lib/module/library/components/AudioPreview.js.map +1 -0
  220. package/lib/module/library/components/DocumentPreview.js +331 -0
  221. package/lib/module/library/components/DocumentPreview.js.map +1 -0
  222. package/lib/module/library/components/ExpandableFab.js +343 -0
  223. package/lib/module/library/components/ExpandableFab.js.map +1 -0
  224. package/lib/module/library/components/FileTiles.js +251 -0
  225. package/lib/module/library/components/FileTiles.js.map +1 -0
  226. package/lib/module/library/components/ImagePreview.js +70 -0
  227. package/lib/module/library/components/ImagePreview.js.map +1 -0
  228. package/lib/module/library/components/MediaMetadataHeader.js +175 -0
  229. package/lib/module/library/components/MediaMetadataHeader.js.map +1 -0
  230. package/lib/module/library/components/RecordAudioScreen.js +816 -0
  231. package/lib/module/library/components/RecordAudioScreen.js.map +1 -0
  232. package/lib/module/library/components/VideoPreview.js +453 -0
  233. package/lib/module/library/components/VideoPreview.js.map +1 -0
  234. package/lib/module/library/components/ViewerCounter.js +55 -0
  235. package/lib/module/library/components/ViewerCounter.js.map +1 -0
  236. package/lib/module/library/compressionEstimate.js +507 -0
  237. package/lib/module/library/compressionEstimate.js.map +1 -0
  238. package/lib/module/library/fileMetadata.js +196 -0
  239. package/lib/module/library/fileMetadata.js.map +1 -0
  240. package/lib/module/library/index.js +42 -0
  241. package/lib/module/library/index.js.map +1 -0
  242. package/lib/module/library/libraryIconRegistry.js +69 -0
  243. package/lib/module/library/libraryIconRegistry.js.map +1 -0
  244. package/lib/module/library/libraryIcons.js +858 -0
  245. package/lib/module/library/libraryIcons.js.map +1 -0
  246. package/lib/module/library/localizedFormat.js +181 -0
  247. package/lib/module/library/localizedFormat.js.map +1 -0
  248. package/lib/module/library/palette.js +225 -0
  249. package/lib/module/library/palette.js.map +1 -0
  250. package/lib/module/library/responsive.js +53 -0
  251. package/lib/module/library/responsive.js.map +1 -0
  252. package/lib/module/locales.js +416 -0
  253. package/lib/module/locales.js.map +1 -0
  254. package/lib/module/localization.js +1064 -0
  255. package/lib/module/localization.js.map +1 -0
  256. package/lib/module/logger.js +20 -0
  257. package/lib/module/logger.js.map +1 -0
  258. package/lib/module/lowMemoryCache.js +206 -0
  259. package/lib/module/lowMemoryCache.js.map +1 -0
  260. package/lib/module/mediaHistory.js +33 -0
  261. package/lib/module/mediaHistory.js.map +1 -0
  262. package/lib/module/mediaStorageService.js +402 -0
  263. package/lib/module/mediaStorageService.js.map +1 -0
  264. package/lib/module/memory.js +79 -0
  265. package/lib/module/memory.js.map +1 -0
  266. package/lib/module/nativeActions.js +307 -0
  267. package/lib/module/nativeActions.js.map +1 -0
  268. package/lib/module/nativeComponentSupport.js +61 -0
  269. package/lib/module/nativeComponentSupport.js.map +1 -0
  270. package/lib/module/networkPerformance.js +24 -0
  271. package/lib/module/networkPerformance.js.map +1 -0
  272. package/lib/module/package.json +1 -0
  273. package/lib/module/performance.js +104 -0
  274. package/lib/module/performance.js.map +1 -0
  275. package/lib/module/performanceMonitor.js +155 -0
  276. package/lib/module/performanceMonitor.js.map +1 -0
  277. package/lib/module/permissions.js +370 -0
  278. package/lib/module/permissions.js.map +1 -0
  279. package/lib/module/rendering.js +40 -0
  280. package/lib/module/rendering.js.map +1 -0
  281. package/lib/module/security.js +18 -0
  282. package/lib/module/security.js.map +1 -0
  283. package/lib/module/state.js +266 -0
  284. package/lib/module/state.js.map +1 -0
  285. package/lib/module/storage.js +400 -0
  286. package/lib/module/storage.js.map +1 -0
  287. package/lib/module/theme.js +390 -0
  288. package/lib/module/theme.js.map +1 -0
  289. package/lib/module/types.js +4 -0
  290. package/lib/module/types.js.map +1 -0
  291. package/lib/module/utils.js +115 -0
  292. package/lib/module/utils.js.map +1 -0
  293. package/lib/module/validation.js +288 -0
  294. package/lib/module/validation.js.map +1 -0
  295. package/lib/typescript/package.json +1 -0
  296. package/lib/typescript/src/AudioPlayerContext.d.ts +27 -0
  297. package/lib/typescript/src/AudioPlayerContext.d.ts.map +1 -0
  298. package/lib/typescript/src/CameraPreview.d.ts +13 -0
  299. package/lib/typescript/src/CameraPreview.d.ts.map +1 -0
  300. package/lib/typescript/src/NativeAnimatedImageView.d.ts +33 -0
  301. package/lib/typescript/src/NativeAnimatedImageView.d.ts.map +1 -0
  302. package/lib/typescript/src/NativeAudioPlayerView.d.ts +12 -0
  303. package/lib/typescript/src/NativeAudioPlayerView.d.ts.map +1 -0
  304. package/lib/typescript/src/NativeBlurView.d.ts +12 -0
  305. package/lib/typescript/src/NativeBlurView.d.ts.map +1 -0
  306. package/lib/typescript/src/NativeCustomCamera.d.ts +167 -0
  307. package/lib/typescript/src/NativeCustomCamera.d.ts.map +1 -0
  308. package/lib/typescript/src/NativeCustomCamera.web.d.ts +47 -0
  309. package/lib/typescript/src/NativeCustomCamera.web.d.ts.map +1 -0
  310. package/lib/typescript/src/NativeCustomVideoView.d.ts +30 -0
  311. package/lib/typescript/src/NativeCustomVideoView.d.ts.map +1 -0
  312. package/lib/typescript/src/NativeDocumentPreviewView.d.ts +48 -0
  313. package/lib/typescript/src/NativeDocumentPreviewView.d.ts.map +1 -0
  314. package/lib/typescript/src/NativeMediaThumbnailView.d.ts +54 -0
  315. package/lib/typescript/src/NativeMediaThumbnailView.d.ts.map +1 -0
  316. package/lib/typescript/src/NativeSvgIcon.d.ts +25 -0
  317. package/lib/typescript/src/NativeSvgIcon.d.ts.map +1 -0
  318. package/lib/typescript/src/ResponsivePreviewContainer.d.ts +92 -0
  319. package/lib/typescript/src/ResponsivePreviewContainer.d.ts.map +1 -0
  320. package/lib/typescript/src/accessibility.d.ts +20 -0
  321. package/lib/typescript/src/accessibility.d.ts.map +1 -0
  322. package/lib/typescript/src/config.d.ts +145 -0
  323. package/lib/typescript/src/config.d.ts.map +1 -0
  324. package/lib/typescript/src/cpuOptimization.d.ts +63 -0
  325. package/lib/typescript/src/cpuOptimization.d.ts.map +1 -0
  326. package/lib/typescript/src/db/DatabaseManager.d.ts +48 -0
  327. package/lib/typescript/src/db/DatabaseManager.d.ts.map +1 -0
  328. package/lib/typescript/src/db/MediaMapper.d.ts +77 -0
  329. package/lib/typescript/src/db/MediaMapper.d.ts.map +1 -0
  330. package/lib/typescript/src/db/MediaRepository.d.ts +66 -0
  331. package/lib/typescript/src/db/MediaRepository.d.ts.map +1 -0
  332. package/lib/typescript/src/db/index.d.ts +23 -0
  333. package/lib/typescript/src/db/index.d.ts.map +1 -0
  334. package/lib/typescript/src/db/migrations.d.ts +31 -0
  335. package/lib/typescript/src/db/migrations.d.ts.map +1 -0
  336. package/lib/typescript/src/db/secureKey.d.ts +10 -0
  337. package/lib/typescript/src/db/secureKey.d.ts.map +1 -0
  338. package/lib/typescript/src/db/types.d.ts +115 -0
  339. package/lib/typescript/src/db/types.d.ts.map +1 -0
  340. package/lib/typescript/src/db/useMediaDatabase.d.ts +41 -0
  341. package/lib/typescript/src/db/useMediaDatabase.d.ts.map +1 -0
  342. package/lib/typescript/src/defaultIcons.d.ts +139 -0
  343. package/lib/typescript/src/defaultIcons.d.ts.map +1 -0
  344. package/lib/typescript/src/deviceCapabilities.d.ts +92 -0
  345. package/lib/typescript/src/deviceCapabilities.d.ts.map +1 -0
  346. package/lib/typescript/src/errors.d.ts +95 -0
  347. package/lib/typescript/src/errors.d.ts.map +1 -0
  348. package/lib/typescript/src/iconConfig.d.ts +176 -0
  349. package/lib/typescript/src/iconConfig.d.ts.map +1 -0
  350. package/lib/typescript/src/icons.d.ts +217 -0
  351. package/lib/typescript/src/icons.d.ts.map +1 -0
  352. package/lib/typescript/src/index.d.ts +104 -0
  353. package/lib/typescript/src/index.d.ts.map +1 -0
  354. package/lib/typescript/src/library/MediaLibrary.d.ts +57 -0
  355. package/lib/typescript/src/library/MediaLibrary.d.ts.map +1 -0
  356. package/lib/typescript/src/library/MediaLibraryScreen.d.ts +51 -0
  357. package/lib/typescript/src/library/MediaLibraryScreen.d.ts.map +1 -0
  358. package/lib/typescript/src/library/components/AudioPreview.d.ts +6 -0
  359. package/lib/typescript/src/library/components/AudioPreview.d.ts.map +1 -0
  360. package/lib/typescript/src/library/components/DocumentPreview.d.ts +10 -0
  361. package/lib/typescript/src/library/components/DocumentPreview.d.ts.map +1 -0
  362. package/lib/typescript/src/library/components/ExpandableFab.d.ts +39 -0
  363. package/lib/typescript/src/library/components/ExpandableFab.d.ts.map +1 -0
  364. package/lib/typescript/src/library/components/FileTiles.d.ts +8 -0
  365. package/lib/typescript/src/library/components/FileTiles.d.ts.map +1 -0
  366. package/lib/typescript/src/library/components/ImagePreview.d.ts +5 -0
  367. package/lib/typescript/src/library/components/ImagePreview.d.ts.map +1 -0
  368. package/lib/typescript/src/library/components/MediaMetadataHeader.d.ts +16 -0
  369. package/lib/typescript/src/library/components/MediaMetadataHeader.d.ts.map +1 -0
  370. package/lib/typescript/src/library/components/RecordAudioScreen.d.ts +8 -0
  371. package/lib/typescript/src/library/components/RecordAudioScreen.d.ts.map +1 -0
  372. package/lib/typescript/src/library/components/VideoPreview.d.ts +20 -0
  373. package/lib/typescript/src/library/components/VideoPreview.d.ts.map +1 -0
  374. package/lib/typescript/src/library/components/ViewerCounter.d.ts +13 -0
  375. package/lib/typescript/src/library/components/ViewerCounter.d.ts.map +1 -0
  376. package/lib/typescript/src/library/compressionEstimate.d.ts +313 -0
  377. package/lib/typescript/src/library/compressionEstimate.d.ts.map +1 -0
  378. package/lib/typescript/src/library/fileMetadata.d.ts +33 -0
  379. package/lib/typescript/src/library/fileMetadata.d.ts.map +1 -0
  380. package/lib/typescript/src/library/index.d.ts +40 -0
  381. package/lib/typescript/src/library/index.d.ts.map +1 -0
  382. package/lib/typescript/src/library/libraryIconRegistry.d.ts +35 -0
  383. package/lib/typescript/src/library/libraryIconRegistry.d.ts.map +1 -0
  384. package/lib/typescript/src/library/libraryIcons.d.ts +85 -0
  385. package/lib/typescript/src/library/libraryIcons.d.ts.map +1 -0
  386. package/lib/typescript/src/library/localizedFormat.d.ts +49 -0
  387. package/lib/typescript/src/library/localizedFormat.d.ts.map +1 -0
  388. package/lib/typescript/src/library/palette.d.ts +104 -0
  389. package/lib/typescript/src/library/palette.d.ts.map +1 -0
  390. package/lib/typescript/src/library/responsive.d.ts +15 -0
  391. package/lib/typescript/src/library/responsive.d.ts.map +1 -0
  392. package/lib/typescript/src/locales.d.ts +103 -0
  393. package/lib/typescript/src/locales.d.ts.map +1 -0
  394. package/lib/typescript/src/localization.d.ts +1051 -0
  395. package/lib/typescript/src/localization.d.ts.map +1 -0
  396. package/lib/typescript/src/logger.d.ts +4 -0
  397. package/lib/typescript/src/logger.d.ts.map +1 -0
  398. package/lib/typescript/src/lowMemoryCache.d.ts +64 -0
  399. package/lib/typescript/src/lowMemoryCache.d.ts.map +1 -0
  400. package/lib/typescript/src/mediaHistory.d.ts +3 -0
  401. package/lib/typescript/src/mediaHistory.d.ts.map +1 -0
  402. package/lib/typescript/src/mediaStorageService.d.ts +155 -0
  403. package/lib/typescript/src/mediaStorageService.d.ts.map +1 -0
  404. package/lib/typescript/src/memory.d.ts +21 -0
  405. package/lib/typescript/src/memory.d.ts.map +1 -0
  406. package/lib/typescript/src/nativeActions.d.ts +174 -0
  407. package/lib/typescript/src/nativeActions.d.ts.map +1 -0
  408. package/lib/typescript/src/nativeComponentSupport.d.ts +30 -0
  409. package/lib/typescript/src/nativeComponentSupport.d.ts.map +1 -0
  410. package/lib/typescript/src/networkPerformance.d.ts +12 -0
  411. package/lib/typescript/src/networkPerformance.d.ts.map +1 -0
  412. package/lib/typescript/src/performance.d.ts +32 -0
  413. package/lib/typescript/src/performance.d.ts.map +1 -0
  414. package/lib/typescript/src/performanceMonitor.d.ts +45 -0
  415. package/lib/typescript/src/performanceMonitor.d.ts.map +1 -0
  416. package/lib/typescript/src/permissions.d.ts +132 -0
  417. package/lib/typescript/src/permissions.d.ts.map +1 -0
  418. package/lib/typescript/src/rendering.d.ts +9 -0
  419. package/lib/typescript/src/rendering.d.ts.map +1 -0
  420. package/lib/typescript/src/security.d.ts +6 -0
  421. package/lib/typescript/src/security.d.ts.map +1 -0
  422. package/lib/typescript/src/state.d.ts +102 -0
  423. package/lib/typescript/src/state.d.ts.map +1 -0
  424. package/lib/typescript/src/storage.d.ts +117 -0
  425. package/lib/typescript/src/storage.d.ts.map +1 -0
  426. package/lib/typescript/src/theme.d.ts +15 -0
  427. package/lib/typescript/src/theme.d.ts.map +1 -0
  428. package/lib/typescript/src/types.d.ts +802 -0
  429. package/lib/typescript/src/types.d.ts.map +1 -0
  430. package/lib/typescript/src/utils.d.ts +50 -0
  431. package/lib/typescript/src/utils.d.ts.map +1 -0
  432. package/lib/typescript/src/validation.d.ts +52 -0
  433. package/lib/typescript/src/validation.d.ts.map +1 -0
  434. package/package.json +204 -0
  435. package/react-native.config.js +17 -0
  436. package/src/AudioPlayerContext.tsx +220 -0
  437. package/src/CameraPreview.tsx +352 -0
  438. package/src/NativeAnimatedImageView.tsx +71 -0
  439. package/src/NativeAudioPlayerView.tsx +39 -0
  440. package/src/NativeBlurView.tsx +40 -0
  441. package/src/NativeCustomCamera.ts +305 -0
  442. package/src/NativeCustomCamera.web.ts +192 -0
  443. package/src/NativeCustomVideoView.ts +30 -0
  444. package/src/NativeDocumentPreviewView.tsx +196 -0
  445. package/src/NativeMediaThumbnailView.tsx +167 -0
  446. package/src/NativeSvgIcon.tsx +46 -0
  447. package/src/ResponsivePreviewContainer.tsx +227 -0
  448. package/src/accessibility.ts +80 -0
  449. package/src/config.ts +406 -0
  450. package/src/cpuOptimization.ts +365 -0
  451. package/src/db/DatabaseManager.ts +212 -0
  452. package/src/db/MediaMapper.ts +377 -0
  453. package/src/db/MediaRepository.ts +313 -0
  454. package/src/db/index.ts +73 -0
  455. package/src/db/migrations.ts +222 -0
  456. package/src/db/react-native-sqlcipher.d.ts +76 -0
  457. package/src/db/secureKey.ts +100 -0
  458. package/src/db/types.ts +123 -0
  459. package/src/db/useMediaDatabase.ts +191 -0
  460. package/src/defaultIcons.tsx +2390 -0
  461. package/src/deviceCapabilities.ts +438 -0
  462. package/src/errors.ts +350 -0
  463. package/src/iconConfig.tsx +962 -0
  464. package/src/icons.tsx +788 -0
  465. package/src/index.tsx +763 -0
  466. package/src/jest-globals.d.ts +9 -0
  467. package/src/library/MediaLibrary.tsx +141 -0
  468. package/src/library/MediaLibraryScreen.tsx +3003 -0
  469. package/src/library/components/AudioPreview.tsx +73 -0
  470. package/src/library/components/DocumentPreview.tsx +361 -0
  471. package/src/library/components/ExpandableFab.tsx +342 -0
  472. package/src/library/components/FileTiles.tsx +252 -0
  473. package/src/library/components/ImagePreview.tsx +72 -0
  474. package/src/library/components/MediaMetadataHeader.tsx +208 -0
  475. package/src/library/components/RecordAudioScreen.tsx +845 -0
  476. package/src/library/components/VideoPreview.tsx +478 -0
  477. package/src/library/components/ViewerCounter.tsx +71 -0
  478. package/src/library/compressionEstimate.ts +662 -0
  479. package/src/library/fileMetadata.ts +212 -0
  480. package/src/library/index.ts +66 -0
  481. package/src/library/libraryIconRegistry.tsx +86 -0
  482. package/src/library/libraryIcons.tsx +928 -0
  483. package/src/library/localizedFormat.ts +207 -0
  484. package/src/library/palette.ts +281 -0
  485. package/src/library/responsive.ts +49 -0
  486. package/src/locales.ts +381 -0
  487. package/src/localization.ts +1947 -0
  488. package/src/logger.ts +20 -0
  489. package/src/lowMemoryCache.ts +218 -0
  490. package/src/mediaHistory.ts +42 -0
  491. package/src/mediaStorageService.ts +546 -0
  492. package/src/memory.ts +102 -0
  493. package/src/nativeActions.ts +422 -0
  494. package/src/nativeComponentSupport.ts +60 -0
  495. package/src/networkPerformance.ts +30 -0
  496. package/src/performance.ts +137 -0
  497. package/src/performanceMonitor.ts +219 -0
  498. package/src/permissions.ts +403 -0
  499. package/src/rendering.ts +47 -0
  500. package/src/security.ts +19 -0
  501. package/src/state.ts +333 -0
  502. package/src/storage.ts +453 -0
  503. package/src/theme.tsx +467 -0
  504. package/src/types.ts +946 -0
  505. package/src/utils.ts +177 -0
  506. package/src/validation.ts +372 -0
@@ -0,0 +1,377 @@
1
+ /**
2
+ * MediaMapper — the single translation boundary between the UI-facing
3
+ * `CameraAsset` type and the persisted `MediaRecord`/SQL row.
4
+ *
5
+ * Keeping all field mapping here means the rest of the app (screens, hooks) and
6
+ * the database schema can evolve independently. Two directions are supported:
7
+ * - `assetToRecordInput` CameraAsset → MediaRecordInput (for writes)
8
+ * - `rowToRecord` / `recordToAsset` SQL row → MediaRecord → CameraAsset (reads)
9
+ */
10
+ import type { CameraAsset } from '../types';
11
+ import {
12
+ MediaColumns,
13
+ type MediaRecord,
14
+ type MediaRecordInput,
15
+ type MediaSource,
16
+ type MediaType,
17
+ } from './types';
18
+
19
+ /**
20
+ * UI uses `photo`; the database normalizes photos to `image`. Videos, documents
21
+ * and audio are preserved as-is so a picked PDF or track keeps its true kind
22
+ * instead of collapsing into `image` (the historical cause of documents/audio
23
+ * showing up under the Photos filter).
24
+ */
25
+ export function assetTypeToMediaType(type: CameraAsset['type']): MediaType {
26
+ switch (type) {
27
+ case 'video':
28
+ return 'video';
29
+ case 'document':
30
+ return 'document';
31
+ case 'audio':
32
+ return 'audio';
33
+ default:
34
+ return 'image';
35
+ }
36
+ }
37
+
38
+ /** Inverse of {@link assetTypeToMediaType}. */
39
+ export function mediaTypeToAssetType(type: MediaType): CameraAsset['type'] {
40
+ switch (type) {
41
+ case 'video':
42
+ return 'video';
43
+ case 'document':
44
+ return 'document';
45
+ case 'audio':
46
+ return 'audio';
47
+ default:
48
+ return 'photo';
49
+ }
50
+ }
51
+
52
+ // Extension → kind tables. Used as a fallback (and cross-check) when the MIME
53
+ // type is missing or generic, so classification never relies on a single signal.
54
+ const IMAGE_EXTS = new Set([
55
+ 'jpg',
56
+ 'jpeg',
57
+ 'png',
58
+ 'gif',
59
+ 'webp',
60
+ 'bmp',
61
+ 'heic',
62
+ 'heif',
63
+ 'hif',
64
+ 'avif',
65
+ 'avifs',
66
+ 'tif',
67
+ 'tiff',
68
+ ]);
69
+ const VIDEO_EXTS = new Set([
70
+ 'mp4',
71
+ 'mov',
72
+ 'mkv',
73
+ 'webm',
74
+ 'avi',
75
+ '3gp',
76
+ '3gpp',
77
+ 'm4v',
78
+ 'mpg',
79
+ 'mpeg',
80
+ ]);
81
+ const AUDIO_EXTS = new Set([
82
+ 'mp3',
83
+ 'wav',
84
+ 'aac',
85
+ 'm4a',
86
+ 'ogg',
87
+ 'oga',
88
+ 'flac',
89
+ 'opus',
90
+ 'amr',
91
+ 'mid',
92
+ 'midi',
93
+ ]);
94
+ const DOCUMENT_EXTS = new Set([
95
+ 'pdf',
96
+ 'doc',
97
+ 'docx',
98
+ 'xls',
99
+ 'xlsx',
100
+ 'ppt',
101
+ 'pptx',
102
+ 'txt',
103
+ 'csv',
104
+ 'rtf',
105
+ 'odt',
106
+ 'ods',
107
+ 'odp',
108
+ 'epub',
109
+ 'md',
110
+ ]);
111
+
112
+ function extensionOf(fileName?: string | null): string | null {
113
+ if (!fileName) return null;
114
+ const dot = fileName.lastIndexOf('.');
115
+ if (dot < 0 || dot === fileName.length - 1) return null;
116
+ return fileName.slice(dot + 1).toLowerCase();
117
+ }
118
+
119
+ /** The extension a path/URI ends in, ignoring any query string and the directories above it. */
120
+ function extensionOfPath(path?: string | null): string | null {
121
+ if (!path) return null;
122
+ const withoutQuery = path.split(/[?#]/)[0] ?? '';
123
+ return extensionOf(withoutQuery.split(/[\\/]/).pop());
124
+ }
125
+
126
+ // Image MIME types whose container is worth preserving in a synthesized file name. Mirrors the
127
+ // native ImageFormatSupport table: naming a GIF `.jpg` puts JPEG's name on bytes that
128
+ // start with GIF89a, and consumers then fail to render it.
129
+ const MIME_TO_EXTENSION: Record<string, string> = {
130
+ 'image/gif': 'gif',
131
+ 'image/png': 'png',
132
+ 'image/webp': 'webp',
133
+ 'image/heic': 'heic',
134
+ 'image/heif': 'heif',
135
+ 'image/heic-sequence': 'heic',
136
+ 'image/heif-sequence': 'heif',
137
+ 'image/avif': 'avif',
138
+ 'image/bmp': 'bmp',
139
+ 'image/jpeg': 'jpg',
140
+ };
141
+
142
+ /**
143
+ * The file extension a synthesized name should carry for `mimeType`, defaulting to the given
144
+ * fallback when the type is missing or unrecognised.
145
+ */
146
+ export function extensionForMimeType(
147
+ mimeType: string | null | undefined,
148
+ fallback: string
149
+ ): string {
150
+ if (!mimeType) return fallback;
151
+ return MIME_TO_EXTENSION[mimeType.toLowerCase()] ?? fallback;
152
+ }
153
+
154
+ /**
155
+ * Resolves the canonical persisted {@link MediaType} for an asset using every
156
+ * available signal — the caller-provided `type`, the MIME type, then the file
157
+ * extension — so a mis-tagged or MIME-less item is still classified correctly.
158
+ * Order of precedence: a document path → MIME family → extension → the UI `type`
159
+ * fallback.
160
+ */
161
+ export function classifyMediaType(asset: CameraAsset): MediaType {
162
+ const mime = asset.mimeType?.toLowerCase() ?? '';
163
+
164
+ // The FILE outranks an `image/*` MIME type when the file itself names a document container.
165
+ // "Export By → PDF" turns a photo into a PDF, and an asset that carries the source photo's
166
+ // metadata — because it was written before the export re-stated it — says `image/jpeg` while
167
+ // its path ends in `.pdf`. Trusting the MIME there stored the PDF as an image: it was filed
168
+ // under Photos and handed to the image decoder, which has no idea what a PDF is, so the
169
+ // preview came up blank.
170
+ const pathExtension = extensionOfPath(resolveDocumentsPath(asset) ?? asset.uri);
171
+ if (
172
+ pathExtension &&
173
+ DOCUMENT_EXTS.has(pathExtension) &&
174
+ (!mime || mime.startsWith('image/'))
175
+ ) {
176
+ return 'document';
177
+ }
178
+
179
+ if (mime.startsWith('image/')) return 'image';
180
+ if (mime.startsWith('video/')) return 'video';
181
+ if (mime.startsWith('audio/')) return 'audio';
182
+ if (isDocumentMime(mime)) return 'document';
183
+
184
+ const ext = extensionOf(asset.fileName);
185
+ if (ext) {
186
+ if (IMAGE_EXTS.has(ext)) return 'image';
187
+ if (VIDEO_EXTS.has(ext)) return 'video';
188
+ if (AUDIO_EXTS.has(ext)) return 'audio';
189
+ if (DOCUMENT_EXTS.has(ext)) return 'document';
190
+ }
191
+
192
+ // Nothing conclusive from mime/extension: trust the type the picker assigned.
193
+ return assetTypeToMediaType(asset.type);
194
+ }
195
+
196
+ /** True for MIME strings that denote a document (non image/video/audio) file. */
197
+ function isDocumentMime(mime: string): boolean {
198
+ if (!mime) return false;
199
+ if (
200
+ mime.startsWith('image/') ||
201
+ mime.startsWith('video/') ||
202
+ mime.startsWith('audio/')
203
+ ) {
204
+ return false;
205
+ }
206
+ return (
207
+ mime === 'application/pdf' ||
208
+ mime.startsWith('application/vnd.') ||
209
+ mime.startsWith('application/msword') ||
210
+ mime.startsWith('application/rtf') ||
211
+ mime === 'application/epub+zip' ||
212
+ mime.startsWith('text/')
213
+ );
214
+ }
215
+
216
+ /** Normalizes an arbitrary source hint onto the allowed enum. */
217
+ export function normalizeSource(source: unknown): MediaSource {
218
+ return source === 'gallery' ? 'gallery' : 'camera';
219
+ }
220
+
221
+ /**
222
+ * Resolves the FINAL, persistent Documents path for an asset — the path that
223
+ * must be stored in SQLCipher — or `null` if the asset is not (yet) in the
224
+ * Documents directory.
225
+ *
226
+ * The native Send workflow moves a confirmed file from cache to
227
+ * `.../Documents/media_documents/...`, clears `cachePath`, and sets
228
+ * `documentUri`. This helper therefore prefers `documentUri`, then falls back to
229
+ * `uri` ONLY when that uri is itself a published Documents path (not a cache or
230
+ * `content://` gallery path). A still-cached / publish-failed asset returns
231
+ * `null`, so callers can skip it and never persist a temporary location.
232
+ */
233
+ export function resolveDocumentsPath(asset: CameraAsset): string | null {
234
+ const documentUri = (asset as { documentUri?: string }).documentUri;
235
+ if (documentUri && documentUri.length > 0) {
236
+ return documentUri;
237
+ }
238
+ const uri = asset.uri ?? '';
239
+ if (uri.length > 0) {
240
+ return uri;
241
+ }
242
+ return null;
243
+ }
244
+
245
+ /**
246
+ * True when an asset has been successfully moved to the Documents directory and
247
+ * is therefore eligible to be persisted in SQLCipher. Cache-only or
248
+ * publish-failed assets return false.
249
+ */
250
+ export function isPublishedToDocuments(asset: CameraAsset): boolean {
251
+ return resolveDocumentsPath(asset) !== null;
252
+ }
253
+
254
+ /**
255
+ * Auto-detects the origin of an asset. Gallery-selected media retains its
256
+ * original `content://`/gallery uri in `originalUri`; camera captures do not.
257
+ * An explicit hint always wins.
258
+ */
259
+ export function detectSource(
260
+ asset: CameraAsset,
261
+ hint?: MediaSource
262
+ ): MediaSource {
263
+ const explicit = (asset as { source?: unknown }).source;
264
+ if (explicit === 'gallery' || explicit === 'camera') return explicit;
265
+ if (hint) return hint;
266
+ const originalUri = (asset as { originalUri?: string }).originalUri;
267
+ return originalUri && originalUri.length > 0 ? 'gallery' : 'camera';
268
+ }
269
+
270
+ /**
271
+ * Builds a `MediaRecordInput` from a UI asset, storing the FINAL Documents path.
272
+ *
273
+ * @param source optional origin hint; when omitted the origin is auto-detected
274
+ * (gallery vs camera). The stored `filePath` is always the resolved Documents
275
+ * path — callers must only pass assets for which {@link isPublishedToDocuments}
276
+ * is true (the repository enforces this).
277
+ */
278
+ export function assetToRecordInput(
279
+ asset: CameraAsset,
280
+ source?: MediaSource
281
+ ): MediaRecordInput {
282
+ // Classify from every available signal (type + MIME + extension) so a picked
283
+ // document/audio file is stored under its true kind, never collapsed to image.
284
+ const mediaType = classifyMediaType(asset);
285
+ const now = Date.now();
286
+ // Persist the Documents path only. Fall back to uri defensively; the
287
+ // repository will have already filtered out un-published assets.
288
+ const documentsPath = resolveDocumentsPath(asset) ?? asset.uri ?? '';
289
+ return {
290
+ id: asset.id,
291
+ mediaType,
292
+ source: detectSource(asset, source),
293
+ filePath: documentsPath,
294
+ fileName:
295
+ asset.fileName ??
296
+ (mediaType === 'video'
297
+ ? `video_${now}.mp4`
298
+ : `photo_${now}.${extensionForMimeType(asset.mimeType, 'jpg')}`),
299
+ fileSize: asset.size ?? null,
300
+ width: asset.width ?? null,
301
+ height: asset.height ?? null,
302
+ // Videos and audio carry a duration; images/documents do not.
303
+ duration:
304
+ mediaType === 'video' || mediaType === 'audio'
305
+ ? asset.duration ?? null
306
+ : null,
307
+ mimeType: asset.mimeType ?? null,
308
+ createdAt: asset.dateTaken ?? now,
309
+ updatedAt: now,
310
+ // A compressed video quotes a proportional display size, not its bytes on
311
+ // disk; persist the flag so the preview / Info panel keeps that figure.
312
+ sizeIsDisplayEstimate: asset.sizeIsDisplayEstimate ?? null,
313
+ };
314
+ }
315
+
316
+ /** Normalizes a raw stored media_type string onto the {@link MediaType} enum. */
317
+ function normalizeMediaType(value: unknown): MediaType {
318
+ return value === 'video' || value === 'document' || value === 'audio'
319
+ ? value
320
+ : 'image';
321
+ }
322
+
323
+ /** Maps a raw SQL row (snake_case columns) into a typed `MediaRecord`. */
324
+ export function rowToRecord(row: Record<string, any>): MediaRecord {
325
+ return {
326
+ id: String(row[MediaColumns.id]),
327
+ mediaType: normalizeMediaType(row[MediaColumns.mediaType]),
328
+ source: normalizeSource(row[MediaColumns.source]),
329
+ filePath: row[MediaColumns.filePath] ?? '',
330
+ fileName: row[MediaColumns.fileName] ?? '',
331
+ fileSize: toNumberOrNull(row[MediaColumns.fileSize]),
332
+ width: toNumberOrNull(row[MediaColumns.width]),
333
+ height: toNumberOrNull(row[MediaColumns.height]),
334
+ duration: toNumberOrNull(row[MediaColumns.duration]),
335
+ mimeType: row[MediaColumns.mimeType] ?? null,
336
+ createdAt: Number(row[MediaColumns.createdAt]) || 0,
337
+ updatedAt: Number(row[MediaColumns.updatedAt]) || 0,
338
+ sizeIsDisplayEstimate: toBoolean(row[MediaColumns.sizeIsDisplayEstimate]),
339
+ };
340
+ }
341
+
342
+ /**
343
+ * Maps a persisted record back to the UI `CameraAsset` shape so the existing
344
+ * Media Library renders identically — only the data source changed.
345
+ */
346
+ export function recordToAsset(record: MediaRecord): CameraAsset {
347
+ return {
348
+ id: record.id,
349
+ uri: record.filePath,
350
+ type: mediaTypeToAssetType(record.mediaType),
351
+ fileName: record.fileName,
352
+ mimeType: record.mimeType ?? undefined,
353
+ width: record.width ?? undefined,
354
+ height: record.height ?? undefined,
355
+ duration: record.duration ?? undefined,
356
+ size: record.fileSize ?? undefined,
357
+ dateTaken: record.createdAt,
358
+ needsThumbnail: record.mediaType === 'video',
359
+ sizeIsDisplayEstimate: record.sizeIsDisplayEstimate,
360
+ };
361
+ }
362
+
363
+ function toNumberOrNull(value: unknown): number | null {
364
+ if (value === null || value === undefined || value === '') return null;
365
+ const n = Number(value);
366
+ return Number.isFinite(n) ? n : null;
367
+ }
368
+
369
+ /**
370
+ * Coerces a stored INTEGER flag (1/0), or a driver-native boolean, to a boolean.
371
+ * A null/absent column (legacy rows predating the column) reads as false.
372
+ */
373
+ function toBoolean(value: unknown): boolean {
374
+ if (value === null || value === undefined || value === '') return false;
375
+ if (typeof value === 'boolean') return value;
376
+ return Number(value) === 1;
377
+ }
@@ -0,0 +1,313 @@
1
+ /**
2
+ * MediaRepository — the CRUD/service layer over the `media` table.
3
+ *
4
+ * This is the ONLY module that writes SQL for media rows. UI code and hooks talk
5
+ * to strongly-typed `MediaRecord`/`CameraAsset` objects and never see a query
6
+ * string, keeping a clean separation between presentation and persistence.
7
+ *
8
+ * All operations delegate raw execution to the centralized `DatabaseManager`.
9
+ */
10
+ import type { CameraAsset } from '../types';
11
+ import {
12
+ getDatabaseManager,
13
+ type BatchStatement,
14
+ type DatabaseManager,
15
+ } from './DatabaseManager';
16
+ import {
17
+ assetToRecordInput,
18
+ isPublishedToDocuments,
19
+ recordToAsset,
20
+ rowToRecord,
21
+ } from './MediaMapper';
22
+ import {
23
+ MEDIA_TABLE,
24
+ MediaColumns as C,
25
+ type MediaCounts,
26
+ type MediaFilter,
27
+ type MediaQueryOptions,
28
+ type MediaRecord,
29
+ type MediaRecordInput,
30
+ type MediaSource,
31
+ } from './types';
32
+
33
+ const ALL_COLUMNS = [
34
+ C.id,
35
+ C.mediaType,
36
+ C.source,
37
+ C.filePath,
38
+ C.fileName,
39
+ C.fileSize,
40
+ C.width,
41
+ C.height,
42
+ C.duration,
43
+ C.mimeType,
44
+ C.createdAt,
45
+ C.updatedAt,
46
+ C.sizeIsDisplayEstimate,
47
+ ].join(', ');
48
+
49
+ export class MediaRepository {
50
+ constructor(private readonly db: DatabaseManager = getDatabaseManager()) {}
51
+
52
+ /** Builds an idempotent UPSERT statement for one record. */
53
+ private upsertStatement(input: MediaRecordInput): BatchStatement {
54
+ const now = Date.now();
55
+ const createdAt = input.createdAt ?? now;
56
+ const updatedAt = input.updatedAt ?? now;
57
+ return {
58
+ sql: `INSERT INTO ${MEDIA_TABLE} (
59
+ ${ALL_COLUMNS}
60
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
61
+ ON CONFLICT(${C.id}) DO UPDATE SET
62
+ ${C.mediaType} = excluded.${C.mediaType},
63
+ ${C.source} = excluded.${C.source},
64
+ ${C.filePath} = excluded.${C.filePath},
65
+ ${C.fileName} = excluded.${C.fileName},
66
+ ${C.fileSize} = excluded.${C.fileSize},
67
+ ${C.width} = excluded.${C.width},
68
+ ${C.height} = excluded.${C.height},
69
+ ${C.duration} = excluded.${C.duration},
70
+ ${C.mimeType} = excluded.${C.mimeType},
71
+ ${C.sizeIsDisplayEstimate} = excluded.${C.sizeIsDisplayEstimate},
72
+ ${C.updatedAt} = excluded.${C.updatedAt};`,
73
+ params: [
74
+ input.id,
75
+ input.mediaType,
76
+ input.source,
77
+ input.filePath,
78
+ input.fileName,
79
+ input.fileSize ?? null,
80
+ input.width ?? null,
81
+ input.height ?? null,
82
+ input.duration ?? null,
83
+ input.mimeType ?? null,
84
+ createdAt,
85
+ updatedAt,
86
+ input.sizeIsDisplayEstimate == null
87
+ ? null
88
+ : input.sizeIsDisplayEstimate
89
+ ? 1
90
+ : 0,
91
+ ],
92
+ };
93
+ }
94
+
95
+ /** Inserts (or updates on id/path conflict) a single record. */
96
+ async insert(input: MediaRecordInput): Promise<void> {
97
+ const stmt = this.upsertStatement(input);
98
+ await this.db.exec(stmt.sql, stmt.params);
99
+ }
100
+
101
+ /** Inserts many records atomically in one transaction. */
102
+ async insertMany(inputs: MediaRecordInput[]): Promise<void> {
103
+ if (inputs.length === 0) return;
104
+ await this.db.batch(inputs.map((i) => this.upsertStatement(i)));
105
+ }
106
+
107
+ /**
108
+ * Persists confirmed, Documents-backed media from the capture/gallery Send
109
+ * flows. This is the ONLY path the app uses after a successful Send.
110
+ *
111
+ * Contract enforced here (so callers can't create inconsistent records):
112
+ * 1. Only assets that have been successfully moved to the Documents
113
+ * directory are persisted — see {@link isPublishedToDocuments}. A
114
+ * cache-only or publish-failed asset is skipped, never stored.
115
+ * 2. The stored `filePath` is always the final Documents path, never cache.
116
+ * 3. The write is atomic; on failure nothing is committed (all-or-nothing),
117
+ * so a partial batch can't leave dangling rows.
118
+ *
119
+ * @param source optional origin hint; when omitted, each asset's origin is
120
+ * auto-detected (gallery vs camera).
121
+ * @returns the records actually persisted (may be fewer than `assets`).
122
+ */
123
+ async insertAssets(
124
+ assets: CameraAsset[],
125
+ source?: MediaSource
126
+ ): Promise<MediaRecord[]> {
127
+ // Gate: keep only well-formed assets that are confirmed in Documents.
128
+ const publishable = assets.filter(
129
+ (a) => a && a.id && a.uri && isPublishedToDocuments(a)
130
+ );
131
+
132
+ const skipped = assets.length - publishable.length;
133
+ if (skipped > 0) {
134
+ console.warn(
135
+ `[MediaRepository] Skipped ${skipped} asset(s) not yet in the ` +
136
+ `Documents directory; they will not be persisted.`
137
+ );
138
+ }
139
+
140
+ if (publishable.length === 0) return [];
141
+
142
+ const inputs = publishable.map((a) => assetToRecordInput(a, source));
143
+
144
+ // Atomic insert. If it throws, the transaction rolls back and we surface the
145
+ // error to the caller without having written any partial state.
146
+ await this.insertMany(inputs);
147
+
148
+ return inputs.map((i) => ({
149
+ ...i,
150
+ fileSize: i.fileSize ?? null,
151
+ width: i.width ?? null,
152
+ height: i.height ?? null,
153
+ duration: i.duration ?? null,
154
+ mimeType: i.mimeType ?? null,
155
+ createdAt: i.createdAt ?? Date.now(),
156
+ updatedAt: i.updatedAt ?? Date.now(),
157
+ sizeIsDisplayEstimate: i.sizeIsDisplayEstimate ?? false,
158
+ }));
159
+ }
160
+
161
+ private buildWhere(filter?: MediaFilter): { clause: string; args: any[] } {
162
+ if (
163
+ filter === 'image' ||
164
+ filter === 'video' ||
165
+ filter === 'document' ||
166
+ filter === 'audio'
167
+ ) {
168
+ return { clause: `WHERE ${C.mediaType} = ?`, args: [filter] };
169
+ }
170
+ return { clause: '', args: [] };
171
+ }
172
+
173
+ /** Returns records matching the query, newest first by default. */
174
+ async getAll(options: MediaQueryOptions = {}): Promise<MediaRecord[]> {
175
+ const {
176
+ filter = 'all',
177
+ orderBy = 'createdAt',
178
+ direction = 'DESC',
179
+ limit,
180
+ offset,
181
+ } = options;
182
+
183
+ const orderColumn =
184
+ orderBy === 'updatedAt'
185
+ ? C.updatedAt
186
+ : orderBy === 'fileName'
187
+ ? C.fileName
188
+ : C.createdAt;
189
+ const dir = direction === 'ASC' ? 'ASC' : 'DESC';
190
+ const { clause, args } = this.buildWhere(filter);
191
+
192
+ let sql = `SELECT ${ALL_COLUMNS} FROM ${MEDIA_TABLE} ${clause} ORDER BY ${orderColumn} ${dir}`;
193
+ const params = [...args];
194
+ if (typeof limit === 'number') {
195
+ sql += ' LIMIT ?';
196
+ params.push(limit);
197
+ if (typeof offset === 'number') {
198
+ sql += ' OFFSET ?';
199
+ params.push(offset);
200
+ }
201
+ }
202
+
203
+ const rows = await this.db.query<Record<string, any>>(sql, params);
204
+ return rows.map(rowToRecord);
205
+ }
206
+
207
+ /** Returns only image records (backs the "Photos" filter). */
208
+ getImages(options: Omit<MediaQueryOptions, 'filter'> = {}) {
209
+ return this.getAll({ ...options, filter: 'image' });
210
+ }
211
+
212
+ /** Returns only video records (backs the "Videos" filter). */
213
+ getVideos(options: Omit<MediaQueryOptions, 'filter'> = {}) {
214
+ return this.getAll({ ...options, filter: 'video' });
215
+ }
216
+
217
+ /** Returns records mapped straight to UI `CameraAsset`s. */
218
+ async getAssets(options: MediaQueryOptions = {}): Promise<CameraAsset[]> {
219
+ const records = await this.getAll(options);
220
+ return records.map(recordToAsset);
221
+ }
222
+
223
+ /** Fetches a single record by id, or null. */
224
+ async getById(id: string): Promise<MediaRecord | null> {
225
+ const rows = await this.db.query<Record<string, any>>(
226
+ `SELECT ${ALL_COLUMNS} FROM ${MEDIA_TABLE} WHERE ${C.id} = ? LIMIT 1`,
227
+ [id]
228
+ );
229
+ return rows.length > 0 ? rowToRecord(rows[0]!) : null;
230
+ }
231
+
232
+ /**
233
+ * Computes the filter-card counts directly from the database in one query,
234
+ * one bucket per media kind (image / video / document / audio).
235
+ */
236
+ async getCounts(): Promise<MediaCounts> {
237
+ const rows = await this.db.query<{ media_type: string; c: number }>(
238
+ `SELECT ${C.mediaType} AS ${C.mediaType}, COUNT(*) AS c
239
+ FROM ${MEDIA_TABLE} GROUP BY ${C.mediaType}`
240
+ );
241
+ const counts: MediaCounts = {
242
+ all: 0,
243
+ image: 0,
244
+ video: 0,
245
+ document: 0,
246
+ audio: 0,
247
+ };
248
+ for (const row of rows) {
249
+ const c = Number(row.c) || 0;
250
+ switch (row.media_type) {
251
+ case 'video':
252
+ counts.video += c;
253
+ break;
254
+ case 'document':
255
+ counts.document += c;
256
+ break;
257
+ case 'audio':
258
+ counts.audio += c;
259
+ break;
260
+ default:
261
+ counts.image += c;
262
+ break;
263
+ }
264
+ counts.all += c;
265
+ }
266
+ return counts;
267
+ }
268
+
269
+ /** Deletes a record by id. Returns true if a row was removed. */
270
+ async deleteById(id: string): Promise<boolean> {
271
+ const result = await this.db.exec(
272
+ `DELETE FROM ${MEDIA_TABLE} WHERE ${C.id} = ?`,
273
+ [id]
274
+ );
275
+ return result.rowsAffected > 0;
276
+ }
277
+
278
+ /** Deletes a record by its file path/URI. Returns true if a row was removed. */
279
+ async deleteByPath(filePath: string): Promise<boolean> {
280
+ const result = await this.db.exec(
281
+ `DELETE FROM ${MEDIA_TABLE} WHERE ${C.filePath} = ?`,
282
+ [filePath]
283
+ );
284
+ return result.rowsAffected > 0;
285
+ }
286
+
287
+ /** Deletes multiple records by id atomically. Returns the number requested. */
288
+ async deleteMany(ids: string[]): Promise<void> {
289
+ if (ids.length === 0) return;
290
+ await this.db.batch(
291
+ ids.map((id) => ({
292
+ sql: `DELETE FROM ${MEDIA_TABLE} WHERE ${C.id} = ?`,
293
+ params: [id],
294
+ }))
295
+ );
296
+ }
297
+
298
+ /** Removes every media row. */
299
+ async clear(): Promise<void> {
300
+ await this.db.exec(`DELETE FROM ${MEDIA_TABLE}`);
301
+ }
302
+ }
303
+
304
+ // Shared singleton repository over the singleton manager.
305
+ let repositoryInstance: MediaRepository | null = null;
306
+
307
+ /** Returns the shared `MediaRepository` singleton. */
308
+ export function getMediaRepository(): MediaRepository {
309
+ if (!repositoryInstance) {
310
+ repositoryInstance = new MediaRepository();
311
+ }
312
+ return repositoryInstance;
313
+ }