react-native-attachment-kit 1.0.1 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/README.md +356 -213
  3. package/android/build.gradle +3 -0
  4. package/android/src/main/libs/rnattachmentkit-release.aar +0 -0
  5. package/android/src/newarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +308 -10
  6. package/android/src/oldarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +311 -9
  7. package/ios/RNAttachmentKit.m +44 -0
  8. package/ios/RNAttachmentKit.swift +307 -23
  9. package/ios/RNAttachmentKit.xcframework/Info.plist +5 -5
  10. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Assets.car +0 -0
  11. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Headers/RNAttachmentKit-Swift.h +30 -0
  12. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.abi.json +20349 -13021
  13. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.private.swiftinterface +118 -1
  14. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  15. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftinterface +118 -1
  16. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/RNAttachmentKit +0 -0
  17. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
  18. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
  19. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
  20. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
  21. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
  22. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
  23. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
  24. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
  25. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
  26. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
  27. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~dark.webp +0 -0
  28. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~light.webp +0 -0
  29. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
  30. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~light.webp +0 -0
  31. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
  32. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
  33. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~dark.webp +0 -0
  34. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~light.webp +0 -0
  35. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~dark.webp +0 -0
  36. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~light.webp +0 -0
  37. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
  38. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~light.webp +0 -0
  39. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
  40. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
  41. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
  42. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~light.webp +0 -0
  43. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
  44. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
  45. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
  46. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
  47. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
  48. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
  49. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~dark.webp +0 -0
  50. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~light.webp +0 -0
  51. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~dark.webp +0 -0
  52. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~light.webp +0 -0
  53. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
  54. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeResources +194 -62
  55. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeSignature +0 -0
  56. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Assets.car +0 -0
  57. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Headers/RNAttachmentKit-Swift.h +60 -0
  58. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.abi.json +20349 -13021
  59. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +118 -1
  60. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  61. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +118 -1
  62. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +20349 -13021
  63. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +118 -1
  64. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  65. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +118 -1
  66. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/RNAttachmentKit +0 -0
  67. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
  68. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
  69. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
  70. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
  71. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
  72. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
  73. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
  74. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
  75. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
  76. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
  77. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~dark.webp +0 -0
  78. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~light.webp +0 -0
  79. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
  80. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~light.webp +0 -0
  81. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
  82. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
  83. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~dark.webp +0 -0
  84. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~light.webp +0 -0
  85. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~dark.webp +0 -0
  86. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~light.webp +0 -0
  87. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
  88. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~light.webp +0 -0
  89. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
  90. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
  91. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
  92. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~light.webp +0 -0
  93. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
  94. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
  95. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
  96. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
  97. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
  98. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
  99. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~dark.webp +0 -0
  100. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~light.webp +0 -0
  101. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~dark.webp +0 -0
  102. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~light.webp +0 -0
  103. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
  104. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeResources +204 -72
  105. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
  106. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
  107. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
  108. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
  109. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
  110. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
  111. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
  112. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
  113. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
  114. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
  115. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_back~dark.webp +0 -0
  116. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_back~light.webp +0 -0
  117. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
  118. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_blur~light.webp +0 -0
  119. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
  120. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
  121. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check~dark.webp +0 -0
  122. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check~light.webp +0 -0
  123. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_close~dark.webp +0 -0
  124. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_close~light.webp +0 -0
  125. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
  126. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_crop~light.webp +0 -0
  127. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
  128. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
  129. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
  130. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_effect~light.webp +0 -0
  131. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
  132. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
  133. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
  134. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
  135. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
  136. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
  137. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_text~dark.webp +0 -0
  138. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_text~light.webp +0 -0
  139. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_text_add~dark.webp +0 -0
  140. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_text_add~light.webp +0 -0
  141. package/package.json +1 -1
  142. package/src/NativeAttachmentKit.ts +10 -0
  143. package/src/index.d.ts +5 -56
  144. package/src/index.ts +188 -35
  145. package/src/types.ts +207 -11
@@ -109,6 +109,9 @@ if (project != rootProject) {
109
109
  // NoClassDefFoundError로 Glide 초기화가 실패해 피커/뷰어 이미지가 전부 안 뜹니다.
110
110
  implementation "com.github.bumptech.glide:okhttp3-integration:4.16.0"
111
111
  implementation "androidx.exifinterface:exifinterface:1.3.7"
112
+ // 첨부 메뉴 > 위치 — AAR 내부 의존성은 앱으로 전달되지 않으므로 여기에도 선언 (Glide 와 같은 이유)
113
+ implementation "com.google.android.gms:play-services-maps:19.0.0"
114
+ implementation "com.google.android.gms:play-services-location:21.3.0"
112
115
 
113
116
  // CameraX dependencies
114
117
  def camerax_version = "1.3.1"
@@ -324,6 +324,21 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
324
324
  }
325
325
 
326
326
  pickImagesPromise = promise
327
+ // 이번 호출의 promise 를 붙잡아 둠 — 리스너가 모듈 필드를 읽으면, 시트가 열린 채 openGallery 를 다시 불렀을 때
328
+ // 이전 시트가 치워지며 오는 onDismiss 가 "새" promise 를 거부해 버림. 시트마다 자기 promise 를 한 번만 끝냄
329
+ val thisPromise = promise
330
+ var settled = false
331
+ val settle: ((Promise) -> Unit) -> Unit = { block ->
332
+ if (!settled) {
333
+ settled = true
334
+ if (pickImagesPromise === thisPromise) pickImagesPromise = null
335
+ block(thisPromise)
336
+ }
337
+ }
338
+ // 지난 호출이 남긴 오래된 임시 파일(24시간 지난 것) 정리 — 백그라운드
339
+ moduleScope.launch(Dispatchers.IO) {
340
+ try { TempFileCleaner.cleanStale(reactApplicationContext) } catch (_: Exception) {}
341
+ }
327
342
 
328
343
  val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
329
344
  Manifest.permission.READ_MEDIA_IMAGES
@@ -336,19 +351,70 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
336
351
  try {
337
352
  val bottomSheet = ImagePickerBottomSheet.newInstance(
338
353
  maxSelection, languageCode, enableEditor, profileMode, maxWidth, maxHeight, themeColor,
339
- forceMultiMode = forceMultiMode
354
+ forceMultiMode = forceMultiMode,
355
+ // 첨부 메뉴(갤러리 / 위치 / 파일). 기본 false — 켜지 않으면 기존과 같음
356
+ showAttachMenu = options.hasKey("showAttachMenu") && options.getBoolean("showAttachMenu"),
357
+ // (Android) 첨부 메뉴 바 아래 여백 (dp). 없으면 기본 12 / 8
358
+ attachMenuSinkIntoNav = if (options.hasKey("attachMenuSinkIntoNav") && !options.isNull("attachMenuSinkIntoNav"))
359
+ options.getDouble("attachMenuSinkIntoNav").toFloat() else com.rnattachmentkit.AttachMenuBarView.SINK_INTO_NAV_DP.toFloat(),
360
+ attachMenuMinBottomMargin = if (options.hasKey("attachMenuMinBottomMargin") && !options.isNull("attachMenuMinBottomMargin"))
361
+ options.getDouble("attachMenuMinBottomMargin").toFloat() else com.rnattachmentkit.AttachMenuBarView.BOTTOM_MARGIN_DP.toFloat(),
362
+ // 위치 보내기 때 지도 이미지 캡처 여부. 기본 true (기존 그대로). false 면 이미지 없이 위치 정보만
363
+ captureLocationImage = !(options.hasKey("captureLocationImage") && !options.isNull("captureLocationImage")) ||
364
+ options.getBoolean("captureLocationImage")
340
365
  )
366
+ // 위치 탭에서 보내기 → onLocationSelected 이벤트로 결과를 넘기고, openGallery 는 빈 배열로 끝냄
367
+ bottomSheet.setOnLocationSelectedListener { loc ->
368
+ val body = com.facebook.react.bridge.Arguments.createMap()
369
+ for ((k, v) in loc) {
370
+ when (v) {
371
+ is Int -> body.putInt(k, v)
372
+ is Long -> body.putDouble(k, v.toDouble())
373
+ is Double -> body.putDouble(k, v)
374
+ is Float -> body.putDouble(k, v.toDouble())
375
+ is Boolean -> body.putBoolean(k, v)
376
+ is String -> body.putString(k, v)
377
+ }
378
+ }
379
+ emitGalleryEvent("onLocationSelected", body)
380
+ settle { it.resolve(com.facebook.react.bridge.Arguments.createArray()) }
381
+ }
382
+ // 파일 탭에서 보내기 → onFilesSelected 이벤트 { files: [{uri, path, name, size, mimeType}], failed: [{name, error}] }, openGallery 는 빈 배열
383
+ bottomSheet.setOnFilesResultListener { files, failed ->
384
+ val arr = com.facebook.react.bridge.Arguments.createArray()
385
+ for (file in files) {
386
+ val m = com.facebook.react.bridge.Arguments.createMap()
387
+ for ((k, v) in file) {
388
+ when (v) {
389
+ is Double -> m.putDouble(k, v)
390
+ is String -> m.putString(k, v)
391
+ }
392
+ }
393
+ arr.pushMap(m)
394
+ }
395
+ val body = com.facebook.react.bridge.Arguments.createMap()
396
+ body.putArray("files", arr)
397
+ // 복사에 실패한 파일 (추가 필드 — files 는 예전과 같음)
398
+ val failedArr = com.facebook.react.bridge.Arguments.createArray()
399
+ for (f in failed) {
400
+ val m = com.facebook.react.bridge.Arguments.createMap()
401
+ m.putString("name", f["name"]?.toString() ?: "")
402
+ m.putString("error", f["error"]?.toString() ?: "")
403
+ failedArr.pushMap(m)
404
+ }
405
+ body.putArray("failed", failedArr)
406
+ emitGalleryEvent("onFilesSelected", body)
407
+ settle { it.resolve(com.facebook.react.bridge.Arguments.createArray()) }
408
+ }
341
409
  bottomSheet.setOnImagesSelectedListener { uris ->
342
410
  val result = WritableNativeArray()
343
411
  for (uri in uris) {
344
412
  result.pushString(uri.toString())
345
413
  }
346
- handleSelectedImages(result, pickImagesPromise)
347
- pickImagesPromise = null
414
+ settle { handleSelectedImages(result, it) }
348
415
  }
349
416
  bottomSheet.setOnCancelledListener {
350
- pickImagesPromise?.reject(E_PICKER_CANCELLED, "User cancelled image picker")
351
- pickImagesPromise = null
417
+ settle { it.reject(E_PICKER_CANCELLED, "User cancelled image picker") }
352
418
  }
353
419
 
354
420
  val existingFragment = fragmentActivity.supportFragmentManager.findFragmentByTag("ImagePickerBottomSheet")
@@ -361,8 +427,7 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
361
427
  .commitAllowingStateLoss()
362
428
 
363
429
  } catch (e: Exception) {
364
- pickImagesPromise?.reject(E_FAILED_TO_PICK, "Failed to open image picker: ${e.message}", e)
365
- pickImagesPromise = null
430
+ settle { it.reject(E_FAILED_TO_PICK, "Failed to open image picker: ${e.message}", e) }
366
431
  }
367
432
  }
368
433
  }
@@ -402,8 +467,7 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
402
467
  if (isGranted) {
403
468
  showSheet()
404
469
  } else {
405
- pickImagesPromise?.reject("E_PERMISSION_DENIED", "Permission denied")
406
- pickImagesPromise = null
470
+ settle { it.reject("E_PERMISSION_DENIED", "Permission denied") }
407
471
  }
408
472
  }
409
473
  return true
@@ -1158,5 +1222,239 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
1158
1222
  androidx.localbroadcastmanager.content.LocalBroadcastManager.getInstance(reactApplicationContext)
1159
1223
  .unregisterReceiver(imageDeletedReceiver)
1160
1224
  }
1161
- }
1162
1225
 
1226
+ // ── 지도 뷰어 (위치 메시지 → 지도) ──
1227
+
1228
+ private var mapViewer: java.lang.ref.WeakReference<MapViewerDialogFragment>? = null
1229
+ /** updateMapViewerSourceRect 로 갱신되는 닫힘 위치 (화면 px) */
1230
+ @Volatile private var mapViewerSourceRect: android.graphics.Rect? = null
1231
+
1232
+ /** RN measureInWindow 값(dp, 창 기준) → 화면 px Rect */
1233
+ private fun mapRectFromDp(map: ReadableMap?, activity: Activity): android.graphics.Rect? {
1234
+ if (map == null || !map.hasKey("x") || !map.hasKey("y") || !map.hasKey("width") || !map.hasKey("height")) return null
1235
+ val w = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(map.getDouble("width").toFloat())
1236
+ val h = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(map.getDouble("height").toFloat())
1237
+ if (w <= 0f || h <= 0f) return null
1238
+ val loc = IntArray(2)
1239
+ try { activity.window?.decorView?.getLocationOnScreen(loc) } catch (_: Exception) {}
1240
+ val x = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(map.getDouble("x").toFloat()) + loc[0]
1241
+ val y = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(map.getDouble("y").toFloat()) + loc[1]
1242
+ return android.graphics.Rect(Math.round(x), Math.round(y), Math.round(x + w), Math.round(y + h))
1243
+ }
1244
+
1245
+ /** 말풍선 이미지 → 로컬 파일 경로. file:// 는 그대로, http(s) 는 Glide 캐시 파일. 실패·지연 시 null (이미지 없이 엶) */
1246
+ private fun mapSourceImagePath(uri: String?): String? {
1247
+ if (uri.isNullOrBlank()) return null
1248
+ if (uri.startsWith("/")) return uri.takeIf { File(it).exists() }
1249
+ if (uri.startsWith("file://")) return Uri.parse(uri).path?.takeIf { File(it).exists() }
1250
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) return null
1251
+ return try {
1252
+ com.bumptech.glide.Glide.with(reactApplicationContext.applicationContext)
1253
+ .downloadOnly().load(uri).submit()
1254
+ .get(1500, java.util.concurrent.TimeUnit.MILLISECONDS)
1255
+ ?.absolutePath
1256
+ } catch (e: Exception) {
1257
+ android.util.Log.w("RNAttachmentKit", "openMapViewer: source image not loaded (${e.javaClass.simpleName})")
1258
+ null
1259
+ }
1260
+ }
1261
+
1262
+ override fun openMapViewer(options: ReadableMap, promise: Promise) {
1263
+ val activity = getCurrentActivity() as? FragmentActivity
1264
+ if (activity == null) {
1265
+ promise.reject("NO_ACTIVITY", "Activity is not a FragmentActivity")
1266
+ return
1267
+ }
1268
+ if (!options.hasKey("latitude") || !options.hasKey("longitude")) {
1269
+ promise.reject("ERROR", "latitude and longitude are required")
1270
+ return
1271
+ }
1272
+ fun str(key: String) = if (options.hasKey(key) && !options.isNull(key)) options.getString(key) else null
1273
+ fun num(key: String) = if (options.hasKey(key) && !options.isNull(key)) options.getDouble(key) else null
1274
+ val lat = options.getDouble("latitude")
1275
+ val lng = options.getDouble("longitude")
1276
+ val rect = mapRectFromDp(if (options.hasKey("sourceRect") && !options.isNull("sourceRect")) options.getMap("sourceRect") else null, activity)
1277
+ val imageUri = str("sourceImageUri")
1278
+ val themeColor = str("themeColor")?.let { try { android.graphics.Color.parseColor(it) } catch (_: Exception) { null } }
1279
+ val radiusPx = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP((num("sourceCornerRadius") ?: 12.0).toFloat())
1280
+ val showDirections = if (options.hasKey("showDirections") && !options.isNull("showDirections")) options.getBoolean("showDirections") else true
1281
+
1282
+ CoroutineScope(Dispatchers.IO).launch {
1283
+ val imagePath = if (rect != null) mapSourceImagePath(imageUri) else null
1284
+ withContext(Dispatchers.Main) {
1285
+ if (activity.isFinishing || activity.isDestroyed || activity.supportFragmentManager.isStateSaved) {
1286
+ promise.reject("ERROR", "Activity is not available")
1287
+ return@withContext
1288
+ }
1289
+ try {
1290
+ mapViewerSourceRect = rect
1291
+ val f = MapViewerDialogFragment.show(
1292
+ activity, lat, lng,
1293
+ title = str("title"),
1294
+ address = str("address"),
1295
+ markerType = str("markerType"),
1296
+ themeColor = themeColor,
1297
+ languageCode = str("languageCode") ?: "en",
1298
+ showDirections = showDirections,
1299
+ sourceRect = rect,
1300
+ sourceImagePath = imagePath,
1301
+ sourceCornerRadius = radiusPx,
1302
+ spanMeters = num("spanMeters") ?: 0.0,
1303
+ appearance = str("appearance"),
1304
+ addressLanguageCode = str("addressLanguageCode"),
1305
+ )
1306
+ if (rect != null) f.sourceRectProvider = { mapViewerSourceRect }
1307
+ var settled = false
1308
+ f.onClosed = {
1309
+ if (!settled) { settled = true; promise.resolve(null) }
1310
+ mapViewerSourceRect = null
1311
+ mapViewer = null
1312
+ }
1313
+ mapViewer = java.lang.ref.WeakReference(f)
1314
+ } catch (e: Exception) {
1315
+ promise.reject("ERROR", e.message, e)
1316
+ }
1317
+ }
1318
+ }
1319
+ }
1320
+
1321
+ override fun updateMapViewerSourceRect(rect: ReadableMap, promise: Promise) {
1322
+ val activity = getCurrentActivity()
1323
+ if (mapViewer?.get() != null && activity != null) mapViewerSourceRect = mapRectFromDp(rect, activity)
1324
+ promise.resolve(null)
1325
+ }
1326
+
1327
+ /**
1328
+ * 위치 메시지 지도 썸네일을 이 기기에서 그림 (테마별 캐시). 결과 { uri, path, width, height(px), cached }
1329
+ * options: latitude, longitude, width, height (dp, 필수) / markerType, placeCategory, spanMeters, themeColor,
1330
+ * appearance ("light"/"dark"/"system"), myLatitude + myLongitude (둘 다 있을 때만)
1331
+ * aspect ("square" | "portrait" | "device"): 주면 width 만 필수, height 는 비율로 (square = 1:1,
1332
+ * portrait = 480:1040 보내던 이미지와 같은 9:19.5, device = 실제 화면 긴 변/짧은 변). 모르는 값이면 없는 것과 같음
1333
+ * maxWidth / maxHeight (px, 기본 512, 0 이하면 무시): 출력 WebP 가 이 안에 들어가게 비율대로 줄임
1334
+ * (k = min(density, maxWidth / widthDp, maxHeight / heightDp)) → 결과 width/height 는 출력 px
1335
+ */
1336
+ @ReactMethod
1337
+ override fun renderLocationThumbnail(options: ReadableMap, promise: Promise) {
1338
+ fun str(key: String) = if (options.hasKey(key) && !options.isNull(key)) options.getString(key) else null
1339
+ fun num(key: String) = if (options.hasKey(key) && !options.isNull(key)) options.getDouble(key) else null
1340
+ val lat = num("latitude")
1341
+ val lng = num("longitude")
1342
+ val wDp = num("width")
1343
+ val activity = getCurrentActivity()
1344
+ val aspectRatio: Double? = when (str("aspect")?.lowercase()) {
1345
+ "square" -> 1.0
1346
+ "portrait" -> 1040.0 / 480.0
1347
+ "device" -> {
1348
+ // 실제 화면 크기 (세로 기준: 긴 변 / 짧은 변 → h > w)
1349
+ val dm = (activity ?: reactApplicationContext).resources.displayMetrics
1350
+ var sw = dm.widthPixels; var sh = dm.heightPixels
1351
+ if (activity != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
1352
+ try {
1353
+ val b = activity.windowManager.currentWindowMetrics.bounds
1354
+ if (b.width() > 0 && b.height() > 0) { sw = b.width(); sh = b.height() }
1355
+ } catch (_: Exception) {}
1356
+ }
1357
+ val lo = minOf(sw, sh); val hi = maxOf(sw, sh)
1358
+ if (lo > 0) hi.toDouble() / lo else 19.5 / 9.0
1359
+ }
1360
+ else -> null
1361
+ }
1362
+ val hDp = if (aspectRatio != null) wDp?.let { it * aspectRatio } else num("height")
1363
+ if (lat == null || lng == null || wDp == null || hDp == null || wDp <= 0 || hDp <= 0 ||
1364
+ lat.isNaN() || lng.isNaN()) {
1365
+ promise.reject("E_INVALID_ARGS", "latitude, longitude, width, height (> 0) are required")
1366
+ return
1367
+ }
1368
+ val myLat = num("myLatitude")
1369
+ val myLng = num("myLongitude")
1370
+ if ((myLat == null) != (myLng == null)) {
1371
+ promise.reject("E_INVALID_ARGS", "myLatitude and myLongitude must be given together")
1372
+ return
1373
+ }
1374
+ if (activity == null) {
1375
+ promise.reject(E_NO_ACTIVITY, "Activity doesn't exist")
1376
+ return
1377
+ }
1378
+ val accent = str("themeColor")?.let { try { android.graphics.Color.parseColor(it) } catch (_: Exception) { null } }
1379
+ ?: android.graphics.Color.parseColor("#FF6B35")
1380
+ val dark = when (str("appearance")?.lowercase()) {
1381
+ "dark" -> true
1382
+ "light" -> false
1383
+ else -> (activity.resources.configuration.uiMode and android.content.res.Configuration.UI_MODE_NIGHT_MASK) ==
1384
+ android.content.res.Configuration.UI_MODE_NIGHT_YES
1385
+ }
1386
+ val wPx = Math.round(com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(wDp.toFloat())).coerceAtLeast(1)
1387
+ val hPx = Math.round(com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(hDp.toFloat())).coerceAtLeast(1)
1388
+ val maxOutW = num("maxWidth")?.takeIf { it > 0 }?.let { Math.round(it).toInt() }
1389
+ val maxOutH = num("maxHeight")?.takeIf { it > 0 }?.let { Math.round(it).toInt() }
1390
+ val req = LocationThumbnailRenderer.Request(
1391
+ lat = lat, lng = lng, widthPx = wPx, heightPx = hPx,
1392
+ density = activity.resources.displayMetrics.density,
1393
+ dark = dark,
1394
+ markerType = str("markerType"),
1395
+ placeCategory = str("placeCategory"),
1396
+ spanMeters = num("spanMeters"),
1397
+ accent = accent,
1398
+ myLat = myLat, myLng = myLng,
1399
+ // 출력 최대 크기 (0 이하·없음 → 기본 512)
1400
+ // 결과 최대 크기 (px). 둘 다 없으면 512 × 512, 하나만 주면 다른 쪽은 제한 없음(0)
1401
+ maxOutWidthPx = maxOutW ?: if (maxOutH != null) 0 else LocationThumbnailRenderer.DEFAULT_MAX_OUT_PX,
1402
+ maxOutHeightPx = maxOutH ?: if (maxOutW != null) 0 else LocationThumbnailRenderer.DEFAULT_MAX_OUT_PX,
1403
+ )
1404
+ com.facebook.react.bridge.UiThreadUtil.runOnUiThread {
1405
+ try {
1406
+ LocationThumbnailRenderer.render(activity, req) { r ->
1407
+ r.fold(
1408
+ onSuccess = { m ->
1409
+ val body = Arguments.createMap()
1410
+ body.putString("uri", m["uri"] as? String)
1411
+ body.putString("path", m["path"] as? String)
1412
+ body.putInt("width", (m["width"] as? Int) ?: wPx)
1413
+ body.putInt("height", (m["height"] as? Int) ?: hPx)
1414
+ body.putBoolean("cached", (m["cached"] as? Boolean) ?: false)
1415
+ promise.resolve(body)
1416
+ },
1417
+ onFailure = { e -> promise.reject("E_THUMBNAIL_FAILED", e.message ?: "Thumbnail render failed", e) }
1418
+ )
1419
+ }
1420
+ } catch (e: Exception) {
1421
+ promise.reject("E_THUMBNAIL_FAILED", e.message ?: "Thumbnail render failed", e)
1422
+ }
1423
+ }
1424
+ }
1425
+
1426
+ /** 지도 썸네일 캐시 비우기 */
1427
+ @ReactMethod
1428
+ override fun clearLocationThumbnailCache(promise: Promise) {
1429
+ CoroutineScope(Dispatchers.IO).launch {
1430
+ try { LocationThumbnailRenderer.clearCache(reactApplicationContext) } catch (_: Exception) {}
1431
+ promise.resolve(null)
1432
+ }
1433
+ }
1434
+
1435
+ // ── 역지오코딩 (기기 Geocoder, 디스크 캐시) ──
1436
+
1437
+ override fun reverseGeocode(options: ReadableMap, promise: Promise) {
1438
+ val lat = if (options.hasKey("latitude") && !options.isNull("latitude")) options.getDouble("latitude") else Double.NaN
1439
+ val lng = if (options.hasKey("longitude") && !options.isNull("longitude")) options.getDouble("longitude") else Double.NaN
1440
+ val lang = if (options.hasKey("languageCode") && !options.isNull("languageCode")) options.getString("languageCode") else null
1441
+ ReverseGeocoder.reverseGeocode(reactApplicationContext, lat, lng, lang) { r, e ->
1442
+ if (r != null) {
1443
+ val map = Arguments.createMap()
1444
+ r.address?.let { map.putString("address", it) } ?: map.putNull("address")
1445
+ r.shortAddress?.let { map.putString("shortAddress", it) } ?: map.putNull("shortAddress")
1446
+ map.putBoolean("cached", r.cached)
1447
+ promise.resolve(map)
1448
+ } else {
1449
+ promise.reject(e?.code ?: "E_GEOCODE_FAILED", e?.message ?: "Reverse geocoding failed")
1450
+ }
1451
+ }
1452
+ }
1453
+
1454
+ override fun clearReverseGeocodeCache(promise: Promise) {
1455
+ CoroutineScope(Dispatchers.IO).launch {
1456
+ try { ReverseGeocoder.clearCache(reactApplicationContext) } catch (_: Exception) {}
1457
+ promise.resolve(null)
1458
+ }
1459
+ }
1460
+ }