react-native-picture-selector 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +627 -0
  2. package/android/CMakeLists.txt +30 -0
  3. package/android/build.gradle +79 -0
  4. package/android/proguard-rules.pro +21 -0
  5. package/android/src/main/AndroidManifest.xml +39 -0
  6. package/android/src/main/kotlin/com/margelo/pictureselector/GlideEngine.kt +80 -0
  7. package/android/src/main/kotlin/com/margelo/pictureselector/HybridPictureSelector.kt +138 -0
  8. package/android/src/main/kotlin/com/margelo/pictureselector/LubanCompressEngine.kt +58 -0
  9. package/android/src/main/kotlin/com/margelo/pictureselector/MediaAssetMapper.kt +69 -0
  10. package/android/src/main/kotlin/com/margelo/pictureselector/NitroPictureSelectorPackage.kt +52 -0
  11. package/android/src/main/kotlin/com/margelo/pictureselector/PictureSelectorOptionsMapper.kt +105 -0
  12. package/android/src/main/kotlin/com/margelo/pictureselector/UCropEngine.kt +57 -0
  13. package/android/src/main/res/xml/file_paths.xml +8 -0
  14. package/ios/HybridPictureSelector.swift +386 -0
  15. package/ios/NitroPictureSelector.podspec +39 -0
  16. package/lib/commonjs/PictureSelector.js +74 -0
  17. package/lib/commonjs/PictureSelector.js.map +1 -0
  18. package/lib/commonjs/index.js +39 -0
  19. package/lib/commonjs/index.js.map +1 -0
  20. package/lib/commonjs/package.json +1 -0
  21. package/lib/commonjs/specs/PictureSelector.nitro.js +34 -0
  22. package/lib/commonjs/specs/PictureSelector.nitro.js.map +1 -0
  23. package/lib/commonjs/types.js +44 -0
  24. package/lib/commonjs/types.js.map +1 -0
  25. package/lib/commonjs/usePictureSelector.js +122 -0
  26. package/lib/commonjs/usePictureSelector.js.map +1 -0
  27. package/lib/module/PictureSelector.js +71 -0
  28. package/lib/module/PictureSelector.js.map +1 -0
  29. package/lib/module/index.js +6 -0
  30. package/lib/module/index.js.map +1 -0
  31. package/lib/module/package.json +1 -0
  32. package/lib/module/specs/PictureSelector.nitro.js +36 -0
  33. package/lib/module/specs/PictureSelector.nitro.js.map +1 -0
  34. package/lib/module/types.js +29 -0
  35. package/lib/module/types.js.map +1 -0
  36. package/lib/module/usePictureSelector.js +119 -0
  37. package/lib/module/usePictureSelector.js.map +1 -0
  38. package/lib/typescript/PictureSelector.d.ts +23 -0
  39. package/lib/typescript/PictureSelector.d.ts.map +1 -0
  40. package/lib/typescript/index.d.ts +6 -0
  41. package/lib/typescript/index.d.ts.map +1 -0
  42. package/lib/typescript/specs/PictureSelector.nitro.d.ts +96 -0
  43. package/lib/typescript/specs/PictureSelector.nitro.d.ts.map +1 -0
  44. package/lib/typescript/types.d.ts +16 -0
  45. package/lib/typescript/types.d.ts.map +1 -0
  46. package/lib/typescript/usePictureSelector.d.ts +26 -0
  47. package/lib/typescript/usePictureSelector.d.ts.map +1 -0
  48. package/nitro.json +11 -0
  49. package/nitrogen/generated/.gitattributes +1 -0
  50. package/nitrogen/generated/android/NitroPictureSelector+autolinking.cmake +81 -0
  51. package/nitrogen/generated/android/NitroPictureSelector+autolinking.gradle +27 -0
  52. package/nitrogen/generated/android/NitroPictureSelectorOnLoad.cpp +41 -0
  53. package/nitrogen/generated/android/NitroPictureSelectorOnLoad.hpp +34 -0
  54. package/nitrogen/generated/android/c++/JCompressOptions.hpp +69 -0
  55. package/nitrogen/generated/android/c++/JCropOptions.hpp +73 -0
  56. package/nitrogen/generated/android/c++/JHybridHybridPictureSelectorSpec.cpp +125 -0
  57. package/nitrogen/generated/android/c++/JHybridHybridPictureSelectorSpec.hpp +64 -0
  58. package/nitrogen/generated/android/c++/JMediaAsset.hpp +98 -0
  59. package/nitrogen/generated/android/c++/JMediaType.hpp +61 -0
  60. package/nitrogen/generated/android/c++/JPickerTheme.hpp +64 -0
  61. package/nitrogen/generated/android/c++/JPictureSelectorOptions.hpp +121 -0
  62. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/CompressOptions.kt +47 -0
  63. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/CropOptions.kt +50 -0
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/HybridHybridPictureSelectorSpec.kt +59 -0
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/MediaAsset.kt +68 -0
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/MediaType.kt +24 -0
  67. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/NitroPictureSelectorOnLoad.kt +35 -0
  68. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/PickerTheme.kt +25 -0
  69. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/margelo/pictureselector/PictureSelectorOptions.kt +65 -0
  70. package/nitrogen/generated/ios/NitroPictureSelector+autolinking.rb +60 -0
  71. package/nitrogen/generated/ios/NitroPictureSelector-Swift-Cxx-Bridge.cpp +49 -0
  72. package/nitrogen/generated/ios/NitroPictureSelector-Swift-Cxx-Bridge.hpp +270 -0
  73. package/nitrogen/generated/ios/NitroPictureSelector-Swift-Cxx-Umbrella.hpp +65 -0
  74. package/nitrogen/generated/ios/c++/HybridHybridPictureSelectorSpecSwift.cpp +11 -0
  75. package/nitrogen/generated/ios/c++/HybridHybridPictureSelectorSpecSwift.hpp +110 -0
  76. package/nitrogen/generated/ios/swift/CompressOptions.swift +83 -0
  77. package/nitrogen/generated/ios/swift/CropOptions.swift +101 -0
  78. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  79. package/nitrogen/generated/ios/swift/Func_void_std__vector_MediaAsset_.swift +46 -0
  80. package/nitrogen/generated/ios/swift/HybridHybridPictureSelectorSpec.swift +56 -0
  81. package/nitrogen/generated/ios/swift/HybridHybridPictureSelectorSpec_cxx.swift +176 -0
  82. package/nitrogen/generated/ios/swift/MediaAsset.swift +118 -0
  83. package/nitrogen/generated/ios/swift/MediaType.swift +44 -0
  84. package/nitrogen/generated/ios/swift/PickerTheme.swift +48 -0
  85. package/nitrogen/generated/ios/swift/PictureSelectorOptions.swift +182 -0
  86. package/nitrogen/generated/shared/c++/CompressOptions.hpp +95 -0
  87. package/nitrogen/generated/shared/c++/CropOptions.hpp +99 -0
  88. package/nitrogen/generated/shared/c++/HybridHybridPictureSelectorSpec.cpp +22 -0
  89. package/nitrogen/generated/shared/c++/HybridHybridPictureSelectorSpec.hpp +69 -0
  90. package/nitrogen/generated/shared/c++/MediaAsset.hpp +124 -0
  91. package/nitrogen/generated/shared/c++/MediaType.hpp +80 -0
  92. package/nitrogen/generated/shared/c++/PickerTheme.hpp +84 -0
  93. package/nitrogen/generated/shared/c++/PictureSelectorOptions.hpp +132 -0
  94. package/package.json +76 -0
  95. package/src/PictureSelector.ts +72 -0
  96. package/src/index.ts +16 -0
  97. package/src/specs/PictureSelector.nitro.ts +121 -0
  98. package/src/types.ts +38 -0
  99. package/src/usePictureSelector.ts +102 -0
@@ -0,0 +1,57 @@
1
+ package com.margelo.pictureselector
2
+
3
+ import android.net.Uri
4
+ import androidx.fragment.app.Fragment
5
+ import com.luck.picture.lib.engine.CropFileEngine
6
+ import com.yalantis.ucrop.UCrop
7
+
8
+ /**
9
+ * uCrop integration for PictureSelector v3.
10
+ *
11
+ * PictureSelector calls [onStartCrop] when the user has selected an image
12
+ * and crop is configured. We forward to UCrop and it calls back into
13
+ * PictureSelector via the requestCode mechanism.
14
+ */
15
+ class UCropEngine(
16
+ private val freeStyle: Boolean,
17
+ private val ratioX: Float,
18
+ private val ratioY: Float,
19
+ private val circular: Boolean,
20
+ private val quality: Int,
21
+ ) : CropFileEngine {
22
+
23
+ override fun onStartCrop(
24
+ fragment: Fragment,
25
+ srcUri: Uri,
26
+ destinationUri: Uri,
27
+ dataSource: ArrayList<String>,
28
+ requestCode: Int,
29
+ ) {
30
+ val options = UCrop.Options().apply {
31
+ setCompressionQuality(quality)
32
+ setHideBottomControls(false)
33
+ setFreeStyleCropEnabled(freeStyle)
34
+ if (circular) {
35
+ setCircleDimmedLayer(true)
36
+ setShowCropFrame(false)
37
+ setShowCropGrid(false)
38
+ }
39
+ }
40
+
41
+ val uCrop = UCrop.of(srcUri, destinationUri)
42
+ .withOptions(options)
43
+
44
+ if (!freeStyle && !circular) {
45
+ uCrop.withAspectRatio(ratioX, ratioY)
46
+ }
47
+
48
+ try {
49
+ uCrop.start(fragment.requireContext(), fragment, requestCode)
50
+ } catch (e: Exception) {
51
+ throw PictureSelectorException(
52
+ "UNKNOWN",
53
+ "UCrop failed to start: ${e.message}"
54
+ )
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <paths>
3
+ <external-path name="external" path="." />
4
+ <external-cache-path name="external_cache" path="." />
5
+ <cache-path name="cache" path="." />
6
+ <files-path name="files" path="." />
7
+ <external-files-path name="external_files" path="." />
8
+ </paths>
@@ -0,0 +1,386 @@
1
+ import Foundation
2
+ import UIKit
3
+ import HXPhotoPicker
4
+ import NitroModules
5
+
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+ // HybridPictureSelector
8
+ //
9
+ // Main iOS implementation. Inherits from the nitrogen-generated
10
+ // HybridHybridPictureSelectorSpec_base and conforms to
11
+ // HybridHybridPictureSelectorSpec_protocol (together they form the
12
+ // HybridHybridPictureSelectorSpec typealias).
13
+ //
14
+ // Threading contract:
15
+ // - Nitro calls openPicker / openCamera on the JS thread.
16
+ // - All UIKit calls are dispatched to DispatchQueue.main.
17
+ // - Async result mapping runs in a Swift Task (cooperative thread pool).
18
+ //
19
+ // API REQUIRES VERIFICATION:
20
+ // - PhotoPickerControllerDelegate method signatures in HXPhotoPicker v5.0.5.
21
+ // - PhotoAsset.getURL(compression:result:) callback API availability.
22
+ // - PickerResult.photoAssets field name.
23
+ // - PhotoAsset.mediaType enum values (.photo / .video).
24
+ // - PhotoAsset.imageSize property name.
25
+ // - PhotoAsset.videoDuration unit (seconds vs ms).
26
+ // - AssetURLResult.fileSize field name / optionality.
27
+ // - PhotoAsset.Compression type name and initialiser parameters.
28
+ // - EditorConfiguration.Photo.CropSize.isRoundCrop property name.
29
+ // ─────────────────────────────────────────────────────────────────────────────
30
+
31
+ final class HybridPictureSelector: HybridHybridPictureSelectorSpec_base, HybridHybridPictureSelectorSpec_protocol {
32
+
33
+ // MARK: - Private state
34
+
35
+ /// Bundles the pending resolver together with the options so the delegate
36
+ /// can perform compression-aware result mapping.
37
+ private struct PendingSession {
38
+ let resolver: (Result<[MediaAsset], Error>) -> Void
39
+ let options: PictureSelectorOptions
40
+ }
41
+
42
+ private var session: PendingSession?
43
+
44
+ /// Strong reference prevents picker deallocation before delegate fires.
45
+ private var activePicker: UIViewController?
46
+
47
+ // MARK: - openPicker
48
+
49
+ func openPicker(options: PictureSelectorOptions) -> Promise<[MediaAsset]> {
50
+ return Promise { [weak self] resolver in
51
+ guard let self = self else {
52
+ resolver.reject(PictureSelectorError.unknown("Instance deallocated"))
53
+ return
54
+ }
55
+
56
+ DispatchQueue.main.async {
57
+ guard let topVC = self.topViewController() else {
58
+ resolver.reject(PictureSelectorError.unknown(
59
+ "No active UIViewController. Ensure the picker is called from a mounted component."
60
+ ))
61
+ return
62
+ }
63
+
64
+ self.session = PendingSession(
65
+ resolver: { result in
66
+ switch result {
67
+ case .success(let assets): resolver.resolve(assets)
68
+ case .failure(let err): resolver.reject(err)
69
+ }
70
+ },
71
+ options: options
72
+ )
73
+
74
+ let config = self.buildPickerConfig(from: options)
75
+ let picker = PhotoPickerController(picker: config)
76
+ picker.pickerDelegate = self
77
+
78
+ self.activePicker = picker
79
+ topVC.present(picker, animated: true)
80
+ }
81
+ }
82
+ }
83
+
84
+ // MARK: - openCamera
85
+
86
+ func openCamera(options: PictureSelectorOptions) -> Promise<[MediaAsset]> {
87
+ return Promise { [weak self] resolver in
88
+ guard let self = self else {
89
+ resolver.reject(PictureSelectorError.unknown("Instance deallocated"))
90
+ return
91
+ }
92
+
93
+ DispatchQueue.main.async {
94
+ guard let topVC = self.topViewController() else {
95
+ resolver.reject(PictureSelectorError.unknown("No active UIViewController."))
96
+ return
97
+ }
98
+
99
+ var cameraConfig = CameraConfiguration()
100
+ switch options.mediaType {
101
+ case "video": cameraConfig.mediaType = .video
102
+ default: cameraConfig.mediaType = .photo
103
+ }
104
+ if let maxDur = options.maxVideoDuration {
105
+ cameraConfig.videoMaximumDuration = maxDur
106
+ }
107
+
108
+ // API REQUIRES VERIFICATION:
109
+ // Photo.camera(_:fromViewController:completion:cancel:) method signature.
110
+ // If this overload doesn't exist, use CameraController directly:
111
+ // let cam = CameraController(config: cameraConfig)
112
+ // cam.onCompletion = { ... }
113
+ // topVC.present(cam, animated: true)
114
+ Photo.camera(
115
+ cameraConfig,
116
+ fromViewController: topVC
117
+ ) { [weak self] result, _ in
118
+ guard let self = self else { return }
119
+ guard let photoAsset = result?.photoAsset else {
120
+ resolver.resolve([])
121
+ return
122
+ }
123
+ Task {
124
+ do {
125
+ let asset = try await self.mapAsset(
126
+ photoAsset,
127
+ compress: options.compress,
128
+ isOriginal: false
129
+ )
130
+ resolver.resolve([asset])
131
+ } catch {
132
+ resolver.reject(error)
133
+ }
134
+ }
135
+ } cancel: { _ in
136
+ resolver.reject(PictureSelectorError.cancelled)
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ // MARK: - Config builder
143
+
144
+ private func buildPickerConfig(from options: PictureSelectorOptions) -> PickerConfiguration {
145
+ var config = PickerConfiguration()
146
+
147
+ // Media type
148
+ switch options.mediaType {
149
+ case "video":
150
+ config.selectOptions = [.video]
151
+ case "all":
152
+ config.selectOptions = [.photo, .video]
153
+ default:
154
+ config.selectOptions = [.photo]
155
+ }
156
+
157
+ // Selection limit
158
+ config.maximumSelectedCount = Int(options.maxCount ?? 1)
159
+
160
+ // In-picker camera button
161
+ config.allowCustomCamera = options.enableCamera ?? true
162
+
163
+ // Video duration limits
164
+ if let maxDur = options.maxVideoDuration {
165
+ config.maximumSelectedVideoDuration = maxDur
166
+ }
167
+ if let minDur = options.minVideoDuration {
168
+ config.minimumSelectedVideoDuration = minDur
169
+ }
170
+
171
+ // Editor / crop (only when maxCount == 1)
172
+ let maxCount = Int(options.maxCount ?? 1)
173
+ if let crop = options.crop, crop.enabled, maxCount == 1 {
174
+ config.editorOptions = [.photo]
175
+ var editorConfig = EditorConfiguration()
176
+
177
+ var cropSizeConfig = EditorConfiguration.Photo.CropSize()
178
+ if crop.circular == true {
179
+ // API REQUIRES VERIFICATION: isRoundCrop property name in v5.0.5
180
+ cropSizeConfig.isRoundCrop = true
181
+ } else if crop.freeStyle == true {
182
+ cropSizeConfig.aspectRatios = [] // empty array = free style
183
+ } else {
184
+ let x = crop.ratioX ?? 1.0
185
+ let y = crop.ratioY ?? 1.0
186
+ cropSizeConfig.aspectRatios = [
187
+ .init(title: "", ratio: .init(width: x, height: y))
188
+ ]
189
+ }
190
+ editorConfig.photo.cropSize = cropSizeConfig
191
+ config.editor = editorConfig
192
+ }
193
+
194
+ // Theme color
195
+ if let hex = options.themeColor, let color = UIColor(hex: hex) {
196
+ config.themeColor = color
197
+ }
198
+
199
+ return config
200
+ }
201
+
202
+ // MARK: - Result mapping
203
+
204
+ private func mapResults(
205
+ _ result: PickerResult,
206
+ compress: CompressOptions?
207
+ ) async throws -> [MediaAsset] {
208
+ var mapped: [MediaAsset] = []
209
+ for photoAsset in result.photoAssets {
210
+ let asset = try await mapAsset(
211
+ photoAsset,
212
+ compress: compress,
213
+ isOriginal: result.isOriginal
214
+ )
215
+ mapped.append(asset)
216
+ }
217
+ return mapped
218
+ }
219
+
220
+ private func mapAsset(
221
+ _ photoAsset: PhotoAsset,
222
+ compress: CompressOptions?,
223
+ isOriginal: Bool
224
+ ) async throws -> MediaAsset {
225
+ // Obtain file URL via callback, bridged to async/await.
226
+ // API REQUIRES VERIFICATION: getURL(compression:result:) availability in v5.0.5.
227
+ let urlResult: AssetURLResult = try await withCheckedThrowingContinuation { cont in
228
+ photoAsset.getURL(
229
+ compression: buildCompression(from: compress)
230
+ ) { result in
231
+ switch result {
232
+ case .success(let r): cont.resume(returning: r)
233
+ case .failure(let e): cont.resume(throwing: e)
234
+ }
235
+ }
236
+ }
237
+
238
+ // Determine if the user applied edits
239
+ let wasEdited = photoAsset.editedResult != nil
240
+ let finalUri = urlResult.url.absoluteString
241
+ let editedUri: String? = wasEdited ? finalUri : nil
242
+
243
+ // Image / video dimensions
244
+ // API REQUIRES VERIFICATION: imageSize property name in v5.0.5
245
+ let size: CGSize = photoAsset.imageSize
246
+
247
+ // Duration: HXPhotoPicker returns seconds; bridge spec expects ms.
248
+ // API REQUIRES VERIFICATION: videoDuration property name and unit.
249
+ let durationMs: Double = (photoAsset.videoDuration ?? 0) * 1_000
250
+
251
+ // File size
252
+ // API REQUIRES VERIFICATION: AssetURLResult.fileSize field name.
253
+ let fileSize: Double = Double(urlResult.fileSize ?? 0)
254
+
255
+ let typeStr: String = (photoAsset.mediaType == .video) ? "video" : "image"
256
+
257
+ return MediaAsset(
258
+ uri: finalUri,
259
+ type: typeStr,
260
+ mimeType: mimeType(for: urlResult.url),
261
+ width: Double(size.width),
262
+ height: Double(size.height),
263
+ duration: durationMs,
264
+ fileName: urlResult.url.lastPathComponent,
265
+ fileSize: fileSize,
266
+ editedUri: editedUri,
267
+ isOriginal: isOriginal,
268
+ bucketName: nil
269
+ )
270
+ }
271
+
272
+ // MARK: - Compression helper
273
+
274
+ /// API REQUIRES VERIFICATION: PhotoAsset.Compression type name and init params in v5.0.5.
275
+ private func buildCompression(from options: CompressOptions?) -> PhotoAsset.Compression? {
276
+ guard let opts = options, opts.enabled else { return nil }
277
+ return PhotoAsset.Compression(
278
+ imageCompressionQuality: opts.quality ?? 0.8
279
+ )
280
+ }
281
+
282
+ // MARK: - Top view controller
283
+
284
+ private func topViewController() -> UIViewController? {
285
+ let windowScene = UIApplication.shared.connectedScenes
286
+ .filter { $0.activationState == .foregroundActive }
287
+ .compactMap { $0 as? UIWindowScene }
288
+ .first
289
+
290
+ guard let window = windowScene?.windows.first(where: { $0.isKeyWindow }) else {
291
+ return nil
292
+ }
293
+
294
+ var top: UIViewController? = window.rootViewController
295
+ while let presented = top?.presentedViewController {
296
+ top = presented
297
+ }
298
+ return top
299
+ }
300
+
301
+ // MARK: - MIME type helper
302
+
303
+ private func mimeType(for url: URL) -> String {
304
+ switch url.pathExtension.lowercased() {
305
+ case "jpg", "jpeg": return "image/jpeg"
306
+ case "png": return "image/png"
307
+ case "gif": return "image/gif"
308
+ case "heic": return "image/heic"
309
+ case "heif": return "image/heif"
310
+ case "webp": return "image/webp"
311
+ case "mp4": return "video/mp4"
312
+ case "mov": return "video/quicktime"
313
+ case "m4v": return "video/x-m4v"
314
+ default: return "application/octet-stream"
315
+ }
316
+ }
317
+ }
318
+
319
+ // MARK: - PhotoPickerControllerDelegate
320
+
321
+ extension HybridPictureSelector: PhotoPickerControllerDelegate {
322
+
323
+ func pickerController(
324
+ _ pickerController: PhotoPickerController,
325
+ didFinishSelection result: PickerResult
326
+ ) {
327
+ // Capture and clear session atomically before dismiss completes
328
+ let captured = session
329
+ session = nil
330
+ activePicker = nil
331
+
332
+ pickerController.dismiss(animated: true) { [weak self] in
333
+ guard let self = self, let s = captured else { return }
334
+ Task {
335
+ do {
336
+ let assets = try await self.mapResults(result, compress: s.options.compress)
337
+ s.resolver(.success(assets))
338
+ } catch {
339
+ s.resolver(.failure(error))
340
+ }
341
+ }
342
+ }
343
+ }
344
+
345
+ func pickerController(didCancel pickerController: PhotoPickerController) {
346
+ let captured = session
347
+ session = nil
348
+ activePicker = nil
349
+
350
+ pickerController.dismiss(animated: true) {
351
+ captured?.resolver(.failure(PictureSelectorError.cancelled))
352
+ }
353
+ }
354
+ }
355
+
356
+ // MARK: - Error type
357
+
358
+ enum PictureSelectorError: Error, LocalizedError {
359
+ case cancelled
360
+ case permissionDenied
361
+ case unknown(String)
362
+
363
+ var errorDescription: String? {
364
+ switch self {
365
+ case .cancelled: return "CANCELLED"
366
+ case .permissionDenied: return "PERMISSION_DENIED"
367
+ case .unknown(let msg): return "UNKNOWN: \(msg)"
368
+ }
369
+ }
370
+ }
371
+
372
+ // MARK: - UIColor hex initialiser
373
+
374
+ extension UIColor {
375
+ convenience init?(hex: String) {
376
+ var s = hex.trimmingCharacters(in: .whitespacesAndNewlines)
377
+ if s.hasPrefix("#") { s = String(s.dropFirst()) }
378
+ guard s.count == 6, let rgb = UInt64(s, radix: 16) else { return nil }
379
+ self.init(
380
+ red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0,
381
+ green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0,
382
+ blue: CGFloat( rgb & 0x0000FF ) / 255.0,
383
+ alpha: 1.0
384
+ )
385
+ }
386
+ }
@@ -0,0 +1,39 @@
1
+ require "json"
2
+ package = JSON.parse(File.read(File.join(__dir__, "..", "package.json")))
3
+
4
+ Pod::Spec.new do |s|
5
+ s.name = "NitroPictureSelector"
6
+ s.version = package["version"]
7
+ s.summary = package["description"]
8
+ s.homepage = package["repository"]["url"]
9
+ s.license = { :type => "MIT" }
10
+ s.author = "react-native-picture-selector contributors"
11
+
12
+ s.platforms = { :ios => "13.0" }
13
+ s.swift_version = "5.9"
14
+ s.requires_arc = true
15
+
16
+ # ── Sources ─────────────────────────────────────────────────────────────
17
+ # Include both our hand-written Swift and the nitrogen-generated bridge files.
18
+ s.source_files = [
19
+ "ios/**/*.{h,m,mm,swift}",
20
+ "../nitrogen/generated/ios/**/*.{h,m,mm,swift,hpp,cpp}",
21
+ ]
22
+
23
+ # ── Dependencies ─────────────────────────────────────────────────────────
24
+ s.dependency "React-Core"
25
+ s.dependency "react-native-nitro-modules"
26
+
27
+ # HXPhotoPicker v5 — default subspec includes Picker + Editor + Camera
28
+ s.dependency "HXPhotoPicker", "~> 5.0.5"
29
+
30
+ # ── Compiler flags ───────────────────────────────────────────────────────
31
+ # Enable HXPhotoPicker conditional compilation flags
32
+ # Enable Swift/C++ interop required by Nitro for zero-overhead calls
33
+ s.pod_target_xcconfig = {
34
+ "SWIFT_ACTIVE_COMPILATION_CONDITIONS" =>
35
+ "HXPICKER_ENABLE_CORE HXPICKER_ENABLE_PICKER HXPICKER_ENABLE_EDITOR HXPICKER_ENABLE_CAMERA",
36
+ "OTHER_SWIFT_FLAGS" => "-enable-experimental-cxx-interop",
37
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
38
+ }
39
+ end
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.PictureSelector = void 0;
7
+ var _reactNativeNitroModules = require("react-native-nitro-modules");
8
+ var _types = require("./types");
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ // Lazy singleton — created once and reused across calls
11
+ // ─────────────────────────────────────────────────────────────────────────────
12
+
13
+ let _native = null;
14
+ function getNative() {
15
+ if (_native == null) {
16
+ _native = _reactNativeNitroModules.NitroModules.createHybridObject('PictureSelector');
17
+ }
18
+ return _native;
19
+ }
20
+
21
+ // ─────────────────────────────────────────────────────────────────────────────
22
+ // Default options
23
+ // ─────────────────────────────────────────────────────────────────────────────
24
+
25
+ const defaultOptions = {
26
+ mediaType: _types.MediaType.IMAGE,
27
+ maxCount: 1,
28
+ enableCamera: true
29
+ };
30
+
31
+ // ─────────────────────────────────────────────────────────────────────────────
32
+ // Static API
33
+ // ─────────────────────────────────────────────────────────────────────────────
34
+
35
+ const PictureSelector = exports.PictureSelector = {
36
+ /**
37
+ * Open the gallery picker.
38
+ *
39
+ * @example
40
+ * const assets = await PictureSelector.openPicker({ maxCount: 9 })
41
+ *
42
+ * @throws PickerError with code CANCELLED when the user dismisses
43
+ * @throws PickerError with code PERMISSION_DENIED on permission failure
44
+ */
45
+ async openPicker(options = {}) {
46
+ try {
47
+ return await getNative().openPicker({
48
+ ...defaultOptions,
49
+ ...options
50
+ });
51
+ } catch (err) {
52
+ throw (0, _types.toPickerError)(err);
53
+ }
54
+ },
55
+ /**
56
+ * Open the camera for capture.
57
+ *
58
+ * @example
59
+ * const [asset] = await PictureSelector.openCamera({ mediaType: MediaType.VIDEO })
60
+ *
61
+ * @throws PickerError with code CANCELLED when the user dismisses
62
+ */
63
+ async openCamera(options = {}) {
64
+ try {
65
+ return await getNative().openCamera({
66
+ ...defaultOptions,
67
+ ...options
68
+ });
69
+ } catch (err) {
70
+ throw (0, _types.toPickerError)(err);
71
+ }
72
+ }
73
+ };
74
+ //# sourceMappingURL=PictureSelector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNativeNitroModules","require","_types","_native","getNative","NitroModules","createHybridObject","defaultOptions","mediaType","MediaType","IMAGE","maxCount","enableCamera","PictureSelector","exports","openPicker","options","err","toPickerError","openCamera"],"sourceRoot":"..\\..\\src","sources":["PictureSelector.ts"],"mappings":";;;;;;AAAA,IAAAA,wBAAA,GAAAC,OAAA;AAGA,IAAAC,MAAA,GAAAD,OAAA;AAEA;AACA;AACA;;AAEA,IAAIE,OAAqC,GAAG,IAAI;AAEhD,SAASC,SAASA,CAAA,EAA0B;EAC1C,IAAID,OAAO,IAAI,IAAI,EAAE;IACnBA,OAAO,GAAGE,qCAAY,CAACC,kBAAkB,CACvC,iBACF,CAAC;EACH;EACA,OAAOH,OAAO;AAChB;;AAEA;AACA;AACA;;AAEA,MAAMI,cAAsC,GAAG;EAC7CC,SAAS,EAAEC,gBAAS,CAACC,KAAK;EAC1BC,QAAQ,EAAE,CAAC;EACXC,YAAY,EAAE;AAChB,CAAC;;AAED;AACA;AACA;;AAEO,MAAMC,eAAe,GAAAC,OAAA,CAAAD,eAAA,GAAG;EAC7B;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACE,MAAME,UAAUA,CACdC,OAA+B,GAAG,CAAC,CAAC,EACb;IACvB,IAAI;MACF,OAAO,MAAMZ,SAAS,CAAC,CAAC,CAACW,UAAU,CAAC;QAAE,GAAGR,cAAc;QAAE,GAAGS;MAAQ,CAAC,CAAC;IACxE,CAAC,CAAC,OAAOC,GAAG,EAAE;MACZ,MAAM,IAAAC,oBAAa,EAACD,GAAG,CAAC;IAC1B;EACF,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACE,MAAME,UAAUA,CACdH,OAA+B,GAAG,CAAC,CAAC,EACb;IACvB,IAAI;MACF,OAAO,MAAMZ,SAAS,CAAC,CAAC,CAACe,UAAU,CAAC;QAAE,GAAGZ,cAAc;QAAE,GAAGS;MAAQ,CAAC,CAAC;IACxE,CAAC,CAAC,OAAOC,GAAG,EAAE;MACZ,MAAM,IAAAC,oBAAa,EAACD,GAAG,CAAC;IAC1B;EACF;AACF,CAAC","ignoreList":[]}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "MediaType", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _types.MediaType;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "PickerTheme", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _types.PickerTheme;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "PictureSelector", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _PictureSelector.PictureSelector;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "toPickerError", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _types.toPickerError;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "usePictureSelector", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _usePictureSelector.usePictureSelector;
34
+ }
35
+ });
36
+ var _PictureSelector = require("./PictureSelector");
37
+ var _usePictureSelector = require("./usePictureSelector");
38
+ var _types = require("./types");
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_PictureSelector","require","_usePictureSelector","_types"],"sourceRoot":"..\\..\\src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,gBAAA,GAAAC,OAAA;AACA,IAAAC,mBAAA,GAAAD,OAAA;AAcA,IAAAE,MAAA,GAAAF,OAAA","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.PickerTheme = exports.MediaType = void 0;
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+ // Enums
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ let MediaType = exports.MediaType = /*#__PURE__*/function (MediaType) {
11
+ MediaType["IMAGE"] = "image";
12
+ MediaType["VIDEO"] = "video";
13
+ MediaType["ALL"] = "all";
14
+ return MediaType;
15
+ }({});
16
+ let PickerTheme = exports.PickerTheme = /*#__PURE__*/function (PickerTheme) {
17
+ PickerTheme["DEFAULT"] = "default";
18
+ PickerTheme["WECHAT"] = "wechat";
19
+ PickerTheme["WHITE"] = "white";
20
+ PickerTheme["DARK"] = "dark";
21
+ return PickerTheme;
22
+ }({}); // ─────────────────────────────────────────────────────────────────────────────
23
+ // Config sub-structures
24
+ // ─────────────────────────────────────────────────────────────────────────────
25
+ // ─────────────────────────────────────────────────────────────────────────────
26
+ // Picker options
27
+ // ─────────────────────────────────────────────────────────────────────────────
28
+ // ─────────────────────────────────────────────────────────────────────────────
29
+ // Result structure
30
+ // ─────────────────────────────────────────────────────────────────────────────
31
+ // ─────────────────────────────────────────────────────────────────────────────
32
+ // HybridObject — the main bridge
33
+ // ─────────────────────────────────────────────────────────────────────────────
34
+ //# sourceMappingURL=PictureSelector.nitro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["MediaType","exports","PickerTheme"],"sourceRoot":"..\\..\\..\\src","sources":["specs/PictureSelector.nitro.ts"],"mappings":";;;;;;AAEA;AACA;AACA;AAAA,IAEYA,SAAS,GAAAC,OAAA,CAAAD,SAAA,0BAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAATA,SAAS;EAAA,OAATA,SAAS;AAAA;AAAA,IAMTE,WAAW,GAAAD,OAAA,CAAAC,WAAA,0BAAXA,WAAW;EAAXA,WAAW;EAAXA,WAAW;EAAXA,WAAW;EAAXA,WAAW;EAAA,OAAXA,WAAW;AAAA,OAOvB;AACA;AACA;AA0BA;AACA;AACA;AAyBA;AACA;AACA;AA2BA;AACA;AACA","ignoreList":[]}