react-native-attachment-kit 1.0.1 → 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.
- package/CHANGELOG.md +116 -0
- package/README.md +356 -213
- package/android/build.gradle +3 -0
- package/android/src/main/libs/rnattachmentkit-release.aar +0 -0
- package/android/src/newarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +308 -10
- package/android/src/oldarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +311 -9
- package/ios/RNAttachmentKit.m +44 -0
- package/ios/RNAttachmentKit.swift +307 -23
- package/ios/RNAttachmentKit.xcframework/Info.plist +5 -5
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Assets.car +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Headers/RNAttachmentKit-Swift.h +30 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.abi.json +20349 -13021
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.private.swiftinterface +118 -1
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftinterface +118 -1
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/RNAttachmentKit +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeResources +194 -62
- package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeSignature +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Assets.car +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Headers/RNAttachmentKit-Swift.h +60 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.abi.json +20349 -13021
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +118 -1
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +118 -1
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +20349 -13021
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +118 -1
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +118 -1
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/RNAttachmentKit +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~dark.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~light.webp +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
- package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeResources +204 -72
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_back~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_back~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_blur~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_close~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_close~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_crop~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_effect~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_text~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_text~light.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_text_add~dark.webp +0 -0
- package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_text_add~light.webp +0 -0
- package/package.json +1 -1
- package/src/NativeAttachmentKit.ts +10 -0
- package/src/index.d.ts +5 -56
- package/src/index.ts +188 -35
- package/src/types.ts +207 -11
package/README.md
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# React Native Attachment Kit 🚀
|
|
2
2
|
|
|
3
|
-
A
|
|
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
|
-
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
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
|
-
|
|
50
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
##
|
|
95
|
+
## 🛠 Quick start
|
|
131
96
|
|
|
132
|
-
|
|
97
|
+
```ts
|
|
98
|
+
import AttachmentKit from 'react-native-attachment-kit'
|
|
133
99
|
|
|
134
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
###
|
|
111
|
+
### Global defaults
|
|
144
112
|
|
|
145
|
-
|
|
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
|
-
|
|
133
|
+
## 📖 API reference
|
|
154
134
|
|
|
155
|
-
|
|
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
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
163
|
-
| `
|
|
164
|
-
| `
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
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
|
-
###
|
|
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
|
-
|
|
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` |
|
|
183
|
-
| `editedFileUri
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `openDuration` / `closeDuration` | `number` |
|
|
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
|
-
###
|
|
249
|
+
### Image viewer
|
|
191
250
|
|
|
192
|
-
|
|
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
|
-
|
|
271
|
+
**Basic**
|
|
195
272
|
|
|
196
273
|
| Option | Type | Default | Description |
|
|
197
274
|
|---|---|---|---|
|
|
198
|
-
| `images` | `string[]` | **required** |
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
202
|
-
| `title` | `string` | — |
|
|
203
|
-
| `
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
284
|
+
**Sticky transition**
|
|
208
285
|
|
|
209
286
|
| Option | Type | Description |
|
|
210
287
|
|---|---|---|
|
|
211
|
-
| `sourceRect` | `SourceRect` |
|
|
212
|
-
| `sourceBorderRadius` | `number` |
|
|
213
|
-
| `sourceBorderCorners` | `('topLeft' \| 'topRight' \| 'bottomLeft' \| 'bottomRight')[]` |
|
|
214
|
-
| `sourceBackgroundColor` | `string` | Background
|
|
215
|
-
| `hideSourceImage` | `boolean` |
|
|
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
|
-
|
|
294
|
+
**Actions**
|
|
218
295
|
|
|
219
|
-
| Option | Type |
|
|
220
|
-
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
223
|
-
| `
|
|
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
|
-
|
|
304
|
+
**Events**
|
|
226
305
|
|
|
227
|
-
|
|
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
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
326
|
+
#### `SourceRect`
|
|
327
|
+
`{ x: number, y: number, width: number, height: number }` — screen / window coordinates in dp (pt).
|
|
242
328
|
|
|
243
|
-
|
|
329
|
+
---
|
|
244
330
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
382
|
+
#### `reverseGeocode(options: ReverseGeocodeOptions): Promise<ReverseGeocodeResult>`
|
|
265
383
|
|
|
266
|
-
|
|
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
|
-
|
|
272
|
-
|
|
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
|
-
|
|
400
|
+
#### `openMapViewer(options: MapViewerOptions): Promise<void>`
|
|
278
401
|
|
|
279
|
-
|
|
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
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
###
|
|
424
|
+
### Media gallery ("all photos")
|
|
289
425
|
|
|
290
|
-
|
|
426
|
+
#### `openMediaGallery(options: MediaGalleryOptions): Promise<{ galleryId }>`
|
|
291
427
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
###
|
|
441
|
+
### Misc
|
|
302
442
|
|
|
303
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
|
310
|
-
|
|
311
|
-
| `
|
|
312
|
-
| `
|
|
313
|
-
| `
|
|
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
|
-
|
|
462
|
+
## 🗂 Caches & temp files
|
|
321
463
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
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
|
-
|
|
480
|
+
See `CHANGELOG.md` for version history.
|