@onekeyfe/react-native-image-crop-picker 3.0.163 → 3.0.165

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/README.md CHANGED
@@ -2,13 +2,29 @@
2
2
 
3
3
  Single photo picker and cropper for OneKey, built on [Nitro Modules](https://nitro.margelo.com/). It replaces `react-native-image-crop-picker` and keeps the part of its API that OneKey uses: `openPicker`, `openCropper`, `clean`, `cleanSingle`, and the `E_*` error codes.
4
4
 
5
- Neither platform asks for photo library permission:
5
+ Picking never asks for photo library permission:
6
6
 
7
7
  - **iOS** picks with `PHPickerViewController`, which runs out of process. `react-native-image-crop-picker` requested full photo library access first. Once a user denied it, iOS never showed the prompt again and every later `openPicker` call failed silently (OK-48227). The crop gestures come from a vendored TOCropViewController 3.2.0 `TOCropView`, with the OK-51551 rotation fix that upstream still lacks.
8
8
  - **Android** picks with the system Photo Picker (`ActivityResultContracts.PickVisualMedia`, which falls back to `ACTION_OPEN_DOCUMENT` on devices without it). The crop gestures come from uCrop `2.2.11-native`'s `UCropView`. Activity results go through the activity's `ActivityResultRegistry`, so no `ActivityEventListener` is needed.
9
9
 
10
10
  Both platforms show the same cropper screen, drawn by this package rather than by TOCropViewController or uCrop. See [Cropper screen](#cropper-screen).
11
11
 
12
+ The behavioral contract and pending acceptance cases are in [docs/SPEC.md](docs/SPEC.md).
13
+
14
+ ## Original selection
15
+
16
+ ```ts
17
+ const image = await ImageCropPicker.openPicker({ preserveOriginal: true });
18
+ // Read the selected file before cleaning it.
19
+ await ImageCropPicker.cleanSingle(image.path);
20
+ ```
21
+
22
+ Original selection preserves the selected representation without JPEG conversion.
23
+ Compression options are ignored unless cropping is enabled. Originals are limited
24
+ to 64 MiB. Saving and its authorization are provided by the separate
25
+ `@onekeyfe/react-native-photo-library`; this picker exposes neither saving nor
26
+ photo-library permission APIs.
27
+
12
28
  ## Installation
13
29
 
14
30
  ```sh
@@ -94,7 +110,7 @@ Colors are CSS hex strings: `#RGB`, `#RRGGBB` or `#RRGGBBAA`. Anything left out
94
110
 
95
111
  ## Behavior
96
112
 
97
- - Results are always JPEG. `width` and `height` set the crop aspect ratio, and the cropped image is scaled to exactly that size, so a crop box a pixel off the ratio still yields the requested dimensions. With `freeStyleCropEnabled`, the crop keeps its own aspect ratio and is scaled to fit inside `width` × `height`.
113
+ - Processed results are always JPEG. `width` and `height` set the crop aspect ratio, and the cropped image is scaled to exactly that size, so a crop box a pixel off the ratio still yields the requested dimensions. With `freeStyleCropEnabled`, the crop keeps its own aspect ratio and is scaled to fit inside `width` × `height`.
98
114
  - `compressImageMaxWidth`, `compressImageMaxHeight` and `compressImageQuality` apply after cropping. The default quality is 0.8 on iOS and 1 on Android, as in `react-native-image-crop-picker`.
99
115
  - Photos are decoded at most 4096 px on the long side, which keeps very large photos from exhausting memory. `cropRect` is reported in the original image's coordinates on iOS.
100
116
  - Results are written to `<tmp>/react-native-image-crop-picker/` on iOS and `<cache>/react-native-image-crop-picker/` on Android. `clean()` empties that directory.
@@ -25,11 +25,12 @@ Pod::Spec.new do |s|
25
25
  # use it. It replaces the standalone TOCropViewController pod, which must not
26
26
  # be installed alongside.
27
27
  s.public_header_files = ["ios/TOCropViewController/**/*.h"]
28
+ s.exclude_files = "ios/tests/**/*"
28
29
  # TOCropViewController looks up its strings in a bundle with exactly this name.
29
30
  s.resource_bundles = {
30
31
  "TOCropViewControllerBundle" => ["ios/TOCropViewController/Resources/**/*.{lproj,xcprivacy}"],
31
32
  }
32
- s.frameworks = "PhotosUI", "UniformTypeIdentifiers"
33
+ s.frameworks = "Photos", "PhotosUI", "UniformTypeIdentifiers"
33
34
 
34
35
  s.dependency 'React-jsi'
35
36
  s.dependency 'React-callinvoker'
@@ -39,4 +40,8 @@ Pod::Spec.new do |s|
39
40
  add_nitrogen_files(s)
40
41
 
41
42
  install_modules_dependencies(s)
43
+
44
+ s.test_spec "Tests" do |test_spec|
45
+ test_spec.source_files = "ios/tests/**/*.swift"
46
+ end
42
47
  end
@@ -26,10 +26,10 @@ internal class ImageCropPickerException(
26
26
  fun noActivity() = ImageCropPickerException(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist")
27
27
 
28
28
  fun failedToShowPicker(cause: Throwable) =
29
- ImageCropPickerException(E_FAILED_TO_SHOW_PICKER, cause.message ?: "Cannot show picker", cause)
29
+ ImageCropPickerException(E_FAILED_TO_SHOW_PICKER, "Cannot show picker", cause)
30
30
 
31
- fun noImageData(message: String = "Cannot find image data") =
32
- ImageCropPickerException(E_NO_IMAGE_DATA_FOUND, message)
31
+ fun noImageData() =
32
+ ImageCropPickerException(E_NO_IMAGE_DATA_FOUND, "Cannot find image data")
33
33
 
34
34
  fun cropperImageNotFound() =
35
35
  ImageCropPickerException(E_CROPPER_IMAGE_NOT_FOUND, "Can't find the image at the specified path")
@@ -38,8 +38,9 @@ internal class ImageCropPickerException(
38
38
  ImageCropPickerException(E_CANNOT_SAVE_IMAGE, "Cannot save image. Unable to write to tmp location.", cause)
39
39
 
40
40
  fun lowMemory(cause: Throwable) =
41
- ImageCropPickerException(E_LOW_MEMORY_ERROR, cause.message ?: "Out of memory", cause)
41
+ ImageCropPickerException(E_LOW_MEMORY_ERROR, "Out of memory", cause)
42
42
 
43
43
  fun cleanupFailed() = ImageCropPickerException(E_ERROR_WHILE_CLEANING_FILES, "Error while cleaning up tmp files")
44
+
44
45
  }
45
46
  }
@@ -6,6 +6,7 @@ import android.graphics.BitmapFactory
6
6
  import android.graphics.Matrix
7
7
  import android.net.Uri
8
8
  import android.util.Base64
9
+ import android.webkit.MimeTypeMap
9
10
  import androidx.exifinterface.media.ExifInterface
10
11
  import java.io.File
11
12
  import java.io.FileOutputStream
@@ -22,6 +23,7 @@ internal data class ImageCropPickerConfig(
22
23
  val height: Int?,
23
24
  val cropping: Boolean,
24
25
  val includeBase64: Boolean,
26
+ val preserveOriginal: Boolean,
25
27
  val compressImageQuality: Double?,
26
28
  val compressImageMaxWidth: Double?,
27
29
  val compressImageMaxHeight: Double?,
@@ -40,6 +42,7 @@ internal data class ImageCropPickerConfig(
40
42
  height = options.height?.takeIf { it.isFinite() }?.roundToInt()?.takeIf { it > 0 },
41
43
  cropping = forceCropping || options.cropping == true,
42
44
  includeBase64 = options.includeBase64 == true,
45
+ preserveOriginal = options.preserveOriginal == true,
43
46
  compressImageQuality = options.compressImageQuality,
44
47
  compressImageMaxWidth = options.compressImageMaxWidth,
45
48
  compressImageMaxHeight = options.compressImageMaxHeight,
@@ -62,6 +65,54 @@ internal object ImageCropPickerImageProcessor {
62
65
  // Matches react-native-image-crop-picker on Android.
63
66
  private const val DEFAULT_COMPRESS_QUALITY = 1.0
64
67
  private const val TEMPORARY_DIRECTORY_NAME = "react-native-image-crop-picker"
68
+ const val MAX_ORIGINAL_BYTES = 64L * 1024 * 1024
69
+
70
+ fun imageBounds(file: File): BitmapFactory.Options {
71
+ if (!file.isFile || file.length() <= 0 || file.length() > MAX_ORIGINAL_BYTES) {
72
+ throw ImageCropPickerException.noImageData()
73
+ }
74
+ val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
75
+ BitmapFactory.decodeFile(file.absolutePath, bounds)
76
+ if (bounds.outWidth <= 0 || bounds.outHeight <= 0 || bounds.outMimeType?.startsWith("image/") != true) {
77
+ throw ImageCropPickerException.noImageData()
78
+ }
79
+ return bounds
80
+ }
81
+
82
+ fun copyOriginal(context: Context, uri: Uri, config: ImageCropPickerConfig, filename: String?): PickedImage {
83
+ val mime = context.contentResolver.getType(uri)
84
+ val extension = mime?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) } ?: "img"
85
+ val file = createTemporaryFile(context, extension)
86
+ try {
87
+ openStream(context, uri).use { input ->
88
+ FileOutputStream(file).use { output ->
89
+ val buffer = ByteArray(8192)
90
+ var total = 0L
91
+ while (true) {
92
+ val count = input.read(buffer)
93
+ if (count < 0) break
94
+ total += count
95
+ if (total > MAX_ORIGINAL_BYTES) throw ImageCropPickerException.noImageData()
96
+ output.write(buffer, 0, count)
97
+ }
98
+ }
99
+ }
100
+ val bounds = imageBounds(file)
101
+ return PickedImage(
102
+ path = Uri.fromFile(file).toString(),
103
+ size = file.length().toDouble(),
104
+ width = bounds.outWidth.toDouble(),
105
+ height = bounds.outHeight.toDouble(),
106
+ mime = bounds.outMimeType,
107
+ data = if (config.includeBase64) Base64.encodeToString(file.readBytes(), Base64.NO_WRAP) else null,
108
+ cropRect = null,
109
+ filename = filename,
110
+ )
111
+ } catch (error: Throwable) {
112
+ file.delete()
113
+ throw error
114
+ }
115
+ }
65
116
 
66
117
  fun temporaryDirectory(context: Context): File {
67
118
  val directory = File(context.cacheDir, TEMPORARY_DIRECTORY_NAME)
@@ -135,7 +186,7 @@ internal object ImageCropPickerImageProcessor {
135
186
  val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
136
187
  openStream(context, uri).use { BitmapFactory.decodeStream(it, null, bounds) }
137
188
  if (bounds.outWidth <= 0 || bounds.outHeight <= 0) {
138
- throw ImageCropPickerException.noImageData("Invalid image selected")
189
+ throw ImageCropPickerException.noImageData()
139
190
  }
140
191
 
141
192
  var sampleSize = 1
@@ -147,7 +198,7 @@ internal object ImageCropPickerImageProcessor {
147
198
  openStream(context, uri).use { BitmapFactory.decodeStream(it, null, options) }
148
199
  } catch (error: OutOfMemoryError) {
149
200
  throw ImageCropPickerException.lowMemory(error)
150
- } ?: throw ImageCropPickerException.noImageData("Invalid image selected")
201
+ } ?: throw ImageCropPickerException.noImageData()
151
202
 
152
203
  val orientation = try {
153
204
  openStream(context, uri).use {
@@ -85,7 +85,9 @@ internal class ImageCropPickerSession(
85
85
  val filename = queryDisplayName(uri)
86
86
  runInBackground(
87
87
  work = {
88
- ImageCropPickerImageProcessor.makeResult(
88
+ if (config.preserveOriginal) {
89
+ ImageCropPickerImageProcessor.copyOriginal(activity, uri, config, filename)
90
+ } else ImageCropPickerImageProcessor.makeResult(
89
91
  activity,
90
92
  ImageCropPickerImageProcessor.decodeBitmap(activity, uri),
91
93
  config,
@@ -169,9 +171,7 @@ internal class ImageCropPickerSession(
169
171
  )
170
172
  }
171
173
  ImageCropperActivity.RESULT_ERROR -> {
172
- val message = data?.getStringExtra(ImageCropperActivity.EXTRA_ERROR_MESSAGE)
173
- ?: "Cannot crop image"
174
- finish(Result.failure(ImageCropPickerException.noImageData(message)))
174
+ finish(Result.failure(ImageCropPickerException.noImageData()))
175
175
  }
176
176
  else -> finish(Result.failure(ImageCropPickerException.cancelled()))
177
177
  }
@@ -212,7 +212,7 @@ internal class ImageCropPickerSession(
212
212
  } catch (error: OutOfMemoryError) {
213
213
  Result.failure(ImageCropPickerException.lowMemory(error))
214
214
  } catch (error: Exception) {
215
- Result.failure(ImageCropPickerException.noImageData(error.message ?: "Cannot process image"))
215
+ Result.failure(ImageCropPickerException.noImageData())
216
216
  }
217
217
  mainHandler.post {
218
218
  isProcessing = false
@@ -3,6 +3,7 @@ package com.margelo.nitro.reactnativeimagecroppicker
3
3
  import android.os.Handler
4
4
  import android.os.Looper
5
5
  import androidx.activity.ComponentActivity
6
+ import com.facebook.react.ReactApplication
6
7
  import com.facebook.proguard.annotations.DoNotStrip
7
8
  import com.margelo.nitro.NitroModules
8
9
  import com.margelo.nitro.core.Promise
@@ -50,7 +51,9 @@ class ReactNativeImageCropPicker : HybridReactNativeImageCropPickerSpec() {
50
51
  session.abandon()
51
52
  }
52
53
 
53
- val activity = NitroModules.applicationContext?.currentActivity as? ComponentActivity
54
+ val context = NitroModules.applicationContext
55
+ val activity = ((context?.applicationContext as? ReactApplication)?.reactHost?.currentReactContext?.currentActivity
56
+ ?: context?.currentActivity) as? ComponentActivity
54
57
  if (activity == null || activity.isFinishing || activity.isDestroyed) {
55
58
  promise.reject(ImageCropPickerException.noActivity())
56
59
  return@post
@@ -54,4 +54,5 @@ struct ImageCropPickerError: Error, CustomStringConvertible {
54
54
  code: .cleanupError,
55
55
  message: "Error while cleaning up tmp files"
56
56
  )
57
+
57
58
  }
@@ -1,6 +1,7 @@
1
1
  import Foundation
2
2
  import ImageIO
3
3
  import UIKit
4
+ import UniformTypeIdentifiers
4
5
 
5
6
  // Plain Swift copy of the options, so no C++ backed struct outlives the JS call.
6
7
  struct ImageCropPickerConfig {
@@ -8,6 +9,7 @@ struct ImageCropPickerConfig {
8
9
  let height: Double?
9
10
  let cropping: Bool
10
11
  let includeBase64: Bool
12
+ let preserveOriginal: Bool
11
13
  let compressImageQuality: Double?
12
14
  let compressImageMaxWidth: Double?
13
15
  let compressImageMaxHeight: Double?
@@ -25,6 +27,7 @@ struct ImageCropPickerConfig {
25
27
  height = options.height
26
28
  cropping = forceCropping || (options.cropping ?? false)
27
29
  includeBase64 = options.includeBase64 ?? false
30
+ preserveOriginal = options.preserveOriginal ?? false
28
31
  compressImageQuality = options.compressImageQuality
29
32
  compressImageMaxWidth = options.compressImageMaxWidth
30
33
  compressImageMaxHeight = options.compressImageMaxHeight
@@ -58,6 +61,45 @@ enum ImageCropPickerImageProcessor {
58
61
  static let maxDecodedPixelSize = 4096
59
62
  static let defaultCompressQuality = 0.8
60
63
  static let temporaryDirectoryName = "react-native-image-crop-picker"
64
+ static let maxOriginalBytes = 64 * 1024 * 1024
65
+
66
+ static func imageMetadata(at url: URL) throws -> (width: Int, height: Int, type: UTType, size: Int) {
67
+ let size = (try? url.resourceValues(forKeys: [.fileSizeKey]).fileSize) ?? 0
68
+ guard size > 0, size <= maxOriginalBytes,
69
+ let source = CGImageSourceCreateWithURL(url as CFURL, [kCGImageSourceShouldCache: false] as CFDictionary),
70
+ let identifier = CGImageSourceGetType(source),
71
+ let type = UTType(identifier as String), type.conforms(to: .image),
72
+ let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any],
73
+ let width = (properties[kCGImagePropertyPixelWidth] as? NSNumber)?.intValue,
74
+ let height = (properties[kCGImagePropertyPixelHeight] as? NSNumber)?.intValue,
75
+ width > 0, height > 0 else {
76
+ throw ImageCropPickerError.noImageData
77
+ }
78
+ return (width, height, type, size)
79
+ }
80
+
81
+ static func copyOriginal(at sourceURL: URL, config: ImageCropPickerConfig, filename: String?) throws -> PickedImage {
82
+ let metadata = try imageMetadata(at: sourceURL)
83
+ let destination = try temporaryDirectory()
84
+ .appendingPathComponent(UUID().uuidString)
85
+ .appendingPathExtension(metadata.type.preferredFilenameExtension ?? "img")
86
+ do {
87
+ try FileManager.default.copyItem(at: sourceURL, to: destination)
88
+ return PickedImage(
89
+ path: destination.absoluteString,
90
+ size: Double(metadata.size),
91
+ width: Double(metadata.width),
92
+ height: Double(metadata.height),
93
+ mime: metadata.type.preferredMIMEType ?? "application/octet-stream",
94
+ data: config.includeBase64 ? try Data(contentsOf: destination).base64EncodedString() : nil,
95
+ cropRect: nil,
96
+ filename: filename
97
+ )
98
+ } catch {
99
+ try? FileManager.default.removeItem(at: destination)
100
+ throw ImageCropPickerError.cannotSaveImage
101
+ }
102
+ }
61
103
 
62
104
  static func decodeImage(at url: URL) -> DecodedImage? {
63
105
  let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
@@ -136,6 +136,17 @@ final class ImageCropPickerSession: NSObject {
136
136
  showLoading(in: picker.view)
137
137
 
138
138
  provider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { [self] url, _ in
139
+ if config.preserveOriginal && !config.cropping {
140
+ guard let url else {
141
+ completeOriginal(.failure(ImageCropPickerError.noImageData))
142
+ return
143
+ }
144
+ // The provider deletes its file when this callback returns.
145
+ completeOriginal(Result {
146
+ try ImageCropPickerImageProcessor.copyOriginal(at: url, config: config, filename: filename)
147
+ })
148
+ return
149
+ }
139
150
  // The file is removed once this handler returns, so decode it right here.
140
151
  if let url, let decoded = ImageCropPickerImageProcessor.decodeImage(at: url) {
141
152
  handlePickedImage(decoded, filename: filename)
@@ -150,6 +161,14 @@ final class ImageCropPickerSession: NSObject {
150
161
  }
151
162
  }
152
163
 
164
+ private func completeOriginal(_ result: Result<PickedImage, Error>) {
165
+ DispatchQueue.main.async { [self] in
166
+ isBusy = false
167
+ hideLoading()
168
+ dismissAll { self.finish(result) }
169
+ }
170
+ }
171
+
153
172
  // Runs on the item provider's background queue.
154
173
  private func handlePickedImage(_ decoded: DecodedImage?, filename: String?) {
155
174
  guard let decoded else {
@@ -0,0 +1,55 @@
1
+ import UIKit
2
+ import XCTest
3
+
4
+ @testable import ReactNativeImageCropPicker
5
+
6
+ final class ImageOriginalCopyTests: XCTestCase {
7
+ func testOriginalCopyPreservesPNGBytesAndDimensions() throws {
8
+ let renderer = UIGraphicsImageRenderer(size: CGSize(width: 12, height: 8), format: {
9
+ let format = UIGraphicsImageRendererFormat()
10
+ format.scale = 1
11
+ return format
12
+ }())
13
+ let bytes = renderer.pngData { context in
14
+ UIColor.red.setFill()
15
+ context.fill(CGRect(x: 0, y: 0, width: 12, height: 8))
16
+ }
17
+ let source = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".png")
18
+ try bytes.write(to: source)
19
+ defer { try? FileManager.default.removeItem(at: source) }
20
+ let options: ImageCropPickerOptions = .init(
21
+ width: nil, height: nil, cropping: false, includeBase64: true, preserveOriginal: true,
22
+ compressImageQuality: 0.1, compressImageMaxWidth: 1, compressImageMaxHeight: 1,
23
+ freeStyleCropEnabled: nil, cropperCircleOverlay: nil, cropperToolbarTitle: nil,
24
+ cropperChooseText: nil, cropperCancelText: nil, cropperRotateButtonsHidden: nil,
25
+ showCropGuidelines: nil, cropperAppearance: nil
26
+ )
27
+ let config = ImageCropPickerConfig(options)
28
+ let result = try ImageCropPickerImageProcessor.copyOriginal(at: source, config: config, filename: "source.png")
29
+ defer { try? ImageCropPickerImageProcessor.removeFile(atPath: result.path) }
30
+ let copied = try XCTUnwrap(URL(string: result.path))
31
+ XCTAssertEqual(try Data(contentsOf: copied), bytes)
32
+ XCTAssertEqual(result.data, bytes.base64EncodedString())
33
+ XCTAssertEqual(result.mime, "image/png")
34
+ XCTAssertEqual(result.width, 12)
35
+ XCTAssertEqual(result.height, 8)
36
+ }
37
+
38
+ func testMetadataRejectsNonImageAndOversizedFile() throws {
39
+ let file = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
40
+ defer { try? FileManager.default.removeItem(at: file) }
41
+ try Data("not an image".utf8).write(to: file)
42
+ XCTAssertThrowsError(try ImageCropPickerImageProcessor.imageMetadata(at: file))
43
+ let handle = try FileHandle(forWritingTo: file)
44
+ try handle.truncate(atOffset: UInt64(ImageCropPickerImageProcessor.maxOriginalBytes + 1))
45
+ try handle.close()
46
+ XCTAssertThrowsError(try ImageCropPickerImageProcessor.imageMetadata(at: file))
47
+ }
48
+
49
+ func testMissingSourceUsesTheDocumentedError() {
50
+ let missing = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
51
+ XCTAssertThrowsError(try ImageCropPickerImageProcessor.imageMetadata(at: missing)) { error in
52
+ XCTAssertEqual((error as? ImageCropPickerError)?.code, ImageCropPickerError.noImageData.code)
53
+ }
54
+ }
55
+ }
@@ -40,7 +40,7 @@ function toImageCropPickerError(error) {
40
40
  }
41
41
 
42
42
  // Options accepted for source compatibility with react-native-image-crop-picker.
43
- // Only single photos are supported, and results are always JPEG.
43
+ // Only single photos are supported; processed results are JPEG.
44
44
 
45
45
  export async function openPicker(options = {}) {
46
46
  try {
@@ -36,6 +36,7 @@ export interface ImageCropPickerOptions {
36
36
  height?: number;
37
37
  cropping?: boolean;
38
38
  includeBase64?: boolean;
39
+ preserveOriginal?: boolean;
39
40
  compressImageQuality?: number;
40
41
  compressImageMaxWidth?: number;
41
42
  compressImageMaxHeight?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"ReactNativeImageCropPicker.nitro.d.ts","sourceRoot":"","sources":["../../../src/ReactNativeImageCropPicker.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAE1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,MAAM,CAAC;AAKvD,MAAM,WAAW,sBAAsB;IAErC,WAAW,CAAC,EAAE,uBAAuB,CAAC;IAEtC,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IAErC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;CAC5C;AAED,MAAM,WAAW,0BACf,SAAQ,YAAY,CAAC;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;IAGzD,UAAU,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAIlE,WAAW,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,WAAW,CAAC,CAAC;IAGxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C"}
1
+ {"version":3,"file":"ReactNativeImageCropPicker.nitro.d.ts","sourceRoot":"","sources":["../../../src/ReactNativeImageCropPicker.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAE1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,MAAM,CAAC;AAKvD,MAAM,WAAW,sBAAsB;IAErC,WAAW,CAAC,EAAE,uBAAuB,CAAC;IAEtC,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IAErC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;CAC5C;AAED,MAAM,WAAW,0BACf,SAAQ,YAAY,CAAC;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;IAGzD,UAAU,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAIlE,WAAW,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,WAAW,CAAC,CAAC;IAGxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAGvB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C"}
@@ -44,6 +44,8 @@ namespace margelo::nitro::reactnativeimagecroppicker {
44
44
  jni::local_ref<jni::JBoolean> cropping = this->getFieldValue(fieldCropping);
45
45
  static const auto fieldIncludeBase64 = clazz->getField<jni::JBoolean>("includeBase64");
46
46
  jni::local_ref<jni::JBoolean> includeBase64 = this->getFieldValue(fieldIncludeBase64);
47
+ static const auto fieldPreserveOriginal = clazz->getField<jni::JBoolean>("preserveOriginal");
48
+ jni::local_ref<jni::JBoolean> preserveOriginal = this->getFieldValue(fieldPreserveOriginal);
47
49
  static const auto fieldCompressImageQuality = clazz->getField<jni::JDouble>("compressImageQuality");
48
50
  jni::local_ref<jni::JDouble> compressImageQuality = this->getFieldValue(fieldCompressImageQuality);
49
51
  static const auto fieldCompressImageMaxWidth = clazz->getField<jni::JDouble>("compressImageMaxWidth");
@@ -71,6 +73,7 @@ namespace margelo::nitro::reactnativeimagecroppicker {
71
73
  height != nullptr ? std::make_optional(height->value()) : std::nullopt,
72
74
  cropping != nullptr ? std::make_optional(static_cast<bool>(cropping->value())) : std::nullopt,
73
75
  includeBase64 != nullptr ? std::make_optional(static_cast<bool>(includeBase64->value())) : std::nullopt,
76
+ preserveOriginal != nullptr ? std::make_optional(static_cast<bool>(preserveOriginal->value())) : std::nullopt,
74
77
  compressImageQuality != nullptr ? std::make_optional(compressImageQuality->value()) : std::nullopt,
75
78
  compressImageMaxWidth != nullptr ? std::make_optional(compressImageMaxWidth->value()) : std::nullopt,
76
79
  compressImageMaxHeight != nullptr ? std::make_optional(compressImageMaxHeight->value()) : std::nullopt,
@@ -91,7 +94,7 @@ namespace margelo::nitro::reactnativeimagecroppicker {
91
94
  */
92
95
  [[maybe_unused]]
93
96
  static jni::local_ref<JImageCropPickerOptions::javaobject> fromCpp(const ImageCropPickerOptions& value) {
94
- using JSignature = JImageCropPickerOptions(jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<JImageCropperAppearance>);
97
+ using JSignature = JImageCropPickerOptions(jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<JImageCropperAppearance>);
95
98
  static const auto clazz = javaClassStatic();
96
99
  static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
97
100
  return create(
@@ -100,6 +103,7 @@ namespace margelo::nitro::reactnativeimagecroppicker {
100
103
  value.height.has_value() ? jni::JDouble::valueOf(value.height.value()) : nullptr,
101
104
  value.cropping.has_value() ? jni::JBoolean::valueOf(value.cropping.value()) : nullptr,
102
105
  value.includeBase64.has_value() ? jni::JBoolean::valueOf(value.includeBase64.value()) : nullptr,
106
+ value.preserveOriginal.has_value() ? jni::JBoolean::valueOf(value.preserveOriginal.value()) : nullptr,
103
107
  value.compressImageQuality.has_value() ? jni::JDouble::valueOf(value.compressImageQuality.value()) : nullptr,
104
108
  value.compressImageMaxWidth.has_value() ? jni::JDouble::valueOf(value.compressImageMaxWidth.value()) : nullptr,
105
109
  value.compressImageMaxHeight.has_value() ? jni::JDouble::valueOf(value.compressImageMaxHeight.value()) : nullptr,
@@ -32,6 +32,9 @@ data class ImageCropPickerOptions(
32
32
  val includeBase64: Boolean?,
33
33
  @DoNotStrip
34
34
  @Keep
35
+ val preserveOriginal: Boolean?,
36
+ @DoNotStrip
37
+ @Keep
35
38
  val compressImageQuality: Double?,
36
39
  @DoNotStrip
37
40
  @Keep
@@ -73,6 +76,7 @@ data class ImageCropPickerOptions(
73
76
  && Objects.deepEquals(this.height, other.height)
74
77
  && Objects.deepEquals(this.cropping, other.cropping)
75
78
  && Objects.deepEquals(this.includeBase64, other.includeBase64)
79
+ && Objects.deepEquals(this.preserveOriginal, other.preserveOriginal)
76
80
  && Objects.deepEquals(this.compressImageQuality, other.compressImageQuality)
77
81
  && Objects.deepEquals(this.compressImageMaxWidth, other.compressImageMaxWidth)
78
82
  && Objects.deepEquals(this.compressImageMaxHeight, other.compressImageMaxHeight)
@@ -92,6 +96,7 @@ data class ImageCropPickerOptions(
92
96
  height,
93
97
  cropping,
94
98
  includeBase64,
99
+ preserveOriginal,
95
100
  compressImageQuality,
96
101
  compressImageMaxWidth,
97
102
  compressImageMaxHeight,
@@ -114,8 +119,8 @@ data class ImageCropPickerOptions(
114
119
  @Keep
115
120
  @Suppress("unused")
116
121
  @JvmStatic
117
- private fun fromCpp(width: Double?, height: Double?, cropping: Boolean?, includeBase64: Boolean?, compressImageQuality: Double?, compressImageMaxWidth: Double?, compressImageMaxHeight: Double?, freeStyleCropEnabled: Boolean?, cropperCircleOverlay: Boolean?, cropperToolbarTitle: String?, cropperChooseText: String?, cropperCancelText: String?, cropperRotateButtonsHidden: Boolean?, showCropGuidelines: Boolean?, cropperAppearance: ImageCropperAppearance?): ImageCropPickerOptions {
118
- return ImageCropPickerOptions(width, height, cropping, includeBase64, compressImageQuality, compressImageMaxWidth, compressImageMaxHeight, freeStyleCropEnabled, cropperCircleOverlay, cropperToolbarTitle, cropperChooseText, cropperCancelText, cropperRotateButtonsHidden, showCropGuidelines, cropperAppearance)
122
+ private fun fromCpp(width: Double?, height: Double?, cropping: Boolean?, includeBase64: Boolean?, preserveOriginal: Boolean?, compressImageQuality: Double?, compressImageMaxWidth: Double?, compressImageMaxHeight: Double?, freeStyleCropEnabled: Boolean?, cropperCircleOverlay: Boolean?, cropperToolbarTitle: String?, cropperChooseText: String?, cropperCancelText: String?, cropperRotateButtonsHidden: Boolean?, showCropGuidelines: Boolean?, cropperAppearance: ImageCropperAppearance?): ImageCropPickerOptions {
123
+ return ImageCropPickerOptions(width, height, cropping, includeBase64, preserveOriginal, compressImageQuality, compressImageMaxWidth, compressImageMaxHeight, freeStyleCropEnabled, cropperCircleOverlay, cropperToolbarTitle, cropperChooseText, cropperCancelText, cropperRotateButtonsHidden, showCropGuidelines, cropperAppearance)
119
124
  }
120
125
  }
121
126
  }
@@ -18,7 +18,7 @@ public extension ImageCropPickerOptions {
18
18
  /**
19
19
  * Create a new instance of `ImageCropPickerOptions`.
20
20
  */
21
- init(width: Double?, height: Double?, cropping: Bool?, includeBase64: Bool?, compressImageQuality: Double?, compressImageMaxWidth: Double?, compressImageMaxHeight: Double?, freeStyleCropEnabled: Bool?, cropperCircleOverlay: Bool?, cropperToolbarTitle: String?, cropperChooseText: String?, cropperCancelText: String?, cropperRotateButtonsHidden: Bool?, showCropGuidelines: Bool?, cropperAppearance: ImageCropperAppearance?) {
21
+ init(width: Double?, height: Double?, cropping: Bool?, includeBase64: Bool?, preserveOriginal: Bool?, compressImageQuality: Double?, compressImageMaxWidth: Double?, compressImageMaxHeight: Double?, freeStyleCropEnabled: Bool?, cropperCircleOverlay: Bool?, cropperToolbarTitle: String?, cropperChooseText: String?, cropperCancelText: String?, cropperRotateButtonsHidden: Bool?, showCropGuidelines: Bool?, cropperAppearance: ImageCropperAppearance?) {
22
22
  self.init({ () -> bridge.std__optional_double_ in
23
23
  if let __unwrappedValue = width {
24
24
  return bridge.create_std__optional_double_(__unwrappedValue)
@@ -43,6 +43,12 @@ public extension ImageCropPickerOptions {
43
43
  } else {
44
44
  return .init()
45
45
  }
46
+ }(), { () -> bridge.std__optional_bool_ in
47
+ if let __unwrappedValue = preserveOriginal {
48
+ return bridge.create_std__optional_bool_(__unwrappedValue)
49
+ } else {
50
+ return .init()
51
+ }
46
52
  }(), { () -> bridge.std__optional_double_ in
47
53
  if let __unwrappedValue = compressImageQuality {
48
54
  return bridge.create_std__optional_double_(__unwrappedValue)
@@ -160,6 +166,18 @@ public extension ImageCropPickerOptions {
160
166
  }()
161
167
  }
162
168
 
169
+ @inline(__always)
170
+ var preserveOriginal: Bool? {
171
+ return { () -> Bool? in
172
+ if bridge.has_value_std__optional_bool_(self.__preserveOriginal) {
173
+ let __unwrapped = bridge.get_std__optional_bool_(self.__preserveOriginal)
174
+ return __unwrapped
175
+ } else {
176
+ return nil
177
+ }
178
+ }()
179
+ }
180
+
163
181
  @inline(__always)
164
182
  var compressImageQuality: Double? {
165
183
  return { () -> Double? in
@@ -46,6 +46,7 @@ namespace margelo::nitro::reactnativeimagecroppicker {
46
46
  std::optional<double> height SWIFT_PRIVATE;
47
47
  std::optional<bool> cropping SWIFT_PRIVATE;
48
48
  std::optional<bool> includeBase64 SWIFT_PRIVATE;
49
+ std::optional<bool> preserveOriginal SWIFT_PRIVATE;
49
50
  std::optional<double> compressImageQuality SWIFT_PRIVATE;
50
51
  std::optional<double> compressImageMaxWidth SWIFT_PRIVATE;
51
52
  std::optional<double> compressImageMaxHeight SWIFT_PRIVATE;
@@ -60,7 +61,7 @@ namespace margelo::nitro::reactnativeimagecroppicker {
60
61
 
61
62
  public:
62
63
  ImageCropPickerOptions() = default;
63
- explicit ImageCropPickerOptions(std::optional<double> width, std::optional<double> height, std::optional<bool> cropping, std::optional<bool> includeBase64, std::optional<double> compressImageQuality, std::optional<double> compressImageMaxWidth, std::optional<double> compressImageMaxHeight, std::optional<bool> freeStyleCropEnabled, std::optional<bool> cropperCircleOverlay, std::optional<std::string> cropperToolbarTitle, std::optional<std::string> cropperChooseText, std::optional<std::string> cropperCancelText, std::optional<bool> cropperRotateButtonsHidden, std::optional<bool> showCropGuidelines, std::optional<ImageCropperAppearance> cropperAppearance): width(width), height(height), cropping(cropping), includeBase64(includeBase64), compressImageQuality(compressImageQuality), compressImageMaxWidth(compressImageMaxWidth), compressImageMaxHeight(compressImageMaxHeight), freeStyleCropEnabled(freeStyleCropEnabled), cropperCircleOverlay(cropperCircleOverlay), cropperToolbarTitle(cropperToolbarTitle), cropperChooseText(cropperChooseText), cropperCancelText(cropperCancelText), cropperRotateButtonsHidden(cropperRotateButtonsHidden), showCropGuidelines(showCropGuidelines), cropperAppearance(cropperAppearance) {}
64
+ explicit ImageCropPickerOptions(std::optional<double> width, std::optional<double> height, std::optional<bool> cropping, std::optional<bool> includeBase64, std::optional<bool> preserveOriginal, std::optional<double> compressImageQuality, std::optional<double> compressImageMaxWidth, std::optional<double> compressImageMaxHeight, std::optional<bool> freeStyleCropEnabled, std::optional<bool> cropperCircleOverlay, std::optional<std::string> cropperToolbarTitle, std::optional<std::string> cropperChooseText, std::optional<std::string> cropperCancelText, std::optional<bool> cropperRotateButtonsHidden, std::optional<bool> showCropGuidelines, std::optional<ImageCropperAppearance> cropperAppearance): width(width), height(height), cropping(cropping), includeBase64(includeBase64), preserveOriginal(preserveOriginal), compressImageQuality(compressImageQuality), compressImageMaxWidth(compressImageMaxWidth), compressImageMaxHeight(compressImageMaxHeight), freeStyleCropEnabled(freeStyleCropEnabled), cropperCircleOverlay(cropperCircleOverlay), cropperToolbarTitle(cropperToolbarTitle), cropperChooseText(cropperChooseText), cropperCancelText(cropperCancelText), cropperRotateButtonsHidden(cropperRotateButtonsHidden), showCropGuidelines(showCropGuidelines), cropperAppearance(cropperAppearance) {}
64
65
 
65
66
  public:
66
67
  friend bool operator==(const ImageCropPickerOptions& lhs, const ImageCropPickerOptions& rhs) = default;
@@ -80,6 +81,7 @@ namespace margelo::nitro {
80
81
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height"))),
81
82
  JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cropping"))),
82
83
  JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "includeBase64"))),
84
+ JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "preserveOriginal"))),
83
85
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compressImageQuality"))),
84
86
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compressImageMaxWidth"))),
85
87
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compressImageMaxHeight"))),
@@ -99,6 +101,7 @@ namespace margelo::nitro {
99
101
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "height"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.height));
100
102
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "cropping"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.cropping));
101
103
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "includeBase64"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.includeBase64));
104
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "preserveOriginal"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.preserveOriginal));
102
105
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "compressImageQuality"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.compressImageQuality));
103
106
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "compressImageMaxWidth"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.compressImageMaxWidth));
104
107
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "compressImageMaxHeight"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.compressImageMaxHeight));
@@ -124,6 +127,7 @@ namespace margelo::nitro {
124
127
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))) return false;
125
128
  if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cropping")))) return false;
126
129
  if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "includeBase64")))) return false;
130
+ if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "preserveOriginal")))) return false;
127
131
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compressImageQuality")))) return false;
128
132
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compressImageMaxWidth")))) return false;
129
133
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "compressImageMaxHeight")))) return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-image-crop-picker",
3
- "version": "3.0.163",
3
+ "version": "3.0.165",
4
4
  "description": "Single photo picker and cropper Nitro module for OneKey, replacing react-native-image-crop-picker",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -134,7 +134,19 @@
134
134
  "modulePathIgnorePatterns": [
135
135
  "<rootDir>/example/node_modules",
136
136
  "<rootDir>/lib/"
137
- ]
137
+ ],
138
+ "transform": {
139
+ "^.+\\.[jt]sx?$": [
140
+ "babel-jest",
141
+ {
142
+ "presets": [
143
+ "module:@react-native/babel-preset"
144
+ ],
145
+ "babelrc": false,
146
+ "configFile": false
147
+ }
148
+ ]
149
+ }
138
150
  },
139
151
  "commitlint": {
140
152
  "extends": [
@@ -52,6 +52,8 @@ export interface ImageCropPickerOptions {
52
52
  height?: number;
53
53
  cropping?: boolean;
54
54
  includeBase64?: boolean;
55
+ // Copy the selected representation without re-encoding when not cropping.
56
+ preserveOriginal?: boolean;
55
57
  compressImageQuality?: number;
56
58
  compressImageMaxWidth?: number;
57
59
  compressImageMaxHeight?: number;
package/src/index.tsx CHANGED
@@ -74,7 +74,7 @@ function toImageCropPickerError(error: unknown): ImageCropPickerError {
74
74
  }
75
75
 
76
76
  // Options accepted for source compatibility with react-native-image-crop-picker.
77
- // Only single photos are supported, and results are always JPEG.
77
+ // Only single photos are supported; processed results are JPEG.
78
78
  export interface Options extends ImageCropPickerOptions {
79
79
  mediaType?: 'photo';
80
80
  multiple?: false;