@developer_tribe/react-native-comnyx 0.13.13 → 0.15.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 (132) hide show
  1. package/android/generated/RCTAppDependencyProvider.h +25 -0
  2. package/android/generated/RCTAppDependencyProvider.mm +55 -0
  3. package/android/generated/RCTModulesConformingToProtocolsProvider.h +18 -0
  4. package/android/generated/RCTModulesConformingToProtocolsProvider.mm +33 -0
  5. package/android/generated/RCTThirdPartyComponentsProvider.h +16 -0
  6. package/android/generated/RCTThirdPartyComponentsProvider.mm +23 -0
  7. package/android/generated/ReactAppDependencyProvider.podspec +34 -0
  8. package/android/generated/jni/CMakeLists.txt +36 -0
  9. package/android/generated/jni/RNComnyxSpec-generated.cpp +22 -0
  10. package/android/generated/jni/RNComnyxSpec.h +24 -0
  11. package/android/generated/jni/react/renderer/components/RNComnyxSpec/RNComnyxSpecJSI-generated.cpp +17 -0
  12. package/android/generated/jni/react/renderer/components/RNComnyxSpec/RNComnyxSpecJSI.h +19 -0
  13. package/android/src/main/AndroidManifest.xml +15 -0
  14. package/android/src/main/AndroidManifestNew.xml +4 -0
  15. package/android/src/main/java/com/comnyx/ComnyxMediaPickerModule.kt +347 -0
  16. package/android/src/main/java/com/comnyx/ComnyxPackage.kt +1 -1
  17. package/android/src/main/java/com/comnyx/VideoPlayerActivity.kt +91 -0
  18. package/ios/ComnyxMediaPicker.m +29 -0
  19. package/ios/ComnyxMediaPicker.swift +436 -0
  20. package/lib/commonjs/NativeComnyxMediaPicker.js +83 -0
  21. package/lib/commonjs/NativeComnyxMediaPicker.js.map +1 -0
  22. package/lib/commonjs/api/index.js +19 -0
  23. package/lib/commonjs/api/index.js.map +1 -1
  24. package/lib/commonjs/api/media.js +76 -0
  25. package/lib/commonjs/api/media.js.map +1 -0
  26. package/lib/commonjs/assets/attachment-01.png +0 -0
  27. package/lib/commonjs/assets/gallery.png +0 -0
  28. package/lib/commonjs/assets/video-play.png +0 -0
  29. package/lib/commonjs/assets/x-circle.png +0 -0
  30. package/lib/commonjs/components/ChatList.js +48 -22
  31. package/lib/commonjs/components/ChatList.js.map +1 -1
  32. package/lib/commonjs/components/LinkifyText.js +5 -1
  33. package/lib/commonjs/components/LinkifyText.js.map +1 -1
  34. package/lib/commonjs/components/MediaMessageItem.js +333 -0
  35. package/lib/commonjs/components/MediaMessageItem.js.map +1 -0
  36. package/lib/commonjs/components/MediaPickerButton.js +244 -0
  37. package/lib/commonjs/components/MediaPickerButton.js.map +1 -0
  38. package/lib/commonjs/components/MediaViewerModal.js +164 -0
  39. package/lib/commonjs/components/MediaViewerModal.js.map +1 -0
  40. package/lib/commonjs/components/MessageInput.js +344 -73
  41. package/lib/commonjs/components/MessageInput.js.map +1 -1
  42. package/lib/commonjs/components/MessageItem.js +17 -8
  43. package/lib/commonjs/components/MessageItem.js.map +1 -1
  44. package/lib/commonjs/constants/translations.js +174 -29
  45. package/lib/commonjs/constants/translations.js.map +1 -1
  46. package/lib/commonjs/data/fake/media.js +105 -0
  47. package/lib/commonjs/data/fake/media.js.map +1 -0
  48. package/lib/commonjs/types/MediaTypes.js +2 -0
  49. package/lib/commonjs/types/MediaTypes.js.map +1 -0
  50. package/lib/commonjs/version.js +1 -1
  51. package/lib/commonjs/version.js.map +1 -1
  52. package/lib/module/NativeComnyxMediaPicker.js +73 -0
  53. package/lib/module/NativeComnyxMediaPicker.js.map +1 -0
  54. package/lib/module/api/index.js +1 -0
  55. package/lib/module/api/index.js.map +1 -1
  56. package/lib/module/api/media.js +70 -0
  57. package/lib/module/api/media.js.map +1 -0
  58. package/lib/module/assets/attachment-01.png +0 -0
  59. package/lib/module/assets/gallery.png +0 -0
  60. package/lib/module/assets/video-play.png +0 -0
  61. package/lib/module/assets/x-circle.png +0 -0
  62. package/lib/module/components/ChatList.js +48 -22
  63. package/lib/module/components/ChatList.js.map +1 -1
  64. package/lib/module/components/LinkifyText.js +5 -1
  65. package/lib/module/components/LinkifyText.js.map +1 -1
  66. package/lib/module/components/MediaMessageItem.js +330 -0
  67. package/lib/module/components/MediaMessageItem.js.map +1 -0
  68. package/lib/module/components/MediaPickerButton.js +240 -0
  69. package/lib/module/components/MediaPickerButton.js.map +1 -0
  70. package/lib/module/components/MediaViewerModal.js +160 -0
  71. package/lib/module/components/MediaViewerModal.js.map +1 -0
  72. package/lib/module/components/MessageInput.js +347 -75
  73. package/lib/module/components/MessageInput.js.map +1 -1
  74. package/lib/module/components/MessageItem.js +17 -8
  75. package/lib/module/components/MessageItem.js.map +1 -1
  76. package/lib/module/constants/translations.js +174 -29
  77. package/lib/module/constants/translations.js.map +1 -1
  78. package/lib/module/data/fake/media.js +99 -0
  79. package/lib/module/data/fake/media.js.map +1 -0
  80. package/lib/module/types/MediaTypes.js +2 -0
  81. package/lib/module/types/MediaTypes.js.map +1 -0
  82. package/lib/module/version.js +1 -1
  83. package/lib/module/version.js.map +1 -1
  84. package/lib/typescript/src/NativeComnyxMediaPicker.d.ts +9 -0
  85. package/lib/typescript/src/NativeComnyxMediaPicker.d.ts.map +1 -0
  86. package/lib/typescript/src/api/index.d.ts +1 -0
  87. package/lib/typescript/src/api/index.d.ts.map +1 -1
  88. package/lib/typescript/src/api/media.d.ts +7 -0
  89. package/lib/typescript/src/api/media.d.ts.map +1 -0
  90. package/lib/typescript/src/components/ChatList.d.ts.map +1 -1
  91. package/lib/typescript/src/components/LinkifyText.d.ts.map +1 -1
  92. package/lib/typescript/src/components/MediaMessageItem.d.ts +6 -0
  93. package/lib/typescript/src/components/MediaMessageItem.d.ts.map +1 -0
  94. package/lib/typescript/src/components/MediaPickerButton.d.ts +5 -0
  95. package/lib/typescript/src/components/MediaPickerButton.d.ts.map +1 -0
  96. package/lib/typescript/src/components/MediaViewerModal.d.ts +8 -0
  97. package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -0
  98. package/lib/typescript/src/components/MessageInput.d.ts.map +1 -1
  99. package/lib/typescript/src/components/MessageItem.d.ts.map +1 -1
  100. package/lib/typescript/src/constants/translations.d.ts.map +1 -1
  101. package/lib/typescript/src/data/fake/media.d.ts +6 -0
  102. package/lib/typescript/src/data/fake/media.d.ts.map +1 -0
  103. package/lib/typescript/src/types/Conversation.d.ts +19 -0
  104. package/lib/typescript/src/types/Conversation.d.ts.map +1 -1
  105. package/lib/typescript/src/types/LocalizationKeys.d.ts +5 -0
  106. package/lib/typescript/src/types/LocalizationKeys.d.ts.map +1 -1
  107. package/lib/typescript/src/types/MediaTypes.d.ts +26 -0
  108. package/lib/typescript/src/types/MediaTypes.d.ts.map +1 -0
  109. package/lib/typescript/src/version.d.ts +1 -1
  110. package/lib/typescript/src/version.d.ts.map +1 -1
  111. package/package.json +1 -1
  112. package/src/NativeComnyxMediaPicker.ts +83 -0
  113. package/src/api/index.ts +1 -0
  114. package/src/api/media.ts +116 -0
  115. package/src/assets/attachment-01.png +0 -0
  116. package/src/assets/gallery.png +0 -0
  117. package/src/assets/video-play.png +0 -0
  118. package/src/assets/x-circle.png +0 -0
  119. package/src/components/ChatList.tsx +81 -24
  120. package/src/components/CustomerForm.tsx +1 -1
  121. package/src/components/LinkifyText.tsx +3 -2
  122. package/src/components/MediaMessageItem.tsx +390 -0
  123. package/src/components/MediaPickerButton.tsx +269 -0
  124. package/src/components/MediaViewerModal.tsx +168 -0
  125. package/src/components/MessageInput.tsx +396 -84
  126. package/src/components/MessageItem.tsx +19 -5
  127. package/src/constants/translations.ts +145 -0
  128. package/src/data/fake/media.ts +110 -0
  129. package/src/types/Conversation.ts +20 -0
  130. package/src/types/LocalizationKeys.ts +5 -0
  131. package/src/types/MediaTypes.ts +27 -0
  132. package/src/version.ts +1 -1
@@ -0,0 +1,436 @@
1
+ import Foundation
2
+ import UIKit
3
+ import PhotosUI
4
+ import AVFoundation
5
+ import AVKit
6
+ import React
7
+
8
+ @objc(ComnyxMediaPicker)
9
+ class ComnyxMediaPicker: NSObject, RCTBridgeModule {
10
+
11
+ static func moduleName() -> String! {
12
+ return "ComnyxMediaPicker"
13
+ }
14
+
15
+ static func requiresMainQueueSetup() -> Bool {
16
+ return true
17
+ }
18
+
19
+ private var resolveBlock: RCTPromiseResolveBlock?
20
+ private var rejectBlock: RCTPromiseRejectBlock?
21
+
22
+ private func rejectPendingPromise() {
23
+ rejectBlock?("CANCELLED", "A new picker request was made before the previous one completed", nil)
24
+ resolveBlock = nil
25
+ rejectBlock = nil
26
+ }
27
+
28
+ private func getRootViewController() -> UIViewController? {
29
+ return UIApplication.shared.delegate?.window??.rootViewController?.presentedViewController
30
+ ?? UIApplication.shared.delegate?.window??.rootViewController
31
+ }
32
+
33
+
34
+ // MARK: - Pick Media (both images and videos)
35
+
36
+ @objc
37
+ func pickMedia(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
38
+ rejectPendingPromise()
39
+ self.resolveBlock = resolve
40
+ self.rejectBlock = reject
41
+
42
+ DispatchQueue.main.async {
43
+ if #available(iOS 14.0, *) {
44
+ var config = PHPickerConfiguration()
45
+ config.filter = .any(of: [.images, .videos])
46
+ config.selectionLimit = 0
47
+ let picker = PHPickerViewController(configuration: config)
48
+ picker.delegate = self
49
+ self.getRootViewController()?.present(picker, animated: true)
50
+ } else {
51
+ let picker = UIImagePickerController()
52
+ picker.sourceType = .photoLibrary
53
+ picker.mediaTypes = ["public.image", "public.movie"]
54
+ picker.delegate = self
55
+ self.getRootViewController()?.present(picker, animated: true)
56
+ }
57
+ }
58
+ }
59
+
60
+ // MARK: - Pick Image Only
61
+
62
+ @objc
63
+ func pickImage(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
64
+ rejectPendingPromise()
65
+ self.resolveBlock = resolve
66
+ self.rejectBlock = reject
67
+
68
+ DispatchQueue.main.async {
69
+ if #available(iOS 14.0, *) {
70
+ var config = PHPickerConfiguration()
71
+ config.filter = .images
72
+ config.selectionLimit = 0
73
+ let picker = PHPickerViewController(configuration: config)
74
+ picker.delegate = self
75
+ self.getRootViewController()?.present(picker, animated: true)
76
+ } else {
77
+ let picker = UIImagePickerController()
78
+ picker.sourceType = .photoLibrary
79
+ picker.mediaTypes = ["public.image"]
80
+ picker.delegate = self
81
+ self.getRootViewController()?.present(picker, animated: true)
82
+ }
83
+ }
84
+ }
85
+
86
+ // MARK: - Pick Video Only
87
+
88
+ @objc
89
+ func pickVideo(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
90
+ rejectPendingPromise()
91
+ self.resolveBlock = resolve
92
+ self.rejectBlock = reject
93
+
94
+ DispatchQueue.main.async {
95
+ if #available(iOS 14.0, *) {
96
+ var config = PHPickerConfiguration()
97
+ config.filter = .videos
98
+ config.selectionLimit = 0
99
+ let picker = PHPickerViewController(configuration: config)
100
+ picker.delegate = self
101
+ self.getRootViewController()?.present(picker, animated: true)
102
+ } else {
103
+ let picker = UIImagePickerController()
104
+ picker.sourceType = .photoLibrary
105
+ picker.mediaTypes = ["public.movie"]
106
+ picker.delegate = self
107
+ self.getRootViewController()?.present(picker, animated: true)
108
+ }
109
+ }
110
+ }
111
+
112
+ // MARK: - Generate Thumbnail from Video URL
113
+
114
+ @objc
115
+ func generateThumbnail(_ videoUrl: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
116
+ DispatchQueue.global(qos: .userInitiated).async {
117
+ guard let url = URL(string: videoUrl) else {
118
+ resolve(nil)
119
+ return
120
+ }
121
+ let asset = AVAsset(url: url)
122
+ let generator = AVAssetImageGenerator(asset: asset)
123
+ generator.appliesPreferredTrackTransform = true
124
+ let time = CMTimeMake(value: 0, timescale: 1)
125
+ do {
126
+ let cgImage = try generator.copyCGImage(at: time, actualTime: nil)
127
+ let uiImage = UIImage(cgImage: cgImage)
128
+ if let data = uiImage.jpegData(compressionQuality: 0.8) {
129
+ let fileName = "thumb_\(Int(Date().timeIntervalSince1970 * 1000)).jpg"
130
+ let tempDir = NSTemporaryDirectory()
131
+ let filePath = (tempDir as NSString).appendingPathComponent(fileName)
132
+ try data.write(to: URL(fileURLWithPath: filePath))
133
+ resolve("file://" + filePath)
134
+ } else {
135
+ resolve(nil)
136
+ }
137
+ } catch {
138
+ resolve(nil)
139
+ }
140
+ }
141
+ }
142
+
143
+ // MARK: - Open Video
144
+
145
+ @objc
146
+ func openVideo(_ uri: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
147
+ guard let url = URL(string: uri) else {
148
+ reject("INVALID_URL", "Invalid video URL", nil)
149
+ return
150
+ }
151
+ DispatchQueue.main.async { [weak self] in
152
+ guard let rootVC = self?.getRootViewController() else {
153
+ reject("NO_VC", "No view controller available", nil)
154
+ return
155
+ }
156
+ let player = AVPlayer(url: url)
157
+ let playerVC = AVPlayerViewController()
158
+ playerVC.player = player
159
+ rootVC.present(playerVC, animated: true) {
160
+ player.play()
161
+ }
162
+ resolve(nil)
163
+ }
164
+ }
165
+
166
+ // MARK: - Compress Image
167
+
168
+ private func compressImageIfNeeded(imageURL: URL, originalData: Data) -> (URL, Data, String) {
169
+ let fileSize = originalData.count
170
+ let threshold = 500 * 1024 // 500KB
171
+
172
+ if fileSize > threshold, let uiImage = UIImage(data: originalData) {
173
+ // Compress to 80% JPEG quality
174
+ if let compressedData = uiImage.jpegData(compressionQuality: 0.8) {
175
+ let tempDir = NSTemporaryDirectory()
176
+ let compressedFileName = UUID().uuidString + ".jpg"
177
+ let compressedURL = URL(fileURLWithPath: tempDir).appendingPathComponent(compressedFileName)
178
+ do {
179
+ try compressedData.write(to: compressedURL)
180
+ if imageURL != compressedURL {
181
+ try? FileManager.default.removeItem(at: imageURL)
182
+ }
183
+ return (compressedURL, compressedData, "image/jpeg")
184
+ } catch {
185
+ // Fall back to original
186
+ return (imageURL, originalData, self.mimeType(for: imageURL))
187
+ }
188
+ }
189
+ }
190
+ return (imageURL, originalData, self.mimeType(for: imageURL))
191
+ }
192
+
193
+ private func mimeType(for url: URL) -> String {
194
+ let ext = url.pathExtension.lowercased()
195
+ switch ext {
196
+ case "jpg", "jpeg": return "image/jpeg"
197
+ case "png": return "image/png"
198
+ case "gif": return "image/gif"
199
+ case "heic", "heif": return "image/heic"
200
+ case "mp4": return "video/mp4"
201
+ case "mov": return "video/quicktime"
202
+ case "m4v": return "video/x-m4v"
203
+ default: return "application/octet-stream"
204
+ }
205
+ }
206
+
207
+ private func resolveWithAsset(url: URL, type: String, mimeType: String, fileSize: Int, thumbnailUri: String? = nil) {
208
+ var result: [String: Any] = [
209
+ "uri": url.absoluteString,
210
+ "type": type,
211
+ "fileName": url.lastPathComponent,
212
+ "fileSize": fileSize,
213
+ "mimeType": mimeType,
214
+ ]
215
+ if let thumb = thumbnailUri {
216
+ result["thumbnailUri"] = thumb
217
+ }
218
+ resolveBlock?([result])
219
+ resolveBlock = nil
220
+ rejectBlock = nil
221
+ }
222
+
223
+ private func generateVideoThumbnail(for videoURL: URL) -> String? {
224
+ let asset = AVAsset(url: videoURL)
225
+ let generator = AVAssetImageGenerator(asset: asset)
226
+ generator.appliesPreferredTrackTransform = true
227
+ generator.maximumSize = CGSize(width: 500, height: 500)
228
+
229
+ do {
230
+ let cgImage = try generator.copyCGImage(at: CMTime.zero, actualTime: nil)
231
+ let uiImage = UIImage(cgImage: cgImage)
232
+ if let jpegData = uiImage.jpegData(compressionQuality: 0.7) {
233
+ let tempDir = NSTemporaryDirectory()
234
+ let thumbFileName = "thumb_" + UUID().uuidString + ".jpg"
235
+ let thumbURL = URL(fileURLWithPath: tempDir).appendingPathComponent(thumbFileName)
236
+ try jpegData.write(to: thumbURL)
237
+ return thumbURL.absoluteString
238
+ }
239
+ } catch {
240
+ // Thumbnail generation failed, return nil
241
+ }
242
+ return nil
243
+ }
244
+
245
+ @objc
246
+ func deleteTempFile(_ uri: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
247
+ DispatchQueue.global(qos: .utility).async {
248
+ let path = uri.replacingOccurrences(of: "file://", with: "")
249
+ let fileManager = FileManager.default
250
+ if fileManager.fileExists(atPath: path) {
251
+ try? fileManager.removeItem(atPath: path)
252
+ }
253
+ resolve(nil)
254
+ }
255
+ }
256
+
257
+ @objc
258
+ func cleanupTempFiles(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
259
+ DispatchQueue.global(qos: .utility).async {
260
+ let tempDir = NSTemporaryDirectory()
261
+ let fileManager = FileManager.default
262
+ do {
263
+ let files = try fileManager.contentsOfDirectory(atPath: tempDir)
264
+ for file in files {
265
+ let lower = file.lowercased()
266
+ if lower.hasPrefix("thumb_") || lower.hasPrefix("compressed_") || lower.contains("_") && (lower.hasSuffix(".jpg") || lower.hasSuffix(".jpeg") || lower.hasSuffix(".png") || lower.hasSuffix(".mp4") || lower.hasSuffix(".mov")) {
267
+ let fullPath = (tempDir as NSString).appendingPathComponent(file)
268
+ try? fileManager.removeItem(atPath: fullPath)
269
+ }
270
+ }
271
+ resolve(nil)
272
+ } catch {
273
+ resolve(nil)
274
+ }
275
+ }
276
+ }
277
+ }
278
+
279
+ // MARK: - PHPickerViewControllerDelegate (iOS 14+)
280
+
281
+ @available(iOS 14.0, *)
282
+ extension ComnyxMediaPicker: PHPickerViewControllerDelegate {
283
+ func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
284
+ picker.dismiss(animated: true)
285
+
286
+ guard !results.isEmpty else {
287
+ resolveBlock?(nil)
288
+ resolveBlock = nil
289
+ rejectBlock = nil
290
+ return
291
+ }
292
+
293
+ let allAssets = NSMutableArray()
294
+ let lock = NSLock()
295
+ let group = DispatchGroup()
296
+
297
+ for result in results {
298
+ let provider = result.itemProvider
299
+
300
+ // Handle Image
301
+ if provider.hasItemConformingToTypeIdentifier("public.image") {
302
+ group.enter()
303
+ provider.loadFileRepresentation(forTypeIdentifier: "public.image") { [weak self] url, error in
304
+ defer { group.leave() }
305
+ guard let self = self, let sourceURL = url, error == nil else {
306
+ print("[ComnyxMediaPicker] Image load failed: \(error?.localizedDescription ?? "no url")")
307
+ return
308
+ }
309
+
310
+ let tempDir = NSTemporaryDirectory()
311
+ let tempURL = URL(fileURLWithPath: tempDir).appendingPathComponent(UUID().uuidString + "_" + sourceURL.lastPathComponent)
312
+ do {
313
+ if FileManager.default.fileExists(atPath: tempURL.path) {
314
+ try FileManager.default.removeItem(at: tempURL)
315
+ }
316
+ try FileManager.default.copyItem(at: sourceURL, to: tempURL)
317
+ let data = try Data(contentsOf: tempURL)
318
+ let (finalURL, finalData, finalMimeType) = self.compressImageIfNeeded(imageURL: tempURL, originalData: data)
319
+ let asset: [String: Any] = [
320
+ "uri": finalURL.absoluteString,
321
+ "type": "image",
322
+ "fileName": finalURL.lastPathComponent,
323
+ "fileSize": finalData.count,
324
+ "mimeType": finalMimeType,
325
+ ]
326
+ print("[ComnyxMediaPicker] Image asset ready: \(asset)")
327
+ lock.lock()
328
+ allAssets.add(asset)
329
+ lock.unlock()
330
+ } catch {
331
+ print("[ComnyxMediaPicker] Image processing error: \(error)")
332
+ }
333
+ }
334
+ continue
335
+ }
336
+
337
+ // Handle Video
338
+ if provider.hasItemConformingToTypeIdentifier("public.movie") {
339
+ group.enter()
340
+ provider.loadFileRepresentation(forTypeIdentifier: "public.movie") { [weak self] url, error in
341
+ defer { group.leave() }
342
+ guard let self = self, let sourceURL = url, error == nil else {
343
+ print("[ComnyxMediaPicker] Video load failed: \(error?.localizedDescription ?? "no url")")
344
+ return
345
+ }
346
+
347
+ let tempDir = NSTemporaryDirectory()
348
+ let tempURL = URL(fileURLWithPath: tempDir).appendingPathComponent(UUID().uuidString + "_" + sourceURL.lastPathComponent)
349
+ do {
350
+ if FileManager.default.fileExists(atPath: tempURL.path) {
351
+ try FileManager.default.removeItem(at: tempURL)
352
+ }
353
+ try FileManager.default.copyItem(at: sourceURL, to: tempURL)
354
+ let attrs = try FileManager.default.attributesOfItem(atPath: tempURL.path)
355
+ let fileSize = attrs[.size] as? Int ?? 0
356
+ let mime = self.mimeType(for: tempURL)
357
+ let thumbnailUri = self.generateVideoThumbnail(for: tempURL)
358
+ var asset: [String: Any] = [
359
+ "uri": tempURL.absoluteString,
360
+ "type": "video",
361
+ "fileName": tempURL.lastPathComponent,
362
+ "fileSize": fileSize,
363
+ "mimeType": mime,
364
+ ]
365
+ if let thumb = thumbnailUri {
366
+ asset["thumbnailUri"] = thumb
367
+ }
368
+ print("[ComnyxMediaPicker] Video asset ready: \(asset)")
369
+ lock.lock()
370
+ allAssets.add(asset)
371
+ lock.unlock()
372
+ } catch {
373
+ print("[ComnyxMediaPicker] Video processing error: \(error)")
374
+ }
375
+ }
376
+ continue
377
+ }
378
+ }
379
+
380
+ group.notify(queue: .main) { [weak self] in
381
+ print("[ComnyxMediaPicker] All assets processed. Count: \(allAssets.count)")
382
+ if allAssets.count == 0 {
383
+ self?.resolveBlock?(nil)
384
+ } else {
385
+ let result = allAssets.map { $0 }
386
+ print("[ComnyxMediaPicker] Resolving with \(result.count) assets")
387
+ self?.resolveBlock?(result)
388
+ }
389
+ self?.resolveBlock = nil
390
+ self?.rejectBlock = nil
391
+ }
392
+ }
393
+ }
394
+
395
+ // MARK: - UIImagePickerControllerDelegate (Fallback)
396
+
397
+ extension ComnyxMediaPicker: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
398
+ func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
399
+ picker.dismiss(animated: true)
400
+
401
+ if let imageURL = info[.imageURL] as? URL {
402
+ do {
403
+ let data = try Data(contentsOf: imageURL)
404
+ let (finalURL, finalData, finalMimeType) = compressImageIfNeeded(imageURL: imageURL, originalData: data)
405
+ resolveWithAsset(url: finalURL, type: "image", mimeType: finalMimeType, fileSize: finalData.count)
406
+ } catch {
407
+ rejectBlock?("FILE_ERROR", error.localizedDescription, error)
408
+ resolveBlock = nil
409
+ rejectBlock = nil
410
+ }
411
+ } else if let videoURL = info[.mediaURL] as? URL {
412
+ do {
413
+ let attrs = try FileManager.default.attributesOfItem(atPath: videoURL.path)
414
+ let fileSize = attrs[.size] as? Int ?? 0
415
+ let mime = mimeType(for: videoURL)
416
+ let thumbnailUri = generateVideoThumbnail(for: videoURL)
417
+ resolveWithAsset(url: videoURL, type: "video", mimeType: mime, fileSize: fileSize, thumbnailUri: thumbnailUri)
418
+ } catch {
419
+ rejectBlock?("FILE_ERROR", error.localizedDescription, error)
420
+ resolveBlock = nil
421
+ rejectBlock = nil
422
+ }
423
+ } else {
424
+ resolveBlock?(nil)
425
+ resolveBlock = nil
426
+ rejectBlock = nil
427
+ }
428
+ }
429
+
430
+ func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
431
+ picker.dismiss(animated: true)
432
+ resolveBlock?(nil)
433
+ resolveBlock = nil
434
+ rejectBlock = nil
435
+ }
436
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.cleanupTempFiles = cleanupTempFiles;
7
+ exports.deleteTempFile = deleteTempFile;
8
+ exports.generateThumbnail = generateThumbnail;
9
+ exports.openVideo = openVideo;
10
+ exports.pickImage = pickImage;
11
+ exports.pickMedia = pickMedia;
12
+ exports.pickVideo = pickVideo;
13
+ var _reactNative = require("react-native");
14
+ const {
15
+ ComnyxMediaPicker
16
+ } = _reactNative.NativeModules;
17
+ function mapResults(results) {
18
+ return results.map(result => {
19
+ const isImage = result.type === 'image' || result.mimeType && result.mimeType.startsWith('image');
20
+ return {
21
+ uri: result.uri,
22
+ type: isImage ? 'image' : 'video',
23
+ fileName: result.fileName,
24
+ fileSize: result.fileSize,
25
+ mimeType: result.mimeType,
26
+ width: isImage ? result.width : undefined,
27
+ height: isImage ? result.height : undefined,
28
+ duration: isImage ? undefined : result.duration,
29
+ thumbnailUri: isImage ? undefined : result.thumbnailUri
30
+ };
31
+ });
32
+ }
33
+ async function pickMedia() {
34
+ if (!ComnyxMediaPicker) {
35
+ return [];
36
+ }
37
+ const results = await ComnyxMediaPicker.pickMedia();
38
+ if (!results || !Array.isArray(results) || results.length === 0) return [];
39
+ return mapResults(results);
40
+ }
41
+ async function pickImage() {
42
+ if (!ComnyxMediaPicker) {
43
+ return [];
44
+ }
45
+ const results = await ComnyxMediaPicker.pickImage();
46
+ if (!results || !Array.isArray(results) || results.length === 0) return [];
47
+ return mapResults(results);
48
+ }
49
+ async function pickVideo() {
50
+ if (!ComnyxMediaPicker) {
51
+ return [];
52
+ }
53
+ const results = await ComnyxMediaPicker.pickVideo();
54
+ if (!results || !Array.isArray(results) || results.length === 0) return [];
55
+ return mapResults(results);
56
+ }
57
+ async function openVideo(uri) {
58
+ if (!ComnyxMediaPicker) {
59
+ console.warn('[Comnyx] ComnyxMediaPicker native module is not available');
60
+ return;
61
+ }
62
+ await ComnyxMediaPicker.openVideo(uri);
63
+ }
64
+ async function generateThumbnail(videoUrl) {
65
+ if (!ComnyxMediaPicker) {
66
+ console.warn('[Comnyx] ComnyxMediaPicker native module is not available');
67
+ return null;
68
+ }
69
+ return ComnyxMediaPicker.generateThumbnail(videoUrl);
70
+ }
71
+ async function deleteTempFile(uri) {
72
+ if (!ComnyxMediaPicker) {
73
+ return;
74
+ }
75
+ return ComnyxMediaPicker.deleteTempFile(uri);
76
+ }
77
+ async function cleanupTempFiles() {
78
+ if (!ComnyxMediaPicker) {
79
+ return;
80
+ }
81
+ return ComnyxMediaPicker.cleanupTempFiles();
82
+ }
83
+ //# sourceMappingURL=NativeComnyxMediaPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNative","require","ComnyxMediaPicker","NativeModules","mapResults","results","map","result","isImage","type","mimeType","startsWith","uri","fileName","fileSize","width","undefined","height","duration","thumbnailUri","pickMedia","Array","isArray","length","pickImage","pickVideo","openVideo","console","warn","generateThumbnail","videoUrl","deleteTempFile","cleanupTempFiles"],"sourceRoot":"../../src","sources":["NativeComnyxMediaPicker.ts"],"mappings":";;;;;;;;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAGA,MAAM;EAAEC;AAAkB,CAAC,GAAGC,0BAAa;AAE3C,SAASC,UAAUA,CAACC,OAAc,EAAgB;EAChD,OAAOA,OAAO,CAACC,GAAG,CAAEC,MAAW,IAAK;IAClC,MAAMC,OAAO,GACXD,MAAM,CAACE,IAAI,KAAK,OAAO,IACtBF,MAAM,CAACG,QAAQ,IAAIH,MAAM,CAACG,QAAQ,CAACC,UAAU,CAAC,OAAO,CAAE;IAE1D,OAAO;MACLC,GAAG,EAAEL,MAAM,CAACK,GAAG;MACfH,IAAI,EAAED,OAAO,GAAI,OAAO,GAAc,OAAiB;MACvDK,QAAQ,EAAEN,MAAM,CAACM,QAAQ;MACzBC,QAAQ,EAAEP,MAAM,CAACO,QAAQ;MACzBJ,QAAQ,EAAEH,MAAM,CAACG,QAAQ;MACzBK,KAAK,EAAEP,OAAO,GAAGD,MAAM,CAACQ,KAAK,GAAGC,SAAS;MACzCC,MAAM,EAAET,OAAO,GAAGD,MAAM,CAACU,MAAM,GAAGD,SAAS;MAC3CE,QAAQ,EAAEV,OAAO,GAAGQ,SAAS,GAAGT,MAAM,CAACW,QAAQ;MAC/CC,YAAY,EAAEX,OAAO,GAAGQ,SAAS,GAAGT,MAAM,CAACY;IAC7C,CAAC;EACH,CAAC,CAAC;AACJ;AAEO,eAAeC,SAASA,CAAA,EAA0B;EACvD,IAAI,CAAClB,iBAAiB,EAAE;IACtB,OAAO,EAAE;EACX;EACA,MAAMG,OAAO,GAAG,MAAMH,iBAAiB,CAACkB,SAAS,CAAC,CAAC;EACnD,IAAI,CAACf,OAAO,IAAI,CAACgB,KAAK,CAACC,OAAO,CAACjB,OAAO,CAAC,IAAIA,OAAO,CAACkB,MAAM,KAAK,CAAC,EAAE,OAAO,EAAE;EAC1E,OAAOnB,UAAU,CAACC,OAAO,CAAC;AAC5B;AAEO,eAAemB,SAASA,CAAA,EAA0B;EACvD,IAAI,CAACtB,iBAAiB,EAAE;IACtB,OAAO,EAAE;EACX;EACA,MAAMG,OAAO,GAAG,MAAMH,iBAAiB,CAACsB,SAAS,CAAC,CAAC;EACnD,IAAI,CAACnB,OAAO,IAAI,CAACgB,KAAK,CAACC,OAAO,CAACjB,OAAO,CAAC,IAAIA,OAAO,CAACkB,MAAM,KAAK,CAAC,EAAE,OAAO,EAAE;EAC1E,OAAOnB,UAAU,CAACC,OAAO,CAAC;AAC5B;AAEO,eAAeoB,SAASA,CAAA,EAA0B;EACvD,IAAI,CAACvB,iBAAiB,EAAE;IACtB,OAAO,EAAE;EACX;EACA,MAAMG,OAAO,GAAG,MAAMH,iBAAiB,CAACuB,SAAS,CAAC,CAAC;EACnD,IAAI,CAACpB,OAAO,IAAI,CAACgB,KAAK,CAACC,OAAO,CAACjB,OAAO,CAAC,IAAIA,OAAO,CAACkB,MAAM,KAAK,CAAC,EAAE,OAAO,EAAE;EAC1E,OAAOnB,UAAU,CAACC,OAAO,CAAC;AAC5B;AAEO,eAAeqB,SAASA,CAACd,GAAW,EAAiB;EAC1D,IAAI,CAACV,iBAAiB,EAAE;IACtByB,OAAO,CAACC,IAAI,CAAC,2DAA2D,CAAC;IACzE;EACF;EACA,MAAM1B,iBAAiB,CAACwB,SAAS,CAACd,GAAG,CAAC;AACxC;AAEO,eAAeiB,iBAAiBA,CACrCC,QAAgB,EACQ;EACxB,IAAI,CAAC5B,iBAAiB,EAAE;IACtByB,OAAO,CAACC,IAAI,CAAC,2DAA2D,CAAC;IACzE,OAAO,IAAI;EACb;EACA,OAAO1B,iBAAiB,CAAC2B,iBAAiB,CAACC,QAAQ,CAAC;AACtD;AAEO,eAAeC,cAAcA,CAACnB,GAAW,EAAiB;EAC/D,IAAI,CAACV,iBAAiB,EAAE;IACtB;EACF;EACA,OAAOA,iBAAiB,CAAC6B,cAAc,CAACnB,GAAG,CAAC;AAC9C;AAEO,eAAeoB,gBAAgBA,CAAA,EAAkB;EACtD,IAAI,CAAC9B,iBAAiB,EAAE;IACtB;EACF;EACA,OAAOA,iBAAiB,CAAC8B,gBAAgB,CAAC,CAAC;AAC7C","ignoreList":[]}
@@ -21,13 +21,32 @@ Object.defineProperty(exports, "getNewCustomerConversation", {
21
21
  return _conversations.getNewCustomerConversation;
22
22
  }
23
23
  });
24
+ Object.defineProperty(exports, "getUploadUrl", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _media.getUploadUrl;
28
+ }
29
+ });
24
30
  Object.defineProperty(exports, "sendCustomerMessage", {
25
31
  enumerable: true,
26
32
  get: function () {
27
33
  return _messages.sendCustomerMessage;
28
34
  }
29
35
  });
36
+ Object.defineProperty(exports, "sendMediaMessage", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _media.sendMediaMessage;
40
+ }
41
+ });
42
+ Object.defineProperty(exports, "uploadFileToS3", {
43
+ enumerable: true,
44
+ get: function () {
45
+ return _media.uploadFileToS3;
46
+ }
47
+ });
30
48
  var _customers = require("./customers.js");
31
49
  var _conversations = require("./conversations.js");
32
50
  var _messages = require("./messages.js");
51
+ var _media = require("./media.js");
33
52
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_customers","require","_conversations","_messages"],"sourceRoot":"../../../src","sources":["api/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,UAAA,GAAAC,OAAA;AACA,IAAAC,cAAA,GAAAD,OAAA;AAIA,IAAAE,SAAA,GAAAF,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["_customers","require","_conversations","_messages","_media"],"sourceRoot":"../../../src","sources":["api/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,UAAA,GAAAC,OAAA;AACA,IAAAC,cAAA,GAAAD,OAAA;AAIA,IAAAE,SAAA,GAAAF,OAAA;AACA,IAAAG,MAAA,GAAAH,OAAA","ignoreList":[]}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getUploadUrl = getUploadUrl;
7
+ exports.sendMediaMessage = sendMediaMessage;
8
+ exports.uploadFileToS3 = uploadFileToS3;
9
+ var _reactNative = require("react-native");
10
+ var _api = require("./api.js");
11
+ var _media = require("../data/fake/media.js");
12
+ function getUploadUrl(fileName, contentType, options) {
13
+ if (options.fake) {
14
+ return (0, _media.getFakeUploadUrlResponse)(fileName);
15
+ }
16
+ return _api.axiosInstance.post('/api/media/presign', {
17
+ fileName,
18
+ fileType: contentType
19
+ }).then(res => res.data);
20
+ }
21
+ function uploadFileToS3(uploadUrl, fileUri, contentType, onProgress, options) {
22
+ if (options?.fake) {
23
+ return (0, _media.fakeUploadToS3)(onProgress);
24
+ }
25
+ return new Promise((resolve, reject) => {
26
+ const xhr = new XMLHttpRequest();
27
+ xhr.upload.addEventListener('progress', event => {
28
+ if (event.lengthComputable && onProgress) {
29
+ const percentage = Math.min(100, Math.round(event.loaded / event.total * 100));
30
+ onProgress(percentage);
31
+ }
32
+ });
33
+ xhr.addEventListener('load', () => {
34
+ if (xhr.status >= 200 && xhr.status < 300) {
35
+ resolve();
36
+ } else {
37
+ console.error('[Comnyx] Upload failed:', xhr.status, xhr.responseText);
38
+ reject(new Error(`Upload failed with status ${xhr.status}`));
39
+ }
40
+ });
41
+ xhr.addEventListener('error', () => {
42
+ console.error('[Comnyx] Upload XHR error, status:', xhr.status, 'response:', xhr.responseText);
43
+ reject(new Error('Upload failed'));
44
+ });
45
+ xhr.addEventListener('abort', () => {
46
+ reject(new Error('Upload aborted'));
47
+ });
48
+ xhr.open('PUT', uploadUrl);
49
+ xhr.setRequestHeader('Content-Type', contentType);
50
+
51
+ // React Native supports sending file URIs directly via XHR
52
+ const file = {
53
+ uri: _reactNative.Platform.OS === 'android' ? fileUri : fileUri.replace('file://', ''),
54
+ type: contentType,
55
+ name: fileUri.split('/').pop() || 'file'
56
+ };
57
+ xhr.send(file);
58
+ });
59
+ }
60
+ function sendMediaMessage(externalId, fileUrls, mediaType, content, options) {
61
+ if (options.fake) {
62
+ return (0, _media.getFakeMediaMessageResponse)(fileUrls, mediaType);
63
+ }
64
+ return _api.axiosInstance.post('/api/customers/message', {
65
+ externalId,
66
+ content,
67
+ files: fileUrls,
68
+ media_type: mediaType
69
+ }, {
70
+ headers: {
71
+ 'Accept': 'application/json',
72
+ 'Content-Type': 'application/json'
73
+ }
74
+ }).then(res => res.data);
75
+ }
76
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNative","require","_api","_media","getUploadUrl","fileName","contentType","options","fake","getFakeUploadUrlResponse","axiosInstance","post","fileType","then","res","data","uploadFileToS3","uploadUrl","fileUri","onProgress","fakeUploadToS3","Promise","resolve","reject","xhr","XMLHttpRequest","upload","addEventListener","event","lengthComputable","percentage","Math","min","round","loaded","total","status","console","error","responseText","Error","open","setRequestHeader","file","uri","Platform","OS","replace","type","name","split","pop","send","sendMediaMessage","externalId","fileUrls","mediaType","content","getFakeMediaMessageResponse","files","media_type","headers"],"sourceRoot":"../../../src","sources":["api/media.ts"],"mappings":"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","ignoreList":[]}
Binary file
Binary file