react-native-attachment-kit 1.0.2 → 1.0.5

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 (143) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +356 -213
  3. package/android/build.gradle +3 -0
  4. package/android/src/main/libs/rnattachmentkit-release.aar +0 -0
  5. package/android/src/newarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +308 -10
  6. package/android/src/oldarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +311 -9
  7. package/ios/RNAttachmentKit.m +44 -0
  8. package/ios/RNAttachmentKit.swift +307 -23
  9. package/ios/RNAttachmentKit.xcframework/Info.plist +5 -5
  10. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Headers/RNAttachmentKit-Swift.h +30 -0
  11. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.abi.json +20349 -13021
  12. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.private.swiftinterface +118 -1
  13. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  14. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftinterface +118 -1
  15. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/RNAttachmentKit +0 -0
  16. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
  17. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
  18. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
  19. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
  20. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
  21. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
  22. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
  23. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
  24. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
  25. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
  26. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~dark.webp +0 -0
  27. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~light.webp +0 -0
  28. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
  29. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~light.webp +0 -0
  30. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
  31. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
  32. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~dark.webp +0 -0
  33. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~light.webp +0 -0
  34. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~dark.webp +0 -0
  35. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~light.webp +0 -0
  36. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
  37. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~light.webp +0 -0
  38. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
  39. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
  40. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
  41. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~light.webp +0 -0
  42. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
  43. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
  44. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
  45. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
  46. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
  47. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
  48. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~dark.webp +0 -0
  49. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~light.webp +0 -0
  50. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~dark.webp +0 -0
  51. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~light.webp +0 -0
  52. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
  53. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeResources +192 -60
  54. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeSignature +0 -0
  55. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Headers/RNAttachmentKit-Swift.h +60 -0
  56. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.abi.json +20349 -13021
  57. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +118 -1
  58. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  59. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +118 -1
  60. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +20349 -13021
  61. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +118 -1
  62. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  63. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +118 -1
  64. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/RNAttachmentKit +0 -0
  65. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
  66. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
  67. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
  68. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
  69. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
  70. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
  71. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
  72. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
  73. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
  74. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
  75. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~dark.webp +0 -0
  76. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~light.webp +0 -0
  77. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
  78. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~light.webp +0 -0
  79. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
  80. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
  81. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~dark.webp +0 -0
  82. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~light.webp +0 -0
  83. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~dark.webp +0 -0
  84. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~light.webp +0 -0
  85. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
  86. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~light.webp +0 -0
  87. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
  88. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
  89. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
  90. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~light.webp +0 -0
  91. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
  92. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
  93. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
  94. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
  95. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
  96. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
  97. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~dark.webp +0 -0
  98. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~light.webp +0 -0
  99. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~dark.webp +0 -0
  100. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~light.webp +0 -0
  101. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
  102. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeResources +202 -70
  103. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
  104. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
  105. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
  106. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
  107. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
  108. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
  109. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
  110. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
  111. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
  112. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
  113. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_back~dark.webp +0 -0
  114. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_back~light.webp +0 -0
  115. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
  116. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_blur~light.webp +0 -0
  117. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
  118. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
  119. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check~dark.webp +0 -0
  120. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check~light.webp +0 -0
  121. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_close~dark.webp +0 -0
  122. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_close~light.webp +0 -0
  123. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
  124. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_crop~light.webp +0 -0
  125. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
  126. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
  127. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
  128. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_effect~light.webp +0 -0
  129. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
  130. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
  131. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
  132. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
  133. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
  134. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
  135. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_text~dark.webp +0 -0
  136. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_text~light.webp +0 -0
  137. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_text_add~dark.webp +0 -0
  138. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_text_add~light.webp +0 -0
  139. package/package.json +1 -1
  140. package/src/NativeAttachmentKit.ts +10 -0
  141. package/src/index.d.ts +5 -56
  142. package/src/index.ts +188 -35
  143. package/src/types.ts +207 -11
package/README.md CHANGED
@@ -1,16 +1,24 @@
1
1
  # React Native Attachment Kit 🚀
2
2
 
3
- A highly customizable, high-performance, and purely native image picker and editor for React Native.
3
+ A fully native attachment toolkit for React Native chat apps: a Telegram-style photo picker with an attach menu (Gallery / Location / File), an image editor, a sticky image viewer, a "all photos" media gallery, and everything a **location message** needs — map thumbnails, reverse geocoding and a full-screen map viewer.
4
+
5
+ ---
4
6
 
5
7
  ## ✨ Features
6
- - **Pure Native UI**: Built with native iOS and Android for the fastest and smoothest performance.
7
- - **Built-in Editor & Viewer**: Includes an integrated image editor (cropping, filtering) and a highly optimized image viewer.
8
- - **Smooth Animations**: Carefully crafted interactions for a premium user experience (e.g., iOS-like smooth thumbnail centering).
9
- - **Sticky Image Viewer**: The viewer "sticks" to the source image thumbnail with a border-radius animation, creating a seamless shared-element transition on both iOS and Android.
10
- - **Native Mask View**: A native mask view hides the source thumbnail while the viewer is open, preventing visual duplication — no extra JS-side opacity manipulation required.
11
- - **Android Overlay Viewer**: Option to render the viewer directly inside the host Activity's Window (no separate Activity or DialogFragment), eliminating AppState blips and enabling real-time background layout updates.
12
- - **Customizable**: Easily configure theme colors, maximum selection limits, and more.
13
- - **New Architecture Ready**: Fully supports React Native's New Architecture (Turbo Modules).
8
+
9
+ - **Pure native UI** (Swift / Kotlin) — smooth on both platforms, New Architecture (Turbo Modules) and old architecture supported.
10
+ - **Photo picker** — bottom-sheet gallery, single or multi selection, albums, camera cell, optional editor step, profile (1:1) crop mode.
11
+ - **Attach menu** — Gallery / **Location** / **File** tabs under the gallery (Telegram style).
12
+ - Location: map with a center pin, "send my current location", place search, "Share my location" switch, map snapshot image.
13
+ - File: system file picker, files copied into the app's temp folder.
14
+ - **Image editor** — crop, rotate, filters, drawing, mosaic, text, caption.
15
+ - **Image viewer** — paging, pinch-to-zoom, thumbnails, save / share / delete actions, and a **sticky** open/close animation from the tapped thumbnail.
16
+ - **Location messages**
17
+ - `renderLocationThumbnail` — draws a chat-bubble map thumbnail on the receiving device from coordinates only (cached).
18
+ - `reverseGeocode` — coordinates → address in the app's language with the device geocoder (free, cached, throttle-safe).
19
+ - `openMapViewer` — full-screen map with the sender's marker, distance · address card and Directions; the bubble image grows into the map and shrinks back on close.
20
+ - **Media gallery** — native grid for thousands of photos (3×3 / 4×4 / 5×5), date scroller, opens the viewer from a cell.
21
+ - **Theming** — theme color, light / dark / system appearance, 59 UI languages.
14
22
 
15
23
  ---
16
24
 
@@ -30,308 +38,443 @@ A highly customizable, high-performance, and purely native image picker and edit
30
38
 
31
39
  ---
32
40
 
41
+ ## 📋 Requirements
42
+
43
+ | | |
44
+ |---|---|
45
+ | iOS | 15.1+ (Apple Maps / MapKit — no API key) |
46
+ | Android | minSdk 24, compileSdk 34. Google Maps — **the app supplies its own key** (see below) |
47
+ | React Native | New Architecture (Turbo Modules) or old architecture |
48
+
49
+ ---
50
+
33
51
  ## 📦 Installation
34
52
 
35
53
  ```bash
36
- # using yarn
37
54
  yarn add react-native-attachment-kit
38
-
39
- # or using npm
55
+ # or
40
56
  npm install react-native-attachment-kit
41
- ```
42
57
 
43
- If you are using iOS, run pod install:
44
-
45
- ```bash
46
58
  cd ios && pod install
47
59
  ```
48
60
 
49
- ### iOS Permissions (Info.plist)
50
- You must add the following permissions to your `ios/YourAppName/Info.plist` file, otherwise your app will crash when trying to access the camera or photo library.
61
+ Rebuild the app after installing or updating (the module ships compiled native code — a Metro reload is not enough).
62
+
63
+ ### iOS — `Info.plist`
51
64
 
52
65
  ```xml
53
66
  <key>NSCameraUsageDescription</key>
54
- <string>We need access to your camera to take photos and videos.</string>
67
+ <string>We need access to your camera to take photos.</string>
55
68
  <key>NSPhotoLibraryUsageDescription</key>
56
69
  <string>We need access to your photo library to select images.</string>
57
70
  <key>NSPhotoLibraryAddUsageDescription</key>
58
- <string>We need access to save edited photos to your library.</string>
71
+ <string>We need access to save photos to your library.</string>
72
+ <!-- Attach menu → Location ("send my current location", my-location dot) -->
73
+ <key>NSLocationWhenInUseUsageDescription</key>
74
+ <string>We use your location to share it in a chat.</string>
59
75
  ```
60
76
 
61
- ---
77
+ ### Android — Google Maps key
62
78
 
63
- ## 🛠 Usage Example
64
-
65
- ```javascript
66
- import AttachmentKit from 'react-native-attachment-kit';
67
-
68
- // Step 1: Initialize the module (usually in App.tsx or index.js)
69
- // Pass your license key (if you have one to remove the watermark) and default configurations
70
- AttachmentKit.init("", {
71
- languageCode: 'en',
72
- themeColor: '#ff0000',
73
- appearance: 'system', // 'system' (default, follows the device) | 'light' | 'dark'
74
- }).catch(console.error);
75
-
76
- // Step 2: Open Image Picker
77
- const handleOpenPicker = async () => {
78
- try {
79
- const result = await AttachmentKit.openGallery({
80
- maxSelection: 10,
81
- themeColor: '#FF6B35',
82
- enableEditor: true,
83
- profileMode: false,
84
- });
85
- console.log('Selected Images:', result);
86
- } catch (error) {
87
- console.error(error);
88
- }
89
- };
90
- ```
79
+ The Location tab, the map viewer and `renderLocationThumbnail` use Google Maps. The library ships **no key**; add your own to the app's `AndroidManifest.xml`:
91
80
 
92
- ### Image Viewer with Sticky Transition (Recommended)
93
-
94
- ```javascript
95
- import AttachmentKit from 'react-native-attachment-kit';
96
-
97
- // Get the on-screen position of the thumbnail to enable the sticky viewer transition
98
- const imageRef = useRef(null);
99
-
100
- const openViewer = () => {
101
- imageRef.current?.measure((x, y, width, height, pageX, pageY) => {
102
- AttachmentKit.openViewer({
103
- images: ['https://example.com/photo1.jpg', 'https://example.com/photo2.jpg'],
104
- initialIndex: 0,
105
- themeColor: '#FF6B35',
106
- // Sticky transition: pass the thumbnail's screen coordinates
107
- sourceRect: { x: pageX, y: pageY, width, height },
108
- sourceBorderRadius: 12,
109
- sourceBorderCorners: ['topLeft', 'topRight', 'bottomLeft', 'bottomRight'],
110
- // Android: use overlay viewer to avoid AppState blips
111
- useOverlayViewer: true,
112
- onPageSelected: (index) => {
113
- console.log('Viewing image at index:', index);
114
- },
115
- onViewerOpened: () => {
116
- // Android only: called when the open animation finishes.
117
- // Safe place to run background scroll corrections.
118
- console.log('Viewer fully open');
119
- },
120
- onViewerWillClose: () => {
121
- console.log('Viewer is closing');
122
- },
123
- });
124
- });
125
- };
81
+ ```xml
82
+ <application>
83
+ <meta-data android:name="com.google.android.geo.API_KEY" android:value="YOUR_KEY"/>
84
+ </application>
126
85
  ```
127
86
 
87
+ Enable on the key: **Maps SDK for Android**, and **Places API (New)** for place search in the Location tab (Autocomplete (New) + Place Details Essentials only; on failure it falls back to the device `Geocoder`). Addresses shown while dragging the map and `reverseGeocode` use only the free device `Geocoder`.
88
+
89
+ ### Android — permissions merged from the library
90
+
91
+ `INTERNET`, `ACCESS_NETWORK_STATE`, `READ_MEDIA_IMAGES` / `READ_EXTERNAL_STORAGE`, `WRITE_EXTERNAL_STORAGE` (Android 9 and below, viewer "save"), `CAMERA`, `ACCESS_FINE_LOCATION`, `ACCESS_COARSE_LOCATION`. Remove any you don't use with `tools:node="remove"`. Runtime prompts are shown by the module when needed. Dependencies `play-services-maps:19.0.0` and `play-services-location:21.3.0` come with the library.
92
+
128
93
  ---
129
94
 
130
- ## 📖 API Reference
95
+ ## 🛠 Quick start
131
96
 
132
- ### `AttachmentKit.init(licenseKey, options?): Promise<boolean>`
97
+ ```ts
98
+ import AttachmentKit from 'react-native-attachment-kit'
133
99
 
134
- Initializes the module with a license key and global defaults.
100
+ // Once at app start
101
+ await AttachmentKit.init('YOUR_LICENSE_KEY', { // '' = evaluation (editor watermark)
102
+ languageCode: 'ko', // any app language code; see Localization
103
+ themeColor: '#10b981',
104
+ appearance: 'system', // 'system' | 'light' | 'dark'
105
+ })
135
106
 
136
- | Option | Type | Description |
137
- |---|---|---|
138
- | `licenseKey` | `string` | Your license key. Pass `""` for evaluation (watermark shown). |
139
- | `options.themeColor` | `string` | Global default theme color (hex). |
140
- | `options.languageCode` | `string` | Global default language code. |
141
- | `options.appearance` | `'system' \| 'light' \| 'dark'` | Global default screen mode. `'system'` (default) follows the device setting; `'light'` / `'dark'` force it. Every `open*` call also accepts `appearance` to override per call. |
107
+ // Pick photos
108
+ const images = await AttachmentKit.openGallery({ maxSelection: 10, enableEditor: true })
109
+ ```
142
110
 
143
- ### `AttachmentKit.setDefaults(options)` / `AttachmentKit.getDefaults()`
111
+ ### Global defaults
144
112
 
145
- Changes the global defaults (`languageCode`, `themeColor`, `appearance`) at any time after `init` — e.g. right after the user changes the app's theme or language. Only the values you pass are changed.
113
+ `init` options become defaults for every screen. Change them any time (e.g. when the user changes the app theme or language) — only the values you pass change:
146
114
 
147
115
  ```ts
148
- AttachmentKit.setDefaults({ appearance: 'dark' })
116
+ AttachmentKit.setDefaults({ appearance: 'dark', languageCode: 'en' })
117
+ AttachmentKit.getDefaults() // { languageCode, themeColor, appearance }
149
118
  ```
150
119
 
120
+ Every `open*` / `render*` call also accepts `themeColor`, `languageCode` and `appearance` to override per call.
121
+
122
+ ### Localization
123
+
124
+ The UI is translated into 59 languages (ISO 639-1):
125
+ `af ar be bg bn ca cs cy da de el en eo es et fa fi fr ga gl gu he hi hr ht hu id is it ja ka kn ko lt lv mk mr ms mt nl no pl pt ro ru sk sl sq sv sw ta te th tl tr uk ur vi zh`
126
+
127
+ - Region/script variants are normalized for the UI (`"pt-BR"` → `pt`, `"zh-Hans"` → `zh`, `"iw"` → `he`, `"nb"` → `no`, `"fil"` → `tl`); unsupported codes fall back to English. `zh` is Simplified Chinese.
128
+ - Addresses (`reverseGeocode`, map viewer) use the code **as given** (e.g. `'zh-Hant'`), since geocoders support more locales than the UI.
129
+ - `normalizeLanguageCode()` and `SUPPORTED_LANGUAGE_CODES` are exported.
130
+
151
131
  ---
152
132
 
153
- ### `AttachmentKit.openGallery(options?): Promise<ImageResult[]>`
133
+ ## 📖 API reference
154
134
 
155
- Opens the native image gallery for selecting photos and videos.
135
+ ### Photo picker
136
+
137
+ #### `openGallery(options?: GalleryOptions): Promise<ImageResult[]>`
138
+
139
+ Opens the gallery bottom sheet. Resolves with the picked images (`[]` when the user sent a location or files from the attach menu, or closed it). Calling it while a gallery is open rejects with `E_ALREADY_OPEN`.
156
140
 
157
141
  | Option | Type | Default | Description |
158
142
  |---|---|---|---|
159
- | `maxSelection` | `number` | `1` | Maximum number of images a user can select. |
160
- | `maxWidth` / `maxHeight` | `number` | — | Maximum dimensions to scale the output image. |
161
- | `mediaType` | `"photo" \| "video" \| "all"` | `"photo"` | Type of media to show. |
162
- | `quality` | `number` | `0.8` | Compression quality (0 to 1). |
163
- | `enableEditor` | `boolean` | `false` | Opens the image editor after a single image selection. |
164
- | `profileMode` | `boolean` | `false` | Enables 1:1 circular crop mode for profile pictures. |
165
- | `themeColor` | `string` | — | Primary hex color for the UI (e.g., `"#FFEB3B"`). |
166
- | `autoCloseOnSelect` | `boolean` | `false` | Close picker automatically on single selection. |
167
- | `asyncProcessing` | `boolean` | `false` | When `true`, image processing runs in the background and results are delivered via `onImageProcessed`. |
168
- | `onSelectionChange` | `(event) => void` | — | Callback fired each time the selection count changes. |
169
- | `onImageProcessed` | `(event) => void` | — | Callback fired per image when `asyncProcessing` is `true`. |
170
- | `openDuration` / `closeDuration` | `number` | — | Override the open/close animation duration (ms). |
171
- | `languageCode` | `string` | — | Language for the UI. Supported: `en`, `ko`, `ja`, `zh`, `fr`, `de`, `es`, `pt`, `ru`, `it`, `nl`, `pl`, `tr`, `vi`, `th`, `id`, `ms`, `hi`, `da`. |
172
- | `doneButtonText` / `allItemsText` / `selectedItemsText` / `recentsAlbumText` | `string` | — | Text overrides for specific UI labels. |
143
+ | `maxSelection` | `number` | `1` | Maximum number of photos. |
144
+ | `selectionMode` | `'single' \| 'multi'` | auto | `'single'`: tap returns immediately (with editor: tap → edit → send). `'multi'`: numbered checks + Done, even with `maxSelection: 1`. Omitted: single when `autoCloseOnSelect` or `maxSelection <= 1`. |
145
+ | `autoCloseOnSelect` | `boolean` | `false` | Single mode: close right after the tap. |
146
+ | `enableEditor` | `boolean` | `false` | Open the editor after selection. |
147
+ | `profileMode` | `boolean` | `false` | 1:1 (circle) crop for profile photos. |
148
+ | `maxWidth` / `maxHeight` | `number` | `1024` | Output images are scaled down to fit. |
149
+ | `outputFormat` | `'webp' \| 'jpg' \| 'png'` | `'webp'` | Output file format. (Not in the TS type yet — pass it with a cast.) |
150
+ | `themeColor` / `languageCode` / `appearance` | | defaults | Per-call overrides. |
151
+ | `doneButtonText` / `allItemsText` / `selectedItemsText` / `recentsAlbumText` | `string` | translated | Label overrides. |
152
+ | `asyncProcessing` | `boolean` | `false` | Resolve right away and deliver each processed image through `onImageProcessed`. |
153
+ | `onSelectionChange` | `(e: SelectionChangeEvent) => void` | — | `{ selectedCount, maxSelection }` on every change. |
154
+ | `onImageProcessed` | `(e) => void` | — | Per image when `asyncProcessing` is on. |
155
+ | `openDuration` / `closeDuration` | `number` (ms) | native | Animation durations. |
156
+ | `showAttachMenu` … | | | Attach menu options — see [Attach menu](#attach-menu-location--file). |
157
+
158
+ `mediaType` and `quality` are accepted for compatibility but currently ignored (photos only; output quality is fixed).
159
+
160
+ #### `ImageResult`
161
+
162
+ | Field | Type | Description |
163
+ |---|---|---|
164
+ | `originalUri` | `string` | Original asset (`ph://` or `file://`). |
165
+ | `originalFileUri?` | `string` | Original-resolution file (`file://`). |
166
+ | `originalWidth` / `originalHeight` | `number` | Original size. |
167
+ | `uri?` | `string` | Resized / edited output (`file://`). |
168
+ | `width?` / `height?` | `number` | Output size. |
169
+ | `type` | `string` | MIME type. |
170
+ | `caption?` | `string` | Caption typed in the editor. |
171
+ | `fileName?` / `fileExtension?` / `fileSize?` | | Output file info (bytes). |
172
+
173
+ #### `closeGallery(): Promise<boolean>`
174
+ Closes the picker from code. `true` if it was open.
175
+
176
+ #### `addSelectionChangeListener(listener): () => void`
177
+ Selection events outside `openGallery`; returns an unsubscribe function.
173
178
 
174
179
  ---
175
180
 
176
- ### `AttachmentKit.openEditor(options): Promise<ImageResult>`
181
+ ### Attach menu (Location / File)
182
+
183
+ ```ts
184
+ const images = await AttachmentKit.openGallery({
185
+ showAttachMenu: true,
186
+ captureLocationImage: false, // send coordinates only; receivers draw the thumbnail
187
+ onLocationSelected: (location) => sendLocationMessage(location),
188
+ onFilesSelected: (files, failed) => sendFiles(files),
189
+ })
190
+ // images is [] when a location or files were sent
191
+ ```
192
+
193
+ | Option | Type | Default | Description |
194
+ |---|---|---|---|
195
+ | `showAttachMenu` | `boolean` | `false` | Show the Gallery / Location / File bar. |
196
+ | `onLocationSelected` | `(location: AttachLocationResult) => void` | — | Location sent from the Location tab. |
197
+ | `onFilesSelected` | `(files: AttachFileResult[], failed?: AttachFileFailure[]) => void` | — | Files sent from the File tab. `failed` lists files that could not be copied (omitted when all succeeded). |
198
+ | `captureLocationImage` | `boolean` | `true` | `false` = no map snapshot; the location is sent immediately (no `image*` fields, everything else is returned). |
199
+ | `attachMenuSinkIntoNav` | `number` (dp) | `12` | **Android** — how far the bar sinks into the navigation-bar area (`0` = right above it). |
200
+ | `attachMenuMinBottomMargin` | `number` (dp) | `8` | **Android** — minimum gap to the screen bottom. |
201
+
202
+ Both callbacks fire before `openGallery` resolves; their listeners are removed when it settles.
203
+
204
+ **Location tab**
205
+ - Map with a center pin: drag the map to choose a point (address from the device geocoder), tap a place, or "Send my current location" (accuracy shown).
206
+ - Search: iOS Apple Maps search; Android Places Autocomplete (New) with a session token + Place Details Essentials (location only) on pick, falling back to the device `Geocoder`.
207
+ - Full-screen map, map type (standard / hybrid), my-location button, light/dark map style.
208
+ - **"Share my location"** switch (off by default, remembered on the device): only affects the snapshot image — my-location dot drawn and the area widened so both points fit.
177
209
 
178
- Directly opens the built-in image editor for a specific image.
210
+ **`AttachLocationResult`**
211
+
212
+ | Field | Type | Description |
213
+ |---|---|---|
214
+ | `latitude`, `longitude` | `number` | Sent point. |
215
+ | `accuracy?` | `number` (m) | Only when the current location was sent. |
216
+ | `title?` | `string` | Place name when a place was picked. |
217
+ | `address?` | `string` | Address (no country name). |
218
+ | `isCurrentLocation` | `boolean` | `false` = dragged pin or place. |
219
+ | `markerType?` | `'current' \| 'pin' \| 'place'` | Marker icon — pass it to the thumbnail / map viewer so they draw the same marker. |
220
+ | `placeCategory?` | `string` | **iOS** — `MKPointOfInterestCategory` of a place (category icon). |
221
+ | `imageSpanMeters?` | `number` | Real distance covered by the image width — pass it as `spanMeters` to keep the same scale. Returned even with `captureLocationImage: false`. |
222
+ | `imageUri?`, `imagePath?`, `imageWidth?`, `imageHeight?` | | Map snapshot: portrait **480×1040 webp** (9:19.5), the point exactly centered, marker drawn. Omitted when `captureLocationImage: false` or the capture fails. |
223
+ | `includesMyLocation?` | `boolean` | Whether my-location was drawn into the snapshot ("Share my location"). |
224
+ | `currentLatitude?`, `currentLongitude?`, `currentAccuracy?` | `number` | The device's own location at send time (when known), independent of what was sent. |
225
+
226
+ **`AttachFileResult`** — `{ uri (file://), path, name, size (bytes), mimeType }`
227
+ **`AttachFileFailure`** — `{ name, error }`
228
+
229
+ **File tab** — iOS Files picker, Android system picker (`ACTION_OPEN_DOCUMENT`, multiple, any type). Files are **copied** to the app's temp folder; copy or upload them promptly (see [Caches & temp files](#-caches--temp-files)).
230
+
231
+ ---
232
+
233
+ ### Image editor
234
+
235
+ #### `openEditor(options: EditorOptions): Promise<ImageResult>`
179
236
 
180
237
  | Option | Type | Description |
181
238
  |---|---|---|
182
- | `uri` | `string` | The URI of the image to edit (`originalUri` from a picker result). |
183
- | `editedFileUri` | `string` | (Optional) Path to a previously edited version to resume editing from. |
184
- | `themeColor` | `string` | UI theme color. |
185
- | `maxWidth` / `maxHeight` | `number` | Maximum bounds for the edited image output. |
186
- | `openDuration` / `closeDuration` | `number` | Override the open/close animation duration (ms). |
239
+ | `uri` | `string` | Image to edit (`originalUri` from a picker result). |
240
+ | `editedFileUri?` | `string` | A previous edit to continue from. |
241
+ | `maxWidth` / `maxHeight` | `number` | Output bounds. |
242
+ | `themeColor` / `appearance` | | Per-call overrides. |
243
+ | `openDuration` / `closeDuration` | `number` (ms) | Animation durations. |
244
+
245
+ Rejects with `E_ALREADY_OPEN` while an editor is open. Without a license key the editor output carries a watermark.
187
246
 
188
247
  ---
189
248
 
190
- ### `AttachmentKit.openViewer(options): Promise<void>`
249
+ ### Image viewer
191
250
 
192
- Opens a high-performance, full-screen image viewer with smooth swiping, thumbnail navigation, and pinch-to-zoom capabilities.
251
+ #### `openViewer(options: ViewerOptions): Promise<void>`
252
+
253
+ ```ts
254
+ const ref = useRef<View>(null)
255
+
256
+ ref.current?.measure((x, y, width, height, pageX, pageY) => {
257
+ AttachmentKit.openViewer({
258
+ images: ['https://example.com/1.jpg', 'https://example.com/2.jpg'],
259
+ initialIndex: 0,
260
+ sourceRect: { x: pageX, y: pageY, width, height }, // sticky open/close
261
+ sourceBorderRadius: 12,
262
+ useOverlayViewer: true, // Android: recommended
263
+ onPageSelected: async (index) => {
264
+ const rect = await measureThumbnail(index)
265
+ if (rect) AttachmentKit.updateSourceRect(rect) // close back into the right thumbnail
266
+ },
267
+ })
268
+ })
269
+ ```
193
270
 
194
- #### Basic Options
271
+ **Basic**
195
272
 
196
273
  | Option | Type | Default | Description |
197
274
  |---|---|---|---|
198
- | `images` | `string[]` | **required** | Array of image URIs to display. |
199
- | `placeholderImages` | `string[]` | — | Low-resolution placeholder URIs shown while the full image loads. |
200
- | `initialIndex` | `number` | `0` | The index of the image to show first. |
201
- | `themeColor` | `string` | — | UI theme color (counter badge, thumbnails). |
202
- | `title` | `string` | — | Title text shown in the viewer toolbar. |
203
- | `openDuration` / `closeDuration` | `number` | — | Override the open/close animation duration (ms). |
204
-
205
- #### Sticky Viewer / Shared-Element Transition Options
275
+ | `images` | `string[]` | **required** | Image URIs (`https://`, `file://`, `ph://`). |
276
+ | `initialIndex` | `number` | `0` | First page. |
277
+ | `placeholderImages` | `string[]` | — | Low-res placeholders shown while the full image loads (same order). |
278
+ | `cacheFiles` | `(string \| null)[]` | — | Local cache paths per image: if the file exists it is shown without network, otherwise the downloaded bytes are saved there. |
279
+ | `title` | `string` | — | Toolbar title. |
280
+ | `themeColor` / `appearance` / `languageCode` | | defaults | Per-call overrides. |
281
+ | `animationType` / `closeAnimationType` | `'zoom' \| 'fade' \| 'slide'` | `zoom` with `sourceRect`, else `slide` | Open / close animation. |
282
+ | `openDuration` / `closeDuration` | `number` (ms) | native | Animation durations. |
206
283
 
207
- These options enable a smooth "shared-element"-style transition where the viewer zooms out from (and back into) the source thumbnail.
284
+ **Sticky transition**
208
285
 
209
286
  | Option | Type | Description |
210
287
  |---|---|---|
211
- | `sourceRect` | `SourceRect` | Screen coordinates (`x`, `y`, `width`, `height`) of the source thumbnail. Obtain via `ref.measure(...)`. Enables the zoom-from-thumbnail open animation. |
212
- | `sourceBorderRadius` | `number` | Border radius of the source thumbnail. The viewer animates from this radius to 0 (and back on close), creating a seamless shape transition. |
213
- | `sourceBorderCorners` | `('topLeft' \| 'topRight' \| 'bottomLeft' \| 'bottomRight')[]` | Specifies which corners of the source thumbnail have the border radius. Useful for images with a radius only on specific corners (e.g., chat bubbles). |
214
- | `sourceBackgroundColor` | `string` | Background color behind the source thumbnail (used to match the background during the open animation). |
215
- | `hideSourceImage` | `boolean` | When `true`, the native mask view hides the source thumbnail while the viewer is open, preventing visual duplication. The mask is automatically removed when the viewer closes. |
288
+ | `sourceRect` | `SourceRect` | On-screen rect of the tapped thumbnail (`measure` page coordinates). |
289
+ | `sourceBorderRadius` | `number` | Thumbnail corner radius (animates to 0 and back). |
290
+ | `sourceBorderCorners` | `('topLeft' \| 'topRight' \| 'bottomLeft' \| 'bottomRight')[]` | Which corners are rounded (chat-bubble groups). `[]` = none. |
291
+ | `sourceBackgroundColor` | `string` | Background behind the thumbnail during the animation. |
292
+ | `hideSourceImage` | `boolean` | A native mask hides the source thumbnail while the viewer is open. |
216
293
 
217
- #### Callbacks / Event Options
294
+ **Actions**
218
295
 
219
- | Option | Type | Platform | Description |
220
- |---|---|---|---|
221
- | `onPageSelected` | `(index: number) => void` | Both | Called whenever the user swipes to a different image. |
222
- | `onViewerWillClose` | `() => void` | Both | Called just before the viewer begins its close animation. |
223
- | `onViewerOpened` | `() => void` | Android | Called once when the open animation finishes. Ideal for triggering background scroll corrections, since the viewer fully covers the screen at this point — no race conditions. |
296
+ | Option | Type | Description |
297
+ |---|---|---|
298
+ | `showActionButtons` | `boolean` (default `true`) | Save / share / delete buttons in the top bar. |
299
+ | `saveAllText` / `saveOneText` / `shareAllText` / `shareOneText` / `actionCancelText` | `string` | Action-sheet labels for groups. |
300
+ | `deleteConfirmTitle` / `deleteConfirmMessage` / `deleteConfirmGroupMessage` / `deleteButtonText` | `string` | Delete-confirm texts (translated by default). |
301
+ | `deleteOptions` | `{ key, label, destructive? }[]` | Several delete choices (e.g. "Delete for everyone" / "Delete for me"); the chosen `key` comes back as `option`. |
302
+ | `onImageDeleted` | `({ index, url, option? }) => void` | The user confirmed delete. The module does not delete anything — the app does. |
224
303
 
225
- #### Android Viewer Mode Options
304
+ **Events**
226
305
 
227
- On Android, the viewer can be opened in three different modes. Choose based on your app's requirements:
306
+ | Option | Platform | Description |
307
+ |---|---|---|
308
+ | `onPageSelected(index)` | both | Page changed. |
309
+ | `onViewerWillClose()` | both | Close animation starts. |
310
+ | `onViewerOpened()` | Android | Open animation finished (safe moment for background scroll corrections). |
228
311
 
229
- | Option | Type | Default | Description |
230
- |---|---|---|---|
231
- | `useDialogViewer` | `boolean` | `false` | Opens the viewer as a **DialogFragment** (full-screen dialog) instead of a separate Activity. Prevents the RN host Activity from pausing (`onPause`), which otherwise causes `AppState` to briefly report `"background"` and temporarily freezes RN UI interaction. **Limitation:** The DialogFragment still uses a separate Android Window, so background RN view updates (e.g., list scroll corrections) may not render to screen until the viewer closes. |
232
- | `useOverlayViewer` | `boolean` | `false` | ⭐ **Recommended for Android.** Opens the viewer as a `View` added directly on top of the host Activity's existing Window — no separate Activity or Dialog Window. **Benefits:** AppState never flickers, background RN layout changes (e.g., scroll corrections for the sticky viewer) are reflected in real time (same render tree, same frame). **Note:** When both `useDialogViewer` and `useOverlayViewer` are `true`, `useOverlayViewer` takes priority. |
312
+ **Android viewer modes**
233
313
 
234
- > **Android Viewer Mode Summary:**
235
- > - Default (no flag): Separate Activity — `AppState` blips, but most stable.
236
- > - `useDialogViewer: true`: DialogFragment — no `AppState` blip, but background layout updates may be deferred.
237
- > - `useOverlayViewer: true`: Overlay View — no `AppState` blip, background layout updates work in real time. ✅
314
+ | Option | Description |
315
+ |---|---|
316
+ | (default) | Separate Activity — `AppState` briefly reports `background`. |
317
+ | `useDialogViewer: true` | DialogFragment — no `AppState` blip, but background RN layout changes may not render until it closes. |
318
+ | `useOverlayViewer: true` | ⭐ View on top of the host Activity's window — no `AppState` blip, background changes render in real time. Wins over `useDialogViewer`. |
238
319
 
239
- ---
320
+ #### `updateSourceRect(rect: SourceRect & { sourceBorderRadius?, sourceBorderCorners? }): Promise<void>`
321
+ Updates where the viewer closes to (e.g. after paging to another thumbnail or scrolling).
322
+
323
+ #### `injectImageCache(url: string, localPath: string): Promise<boolean>`
324
+ Seeds the viewer's image cache with a local file for a remote URL, so it opens instantly without network.
240
325
 
241
- ### `AttachmentKit.updateSourceRect(rect): Promise<void>`
326
+ #### `SourceRect`
327
+ `{ x: number, y: number, width: number, height: number }` — screen / window coordinates in dp (pt).
242
328
 
243
- Updates the source thumbnail coordinates while the viewer is open. Call this inside `onPageSelected` after measuring the newly selected thumbnail's position, so the close animation correctly animates back to the right thumbnail.
329
+ ---
244
330
 
245
- ```typescript
246
- onPageSelected: async (index) => {
247
- const rect = await measureThumbnail(index); // your measurement logic
248
- if (rect) {
249
- AttachmentKit.updateSourceRect(rect);
250
- }
251
- }
331
+ ### Location messages
332
+
333
+ Recommended flow for a chat app that sends **coordinates only** (`captureLocationImage: false`):
334
+
335
+ 1. **Sender**: `openGallery({ showAttachMenu: true, captureLocationImage: false, onLocationSelected })` → send `latitude`, `longitude`, `title?`, `markerType`, `placeCategory?`, `imageSpanMeters`, and `currentLatitude/Longitude` only if `includesMyLocation`.
336
+ 2. **Receiver bubble**: `renderLocationThumbnail(...)` → show `uri` in the bubble (e.g. 170×128, `resizeMode="cover"`); `reverseGeocode(...)` → show the address in the app's language.
337
+ 3. **Tap**: `openMapViewer({ ..., sourceRect, sourceImageUri: thumb.uri })` → the bubble image grows into the map.
338
+
339
+ ```tsx
340
+ const bubbleRef = useRef<View>(null)
341
+
342
+ // bubble
343
+ const thumb = await AttachmentKit.renderLocationThumbnail({
344
+ latitude, longitude, markerType, placeCategory, spanMeters: imageSpanMeters,
345
+ width: 170, aspect: 'device', // screen-ratio image, shown cropped in a 4:3 bubble
346
+ maxWidth: 512, maxHeight: 1152, // sharp on 3× screens
347
+ })
348
+ const { address } = await AttachmentKit.reverseGeocode({ latitude, longitude })
349
+
350
+ // tap
351
+ bubbleRef.current?.measureInWindow((x, y, width, height) => {
352
+ AttachmentKit.openMapViewer({
353
+ latitude, longitude, title, markerType, placeCategory, spanMeters: imageSpanMeters,
354
+ sourceRect: { x, y, width, height },
355
+ sourceImageUri: thumb.uri, // pass the uri as returned
356
+ sourceCornerRadius: 16,
357
+ })
358
+ })
252
359
  ```
253
360
 
254
- ---
361
+ #### `renderLocationThumbnail(options: LocationThumbnailOptions): Promise<LocationThumbnailResult>`
255
362
 
256
- ### `AttachmentKit.closeGallery(): Promise<boolean>`
363
+ Draws a map thumbnail on this device from the location info alone, in this device's theme, and caches it on disk.
257
364
 
258
- Programmatically closes the gallery picker. Returns `true` if it was open and is now closed.
365
+ | Option | Type | Description |
366
+ |---|---|---|
367
+ | `latitude`, `longitude` | `number` | **Required.** |
368
+ | `width` | `number` (dp/pt) | **Required.** Output px = width × screen scale (capped by `maxWidth/maxHeight`). |
369
+ | `height` / `aspect` | `number` / `'square' \| 'portrait' \| 'device'` | Height directly, or from the width: `square` 1:1, `portrait` 9:19.5 (same as the sent snapshot), `device` this screen's ratio (the image then fills the screen exactly in the map viewer). |
370
+ | `maxWidth` / `maxHeight` | `number` (px) | Scale down to fit. Both omitted → 512 × 512; only one given → the other is not limited. |
371
+ | `markerType`, `placeCategory` | | Marker icon (as `AttachLocationResult`). Android has no per-category icons. |
372
+ | `spanMeters` | `number` | Distance covered by the width (`imageSpanMeters`). Default 400. |
373
+ | `myLatitude`, `myLongitude` | `number` | Only if the sender shared their location (`includesMyLocation`): draws the dot and widens the area. |
374
+ | `themeColor`, `appearance` | | Marker color, light/dark map (`system` = current app/device mode). |
259
375
 
260
- ---
376
+ Result: `{ uri: 'file://…webp', path, width, height (px), cached }`.
261
377
 
262
- ### `AttachmentKit.addSelectionChangeListener(listener): () => void`
378
+ - Cached by coordinates, output size, theme, marker, span, color and my-location; identical requests in flight are merged. A theme change renders once more, then that is cached too.
379
+ - Errors: `E_INVALID_ARGS`, `E_THUMBNAIL_FAILED` (no tiles — e.g. offline — or a 10 s timeout). Failures are not cached.
380
+ - The renderer also stores the same map without the marker; the map viewer uses it so the marker keeps the right size while the bubble grows into the map.
263
381
 
264
- Subscribes to selection change events outside of `openGallery`. Returns an unsubscribe function.
382
+ #### `reverseGeocode(options: ReverseGeocodeOptions): Promise<ReverseGeocodeResult>`
265
383
 
266
- ```typescript
267
- const unsubscribe = AttachmentKit.addSelectionChangeListener((event) => {
268
- console.log(`${event.selectedCount} / ${event.maxSelection} selected`);
269
- });
384
+ Coordinates → address with the **device geocoder** (iOS `CLGeocoder`, Android `Geocoder`) — free, no key.
270
385
 
271
- // Later, when done:
272
- unsubscribe();
386
+ ```ts
387
+ const { address, shortAddress, cached } = await AttachmentKit.reverseGeocode({
388
+ latitude: 37.5665, longitude: 126.978,
389
+ languageCode: 'ko', // optional — defaults to the init/setDefaults languageCode, as given
390
+ })
273
391
  ```
274
392
 
275
- ---
393
+ - `address`: full address (Location-tab format, no country). `shortAddress`: street / neighbourhood + number. Both `null` when there is no address (e.g. open sea) — a **resolve**, not a reject.
394
+ - Disk cache, key `round(lat,5)|round(lng,5)|languageCode`; "no result" is cached too. 1000 entries max, trimmed to 800 least recently used. A hit resolves with `cached: true` in a few ms.
395
+ - Same-key requests are merged; the geocoder is called **one request at a time** (Apple allows ~50 per app per minute). 8 s timeout per call.
396
+ - Rejects (never cached): `E_INVALID_COORDINATE`, `E_GEOCODE_UNAVAILABLE` (Android device without a geocoder), `E_GEOCODE_THROTTLED` (retry later; on iOS new requests are rejected right away until the throttle resets), `E_GEOCODE_TIMEOUT`, `E_GEOCODE_NETWORK` (offline), `E_GEOCODE_FAILED`.
397
+ - Logs never contain coordinates or addresses.
398
+ - iOS data limits (Apple Maps): Korean addresses come without the district (`중구`), and Korea has only Korean/English data, so `ja`, `zh-Hans`, … return Korean there. Android (Google) includes the district.
276
399
 
277
- ### `AttachmentKit.getDefaultAnimationConfig(): Promise<AnimationConfig>`
400
+ #### `openMapViewer(options: MapViewerOptions): Promise<void>`
278
401
 
279
- Returns the native default animation durations (in ms) for all transitions. Useful for synchronizing JS animations.
402
+ Full-screen map (Android Google Maps, iOS MapKit) with the sender's marker, a card (title, distance · address), map type / my-location buttons and an optional **Directions** button. Resolves when the viewer closes.
280
403
 
281
- ```typescript
282
- const config = await AttachmentKit.getDefaultAnimationConfig();
283
- // { galleryOpen, galleryClose, editorOpen, editorClose, viewerOpen, viewerClose }
284
- ```
404
+ | Option | Type | Description |
405
+ |---|---|---|
406
+ | `latitude`, `longitude` | `number` | **Required.** |
407
+ | `title?`, `address?` | `string` | Without `address` it is filled with `reverseGeocode` (same cache and language). |
408
+ | `markerType?`, `placeCategory?` | | Same marker as the sender. |
409
+ | `spanMeters?` | `number` | Initial zoom = same scale as the bubble image (`imageSpanMeters`). The point is exactly at the screen center. |
410
+ | `showDirections?` | `boolean` (default `true`) | Android: chooser of installed map apps (`geo:`). iOS: Apple Maps / Google Maps, plus Kakao Map / Naver Map when `languageCode` is `ko`. |
411
+ | `sourceRect?` | `SourceRect` | Bubble rect (`measureInWindow` values). Sticky open/close; omitted → fade. |
412
+ | `sourceImageUri?` | `string` | Bubble image (`file://` or `https://`). May have a different aspect than `sourceRect` (screen-ratio image in a 4:3 bubble): it is always drawn center-cropped, so the first frame equals the bubble, then the cropped top/bottom unfold; closing crops back. Not loaded within ~1.5 s → opens without it. |
413
+ | `sourceCornerRadius?` | `number` (default 12) | Bubble corner radius. |
414
+ | `themeColor?`, `languageCode?`, `appearance?` | | Per-call overrides. |
415
+
416
+ #### `updateMapViewerSourceRect(rect: SourceRect | null): Promise<void>`
417
+ While the map viewer is open, updates where it closes to (e.g. the chat list scrolled). `null` → the original `sourceRect`.
418
+
419
+ #### `clearLocationThumbnailCache()` / `clearReverseGeocodeCache(): Promise<void>`
420
+ Delete the thumbnail / address caches (e.g. on logout).
285
421
 
286
422
  ---
287
423
 
288
- ### `AttachmentKit.injectImageCache(url, localPath): Promise<boolean>`
424
+ ### Media gallery ("all photos")
289
425
 
290
- Pre-populates the native image cache (SDWebImage on iOS, Glide on Android) with a local file for a given remote URL. This lets the viewer display the image instantly from disk without a network request.
426
+ #### `openMediaGallery(options: MediaGalleryOptions): Promise<{ galleryId }>`
291
427
 
292
- ```typescript
293
- await AttachmentKit.injectImageCache(
294
- 'https://example.com/photo.jpg',
295
- '/path/to/cached/photo.jpg'
296
- );
297
- ```
428
+ A native grid built for thousands of photos. It opens like the picker (same bottom sheet: 60% → drag up to expand, drag down to close), with a date quick-scroller. Tapping a photo opens the image viewer with the sticky zoom from that cell; the viewer pages through every loaded photo and closes back into the cell you were viewing. Resolves when the screen is shown.
429
+
430
+ - **Native data source (recommended)**: implement `MediaGalleryDataSource` in your app's native code and register it with `MediaGalleryRegistry.register("name", source)` (iOS Swift / Android Kotlin). Pass `dataSource: "name"` and `dataSourceParams`. Pages are loaded natively, no JS round trip. Built-in demo: `dataSource: "turboDemo", dataSourceParams: { count: 10000 }`.
431
+ - **JS data source**: pass `initialItems`, `hasMore`, `nextCursor`; answer `onNeedPage` with `appendGalleryItems({ galleryId, items, nextCursor, hasMore })`.
432
+ - `MediaGalleryItem`: `{ id, groupId?, timestamp?, thumbUri? (file:// only — no base64), imageUri, cacheFile?, isMine?, width?, height? }`.
433
+ - `title` (required), `gridSize: '3x3' | '4x4' | '5x5'` (default until the user picks one; the user's choice is saved on the device and wins), `spacing`, `theme`, `appearance`, `languageCode`, `emptyTitle`, `emptySubtitle`, `countFormat`.
434
+ - `viewer`: `{ themeColor, deleteOptionsForMine, deleteOptionsForOthers, titleFormat: 'date' | 'none', …openViewer text options }`.
435
+ - Events: `onItemDeleted({ galleryId, id, groupId, option })` (the module already removed that group from the screen — delete it in the app), `onNeedPage`, `onGridSizeChanged`, `onClosed`.
436
+ - Other: `closeMediaGallery(galleryId)`, `removeGalleryItems({ galleryId, groupIds })`, `isMediaGallerySupported()`.
437
+ - Android: the sheet is a view on top of the host Activity (no Activity / Dialog window); system back closes it.
298
438
 
299
439
  ---
300
440
 
301
- ### `ImageResult` (Returned Object)
441
+ ### Misc
302
442
 
303
- When an image is selected or edited, the promise resolves to an array of `ImageResult` objects:
443
+ | API | Description |
444
+ |---|---|
445
+ | `getDefaultAnimationConfig()` | Native default durations (ms): `{ galleryOpen, galleryClose, editorOpen, editorClose, viewerOpen, viewerClose }`. |
446
+ | `isUsingTurboModule` / `isUsingLegacyModule` | Which architecture is in use. |
447
+ | `normalizeLanguageCode(code)` / `SUPPORTED_LANGUAGE_CODES` | UI language helpers. |
448
+ | `normalizeAppearance(value)` | `'light' \| 'dark'`, anything else → `'system'`. |
304
449
 
305
- | Field | Type | Description |
306
- |---|---|---|
307
- | `originalUri` | `string` | URI of the original asset (`ph://` or `file://`). |
308
- | `originalFileUri` | `string` | File URI of the original-resolution image (`file://`). |
309
- | `originalWidth` / `originalHeight` | `number` | Dimensions of the original asset. |
310
- | `uri` | `string` | URI of the resized/edited output image (`file://`). |
311
- | `width` / `height` | `number` | Dimensions of the output image. |
312
- | `type` | `string` | MIME type (e.g., `"image/jpeg"`). |
313
- | `caption` | `string` | Caption text entered in the editor (if any). |
314
- | `fileName` | `string` | File name of the output image. |
315
- | `fileExtension` | `string` | File extension (e.g., `"jpg"`). |
316
- | `fileSize` | `number` | File size in bytes. |
450
+ ---
451
+
452
+ ## ⚠️ Error codes
453
+
454
+ | Code | Where |
455
+ |---|---|
456
+ | `E_ALREADY_OPEN` | `openGallery` / `openEditor` while one is already open. |
457
+ | `E_INVALID_ARGS`, `E_THUMBNAIL_FAILED` | `renderLocationThumbnail`. |
458
+ | `E_INVALID_COORDINATE`, `E_GEOCODE_UNAVAILABLE`, `E_GEOCODE_THROTTLED`, `E_GEOCODE_TIMEOUT`, `E_GEOCODE_NETWORK`, `E_GEOCODE_FAILED` | `reverseGeocode` (type `ReverseGeocodeErrorCode`). |
317
459
 
318
460
  ---
319
461
 
320
- ### `SourceRect`
462
+ ## 🗂 Caches & temp files
321
463
 
322
- ```typescript
323
- interface SourceRect {
324
- x: number; // Screen X coordinate of the thumbnail
325
- y: number; // Screen Y coordinate of the thumbnail
326
- width: number; // Thumbnail width
327
- height: number; // Thumbnail height
328
- }
329
- ```
464
+ | What | iOS | Android | Lifetime |
465
+ |---|---|---|---|
466
+ | Files from the File tab | `tmp/AttachmentKitFiles/<uuid>/<name>` | `cache/AttachmentKitFiles/<uuid>/<name>` | Deleted after 24 h (next gallery open); the OS may purge earlier — copy/upload promptly. |
467
+ | Location snapshots | `tmp/AttachmentKitLocation/` | `cache/AttachmentKitLocation/` | Deleted after 24 h. |
468
+ | Location thumbnails | `Library/Caches/RNAttachmentKit/LocationThumbs/` | `cache/RNAttachmentKitLocationThumbs/` | 300 max → 250, least recently used. `clearLocationThumbnailCache()`. |
469
+ | Reverse-geocode results | `Library/Caches/RNAttachmentKit/Geocode/` | `cache/RNAttachmentKitGeocode/` | 1000 max → 800, least recently used. `clearReverseGeocodeCache()`. |
330
470
 
331
471
  ---
332
472
 
333
473
  ## 📜 License & Contact
334
474
 
335
- This `react-native-attachment-kit` library is released under the **MIT License**.
475
+ `react-native-attachment-kit` is released under the **MIT License**. Removing the editor watermark requires a license key passed to `init()`.
476
+
477
+ 🛒 **License**: [https://license.rnturboimagepicker.usomnia.co.kr](https://license.rnturboimagepicker.usomnia.co.kr)
478
+ 📧 **Contact**: [contact@usomnia.co.kr](mailto:contact@usomnia.co.kr)
336
479
 
337
- 📧 **Contact**: [contact@usomnia.co.kr](mailto:contact@usomnia.co.kr)
480
+ See `CHANGELOG.md` for version history.