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
@@ -262,6 +262,21 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
262
262
  }
263
263
 
264
264
  pickImagesPromise = promise
265
+ // 이번 호출의 promise 를 붙잡아 둠 — 리스너가 모듈 필드를 읽으면, 시트가 열린 채 openGallery 를 다시 불렀을 때
266
+ // 이전 시트가 치워지며 오는 onDismiss 가 "새" promise 를 거부해 버림. 시트마다 자기 promise 를 한 번만 끝냄
267
+ val thisPromise = promise
268
+ var settled = false
269
+ val settle: ((Promise) -> Unit) -> Unit = { block ->
270
+ if (!settled) {
271
+ settled = true
272
+ if (pickImagesPromise === thisPromise) pickImagesPromise = null
273
+ block(thisPromise)
274
+ }
275
+ }
276
+ // 지난 호출이 남긴 오래된 임시 파일(24시간 지난 것) 정리 — 백그라운드
277
+ moduleScope.launch(Dispatchers.IO) {
278
+ try { TempFileCleaner.cleanStale(reactApplicationContext) } catch (_: Exception) {}
279
+ }
265
280
 
266
281
  val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
267
282
  Manifest.permission.READ_MEDIA_IMAGES
@@ -274,19 +289,70 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
274
289
  try {
275
290
  val bottomSheet = ImagePickerBottomSheet.newInstance(
276
291
  maxSelection, languageCode, enableEditor, profileMode, maxWidth, maxHeight, themeColor,
277
- forceMultiMode = forceMultiMode
292
+ forceMultiMode = forceMultiMode,
293
+ // 첨부 메뉴(갤러리 / 위치 / 파일). 기본 false — 켜지 않으면 기존과 같음
294
+ showAttachMenu = options.hasKey("showAttachMenu") && options.getBoolean("showAttachMenu"),
295
+ // (Android) 첨부 메뉴 바 아래 여백 (dp). 없으면 기본 12 / 8
296
+ attachMenuSinkIntoNav = if (options.hasKey("attachMenuSinkIntoNav") && !options.isNull("attachMenuSinkIntoNav"))
297
+ options.getDouble("attachMenuSinkIntoNav").toFloat() else com.rnattachmentkit.AttachMenuBarView.SINK_INTO_NAV_DP.toFloat(),
298
+ attachMenuMinBottomMargin = if (options.hasKey("attachMenuMinBottomMargin") && !options.isNull("attachMenuMinBottomMargin"))
299
+ options.getDouble("attachMenuMinBottomMargin").toFloat() else com.rnattachmentkit.AttachMenuBarView.BOTTOM_MARGIN_DP.toFloat(),
300
+ // 위치 보내기 때 지도 이미지 캡처 여부. 기본 true (기존 그대로). false 면 이미지 없이 위치 정보만
301
+ captureLocationImage = !(options.hasKey("captureLocationImage") && !options.isNull("captureLocationImage")) ||
302
+ options.getBoolean("captureLocationImage")
278
303
  )
304
+ // 위치 탭에서 보내기 → onLocationSelected 이벤트로 결과를 넘기고, openGallery 는 빈 배열로 끝냄
305
+ bottomSheet.setOnLocationSelectedListener { loc ->
306
+ val body = com.facebook.react.bridge.Arguments.createMap()
307
+ for ((k, v) in loc) {
308
+ when (v) {
309
+ is Int -> body.putInt(k, v)
310
+ is Long -> body.putDouble(k, v.toDouble())
311
+ is Double -> body.putDouble(k, v)
312
+ is Float -> body.putDouble(k, v.toDouble())
313
+ is Boolean -> body.putBoolean(k, v)
314
+ is String -> body.putString(k, v)
315
+ }
316
+ }
317
+ emitGalleryEvent("onLocationSelected", body)
318
+ settle { it.resolve(com.facebook.react.bridge.Arguments.createArray()) }
319
+ }
320
+ // 파일 탭에서 보내기 → onFilesSelected 이벤트 { files: [{uri, path, name, size, mimeType}], failed: [{name, error}] }, openGallery 는 빈 배열
321
+ bottomSheet.setOnFilesResultListener { files, failed ->
322
+ val arr = com.facebook.react.bridge.Arguments.createArray()
323
+ for (file in files) {
324
+ val m = com.facebook.react.bridge.Arguments.createMap()
325
+ for ((k, v) in file) {
326
+ when (v) {
327
+ is Double -> m.putDouble(k, v)
328
+ is String -> m.putString(k, v)
329
+ }
330
+ }
331
+ arr.pushMap(m)
332
+ }
333
+ val body = com.facebook.react.bridge.Arguments.createMap()
334
+ body.putArray("files", arr)
335
+ // 복사에 실패한 파일 (추가 필드 — files 는 예전과 같음)
336
+ val failedArr = com.facebook.react.bridge.Arguments.createArray()
337
+ for (f in failed) {
338
+ val m = com.facebook.react.bridge.Arguments.createMap()
339
+ m.putString("name", f["name"]?.toString() ?: "")
340
+ m.putString("error", f["error"]?.toString() ?: "")
341
+ failedArr.pushMap(m)
342
+ }
343
+ body.putArray("failed", failedArr)
344
+ emitGalleryEvent("onFilesSelected", body)
345
+ settle { it.resolve(com.facebook.react.bridge.Arguments.createArray()) }
346
+ }
279
347
  bottomSheet.setOnImagesSelectedListener { uris ->
280
348
  val result = WritableNativeArray()
281
349
  for (uri in uris) {
282
350
  result.pushString(uri.toString())
283
351
  }
284
- handleSelectedImages(result, pickImagesPromise)
285
- pickImagesPromise = null
352
+ settle { handleSelectedImages(result, it) }
286
353
  }
287
354
  bottomSheet.setOnCancelledListener {
288
- pickImagesPromise?.reject(E_PICKER_CANCELLED, "User cancelled image picker")
289
- pickImagesPromise = null
355
+ settle { it.reject(E_PICKER_CANCELLED, "User cancelled image picker") }
290
356
  }
291
357
 
292
358
  val existingFragment = fragmentActivity.supportFragmentManager.findFragmentByTag("ImagePickerBottomSheet")
@@ -299,8 +365,7 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
299
365
  .commitAllowingStateLoss()
300
366
 
301
367
  } catch (e: Exception) {
302
- pickImagesPromise?.reject(E_FAILED_TO_PICK, "Failed to open image picker: ${e.message}", e)
303
- pickImagesPromise = null
368
+ settle { it.reject(E_FAILED_TO_PICK, "Failed to open image picker: ${e.message}", e) }
304
369
  }
305
370
  }
306
371
  }
@@ -340,8 +405,7 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
340
405
  if (isGranted) {
341
406
  showSheet()
342
407
  } else {
343
- pickImagesPromise?.reject("E_PERMISSION_DENIED", "Permission denied")
344
- pickImagesPromise = null
408
+ settle { it.reject("E_PERMISSION_DENIED", "Permission denied") }
345
409
  }
346
410
  }
347
411
  return true
@@ -1142,4 +1206,242 @@ class RNAttachmentKitModule(reactContext: ReactApplicationContext) :
1142
1206
  .unregisterReceiver(imageDeletedReceiver)
1143
1207
  }
1144
1208
 
1209
+ // ── 지도 뷰어 (위치 메시지 → 지도) ──
1210
+
1211
+ private var mapViewer: java.lang.ref.WeakReference<MapViewerDialogFragment>? = null
1212
+ /** updateMapViewerSourceRect 로 갱신되는 닫힘 위치 (화면 px) */
1213
+ @Volatile private var mapViewerSourceRect: android.graphics.Rect? = null
1214
+
1215
+ /** RN measureInWindow 값(dp, 창 기준) → 화면 px Rect */
1216
+ private fun mapRectFromDp(map: ReadableMap?, activity: Activity): android.graphics.Rect? {
1217
+ if (map == null || !map.hasKey("x") || !map.hasKey("y") || !map.hasKey("width") || !map.hasKey("height")) return null
1218
+ val w = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(map.getDouble("width").toFloat())
1219
+ val h = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(map.getDouble("height").toFloat())
1220
+ if (w <= 0f || h <= 0f) return null
1221
+ val loc = IntArray(2)
1222
+ try { activity.window?.decorView?.getLocationOnScreen(loc) } catch (_: Exception) {}
1223
+ val x = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(map.getDouble("x").toFloat()) + loc[0]
1224
+ val y = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(map.getDouble("y").toFloat()) + loc[1]
1225
+ return android.graphics.Rect(Math.round(x), Math.round(y), Math.round(x + w), Math.round(y + h))
1226
+ }
1227
+
1228
+ /** 말풍선 이미지 → 로컬 파일 경로. file:// 는 그대로, http(s) 는 Glide 캐시 파일. 실패·지연 시 null (이미지 없이 엶) */
1229
+ private fun mapSourceImagePath(uri: String?): String? {
1230
+ if (uri.isNullOrBlank()) return null
1231
+ if (uri.startsWith("/")) return uri.takeIf { File(it).exists() }
1232
+ if (uri.startsWith("file://")) return Uri.parse(uri).path?.takeIf { File(it).exists() }
1233
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) return null
1234
+ return try {
1235
+ com.bumptech.glide.Glide.with(reactApplicationContext.applicationContext)
1236
+ .downloadOnly().load(uri).submit()
1237
+ .get(1500, java.util.concurrent.TimeUnit.MILLISECONDS)
1238
+ ?.absolutePath
1239
+ } catch (e: Exception) {
1240
+ android.util.Log.w("RNAttachmentKit", "openMapViewer: source image not loaded (${e.javaClass.simpleName})")
1241
+ null
1242
+ }
1243
+ }
1244
+
1245
+ @ReactMethod
1246
+ fun openMapViewer(options: ReadableMap, promise: Promise) {
1247
+ val activity = getCurrentActivity() as? FragmentActivity
1248
+ if (activity == null) {
1249
+ promise.reject("NO_ACTIVITY", "Activity is not a FragmentActivity")
1250
+ return
1251
+ }
1252
+ if (!options.hasKey("latitude") || !options.hasKey("longitude")) {
1253
+ promise.reject("ERROR", "latitude and longitude are required")
1254
+ return
1255
+ }
1256
+ fun str(key: String) = if (options.hasKey(key) && !options.isNull(key)) options.getString(key) else null
1257
+ fun num(key: String) = if (options.hasKey(key) && !options.isNull(key)) options.getDouble(key) else null
1258
+ val lat = options.getDouble("latitude")
1259
+ val lng = options.getDouble("longitude")
1260
+ val rect = mapRectFromDp(if (options.hasKey("sourceRect") && !options.isNull("sourceRect")) options.getMap("sourceRect") else null, activity)
1261
+ val imageUri = str("sourceImageUri")
1262
+ val themeColor = str("themeColor")?.let { try { android.graphics.Color.parseColor(it) } catch (_: Exception) { null } }
1263
+ val radiusPx = com.facebook.react.uimanager.PixelUtil.toPixelFromDIP((num("sourceCornerRadius") ?: 12.0).toFloat())
1264
+ val showDirections = if (options.hasKey("showDirections") && !options.isNull("showDirections")) options.getBoolean("showDirections") else true
1265
+
1266
+ CoroutineScope(Dispatchers.IO).launch {
1267
+ val imagePath = if (rect != null) mapSourceImagePath(imageUri) else null
1268
+ withContext(Dispatchers.Main) {
1269
+ if (activity.isFinishing || activity.isDestroyed || activity.supportFragmentManager.isStateSaved) {
1270
+ promise.reject("ERROR", "Activity is not available")
1271
+ return@withContext
1272
+ }
1273
+ try {
1274
+ mapViewerSourceRect = rect
1275
+ val f = MapViewerDialogFragment.show(
1276
+ activity, lat, lng,
1277
+ title = str("title"),
1278
+ address = str("address"),
1279
+ markerType = str("markerType"),
1280
+ themeColor = themeColor,
1281
+ languageCode = str("languageCode") ?: "en",
1282
+ showDirections = showDirections,
1283
+ sourceRect = rect,
1284
+ sourceImagePath = imagePath,
1285
+ sourceCornerRadius = radiusPx,
1286
+ spanMeters = num("spanMeters") ?: 0.0,
1287
+ appearance = str("appearance"),
1288
+ addressLanguageCode = str("addressLanguageCode"),
1289
+ )
1290
+ if (rect != null) f.sourceRectProvider = { mapViewerSourceRect }
1291
+ var settled = false
1292
+ f.onClosed = {
1293
+ if (!settled) { settled = true; promise.resolve(null) }
1294
+ mapViewerSourceRect = null
1295
+ mapViewer = null
1296
+ }
1297
+ mapViewer = java.lang.ref.WeakReference(f)
1298
+ } catch (e: Exception) {
1299
+ promise.reject("ERROR", e.message, e)
1300
+ }
1301
+ }
1302
+ }
1303
+ }
1304
+
1305
+ @ReactMethod
1306
+ fun updateMapViewerSourceRect(rect: ReadableMap, promise: Promise) {
1307
+ val activity = getCurrentActivity()
1308
+ if (mapViewer?.get() != null && activity != null) mapViewerSourceRect = mapRectFromDp(rect, activity)
1309
+ promise.resolve(null)
1310
+ }
1311
+
1312
+ /**
1313
+ * 위치 메시지 지도 썸네일을 이 기기에서 그림 (테마별 캐시). 결과 { uri, path, width, height(px), cached }
1314
+ * options: latitude, longitude, width, height (dp, 필수) / markerType, placeCategory, spanMeters, themeColor,
1315
+ * appearance ("light"/"dark"/"system"), myLatitude + myLongitude (둘 다 있을 때만)
1316
+ * aspect ("square" | "portrait" | "device"): 주면 width 만 필수, height 는 비율로 (square = 1:1,
1317
+ * portrait = 480:1040 보내던 이미지와 같은 9:19.5, device = 실제 화면 긴 변/짧은 변). 모르는 값이면 없는 것과 같음
1318
+ * maxWidth / maxHeight (px, 기본 512, 0 이하면 무시): 출력 WebP 가 이 안에 들어가게 비율대로 줄임
1319
+ * (k = min(density, maxWidth / widthDp, maxHeight / heightDp)) → 결과 width/height 는 출력 px
1320
+ */
1321
+ @ReactMethod
1322
+ fun renderLocationThumbnail(options: ReadableMap, promise: Promise) {
1323
+ fun str(key: String) = if (options.hasKey(key) && !options.isNull(key)) options.getString(key) else null
1324
+ fun num(key: String) = if (options.hasKey(key) && !options.isNull(key)) options.getDouble(key) else null
1325
+ val lat = num("latitude")
1326
+ val lng = num("longitude")
1327
+ val wDp = num("width")
1328
+ val activity = getCurrentActivity()
1329
+ val aspectRatio: Double? = when (str("aspect")?.lowercase()) {
1330
+ "square" -> 1.0
1331
+ "portrait" -> 1040.0 / 480.0
1332
+ "device" -> {
1333
+ // 실제 화면 크기 (세로 기준: 긴 변 / 짧은 변 → h > w)
1334
+ val dm = (activity ?: reactApplicationContext).resources.displayMetrics
1335
+ var sw = dm.widthPixels; var sh = dm.heightPixels
1336
+ if (activity != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
1337
+ try {
1338
+ val b = activity.windowManager.currentWindowMetrics.bounds
1339
+ if (b.width() > 0 && b.height() > 0) { sw = b.width(); sh = b.height() }
1340
+ } catch (_: Exception) {}
1341
+ }
1342
+ val lo = minOf(sw, sh); val hi = maxOf(sw, sh)
1343
+ if (lo > 0) hi.toDouble() / lo else 19.5 / 9.0
1344
+ }
1345
+ else -> null
1346
+ }
1347
+ val hDp = if (aspectRatio != null) wDp?.let { it * aspectRatio } else num("height")
1348
+ if (lat == null || lng == null || wDp == null || hDp == null || wDp <= 0 || hDp <= 0 ||
1349
+ lat.isNaN() || lng.isNaN()) {
1350
+ promise.reject("E_INVALID_ARGS", "latitude, longitude, width, height (> 0) are required")
1351
+ return
1352
+ }
1353
+ val myLat = num("myLatitude")
1354
+ val myLng = num("myLongitude")
1355
+ if ((myLat == null) != (myLng == null)) {
1356
+ promise.reject("E_INVALID_ARGS", "myLatitude and myLongitude must be given together")
1357
+ return
1358
+ }
1359
+ if (activity == null) {
1360
+ promise.reject(E_NO_ACTIVITY, "Activity doesn't exist")
1361
+ return
1362
+ }
1363
+ val accent = str("themeColor")?.let { try { android.graphics.Color.parseColor(it) } catch (_: Exception) { null } }
1364
+ ?: android.graphics.Color.parseColor("#FF6B35")
1365
+ val dark = when (str("appearance")?.lowercase()) {
1366
+ "dark" -> true
1367
+ "light" -> false
1368
+ else -> (activity.resources.configuration.uiMode and android.content.res.Configuration.UI_MODE_NIGHT_MASK) ==
1369
+ android.content.res.Configuration.UI_MODE_NIGHT_YES
1370
+ }
1371
+ val wPx = Math.round(com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(wDp.toFloat())).coerceAtLeast(1)
1372
+ val hPx = Math.round(com.facebook.react.uimanager.PixelUtil.toPixelFromDIP(hDp.toFloat())).coerceAtLeast(1)
1373
+ val maxOutW = num("maxWidth")?.takeIf { it > 0 }?.let { Math.round(it).toInt() }
1374
+ val maxOutH = num("maxHeight")?.takeIf { it > 0 }?.let { Math.round(it).toInt() }
1375
+ val req = LocationThumbnailRenderer.Request(
1376
+ lat = lat, lng = lng, widthPx = wPx, heightPx = hPx,
1377
+ density = activity.resources.displayMetrics.density,
1378
+ dark = dark,
1379
+ markerType = str("markerType"),
1380
+ placeCategory = str("placeCategory"),
1381
+ spanMeters = num("spanMeters"),
1382
+ accent = accent,
1383
+ myLat = myLat, myLng = myLng,
1384
+ // 출력 최대 크기 (0 이하·없음 → 기본 512)
1385
+ // 결과 최대 크기 (px). 둘 다 없으면 512 × 512, 하나만 주면 다른 쪽은 제한 없음(0)
1386
+ maxOutWidthPx = maxOutW ?: if (maxOutH != null) 0 else LocationThumbnailRenderer.DEFAULT_MAX_OUT_PX,
1387
+ maxOutHeightPx = maxOutH ?: if (maxOutW != null) 0 else LocationThumbnailRenderer.DEFAULT_MAX_OUT_PX,
1388
+ )
1389
+ com.facebook.react.bridge.UiThreadUtil.runOnUiThread {
1390
+ try {
1391
+ LocationThumbnailRenderer.render(activity, req) { r ->
1392
+ r.fold(
1393
+ onSuccess = { m ->
1394
+ val body = Arguments.createMap()
1395
+ body.putString("uri", m["uri"] as? String)
1396
+ body.putString("path", m["path"] as? String)
1397
+ body.putInt("width", (m["width"] as? Int) ?: wPx)
1398
+ body.putInt("height", (m["height"] as? Int) ?: hPx)
1399
+ body.putBoolean("cached", (m["cached"] as? Boolean) ?: false)
1400
+ promise.resolve(body)
1401
+ },
1402
+ onFailure = { e -> promise.reject("E_THUMBNAIL_FAILED", e.message ?: "Thumbnail render failed", e) }
1403
+ )
1404
+ }
1405
+ } catch (e: Exception) {
1406
+ promise.reject("E_THUMBNAIL_FAILED", e.message ?: "Thumbnail render failed", e)
1407
+ }
1408
+ }
1409
+ }
1410
+
1411
+ /** 지도 썸네일 캐시 비우기 */
1412
+ @ReactMethod
1413
+ fun clearLocationThumbnailCache(promise: Promise) {
1414
+ CoroutineScope(Dispatchers.IO).launch {
1415
+ try { LocationThumbnailRenderer.clearCache(reactApplicationContext) } catch (_: Exception) {}
1416
+ promise.resolve(null)
1417
+ }
1418
+ }
1419
+
1420
+ // ── 역지오코딩 (기기 Geocoder, 디스크 캐시) ──
1421
+
1422
+ @ReactMethod
1423
+ fun reverseGeocode(options: ReadableMap, promise: Promise) {
1424
+ val lat = if (options.hasKey("latitude") && !options.isNull("latitude")) options.getDouble("latitude") else Double.NaN
1425
+ val lng = if (options.hasKey("longitude") && !options.isNull("longitude")) options.getDouble("longitude") else Double.NaN
1426
+ val lang = if (options.hasKey("languageCode") && !options.isNull("languageCode")) options.getString("languageCode") else null
1427
+ ReverseGeocoder.reverseGeocode(reactApplicationContext, lat, lng, lang) { r, e ->
1428
+ if (r != null) {
1429
+ val map = Arguments.createMap()
1430
+ r.address?.let { map.putString("address", it) } ?: map.putNull("address")
1431
+ r.shortAddress?.let { map.putString("shortAddress", it) } ?: map.putNull("shortAddress")
1432
+ map.putBoolean("cached", r.cached)
1433
+ promise.resolve(map)
1434
+ } else {
1435
+ promise.reject(e?.code ?: "E_GEOCODE_FAILED", e?.message ?: "Reverse geocoding failed")
1436
+ }
1437
+ }
1438
+ }
1439
+
1440
+ @ReactMethod
1441
+ fun clearReverseGeocodeCache(promise: Promise) {
1442
+ CoroutineScope(Dispatchers.IO).launch {
1443
+ try { ReverseGeocoder.clearCache(reactApplicationContext) } catch (_: Exception) {}
1444
+ promise.resolve(null)
1445
+ }
1446
+ }
1145
1447
  }
@@ -41,6 +41,28 @@ RCT_EXTERN_METHOD(updateSourceRect:(NSDictionary *)options
41
41
  resolve:(RCTPromiseResolveBlock)resolve
42
42
  reject:(RCTPromiseRejectBlock)reject)
43
43
 
44
+ RCT_EXTERN_METHOD(openMapViewer:(NSDictionary *)options
45
+ resolve:(RCTPromiseResolveBlock)resolve
46
+ reject:(RCTPromiseRejectBlock)reject)
47
+
48
+ RCT_EXTERN_METHOD(updateMapViewerSourceRect:(NSDictionary *)rect
49
+ resolve:(RCTPromiseResolveBlock)resolve
50
+ reject:(RCTPromiseRejectBlock)reject)
51
+
52
+ RCT_EXTERN_METHOD(renderLocationThumbnail:(NSDictionary *)options
53
+ resolve:(RCTPromiseResolveBlock)resolve
54
+ reject:(RCTPromiseRejectBlock)reject)
55
+
56
+ RCT_EXTERN_METHOD(reverseGeocode:(NSDictionary *)options
57
+ resolve:(RCTPromiseResolveBlock)resolve
58
+ reject:(RCTPromiseRejectBlock)reject)
59
+
60
+ RCT_EXTERN_METHOD(clearReverseGeocodeCache:(RCTPromiseResolveBlock)resolve
61
+ reject:(RCTPromiseRejectBlock)reject)
62
+
63
+ RCT_EXTERN_METHOD(clearLocationThumbnailCache:(RCTPromiseResolveBlock)resolve
64
+ reject:(RCTPromiseRejectBlock)reject)
65
+
44
66
  RCT_EXTERN_METHOD(getDefaultAnimationConfig:(RCTPromiseResolveBlock)resolve
45
67
  reject:(RCTPromiseRejectBlock)reject)
46
68
 
@@ -101,6 +123,28 @@ RCT_EXTERN_METHOD(updateSourceRect:(NSDictionary *)options
101
123
  resolve:(RCTPromiseResolveBlock)resolve
102
124
  reject:(RCTPromiseRejectBlock)reject)
103
125
 
126
+ RCT_EXTERN_METHOD(openMapViewer:(NSDictionary *)options
127
+ resolve:(RCTPromiseResolveBlock)resolve
128
+ reject:(RCTPromiseRejectBlock)reject)
129
+
130
+ RCT_EXTERN_METHOD(updateMapViewerSourceRect:(NSDictionary *)rect
131
+ resolve:(RCTPromiseResolveBlock)resolve
132
+ reject:(RCTPromiseRejectBlock)reject)
133
+
134
+ RCT_EXTERN_METHOD(renderLocationThumbnail:(NSDictionary *)options
135
+ resolve:(RCTPromiseResolveBlock)resolve
136
+ reject:(RCTPromiseRejectBlock)reject)
137
+
138
+ RCT_EXTERN_METHOD(reverseGeocode:(NSDictionary *)options
139
+ resolve:(RCTPromiseResolveBlock)resolve
140
+ reject:(RCTPromiseRejectBlock)reject)
141
+
142
+ RCT_EXTERN_METHOD(clearReverseGeocodeCache:(RCTPromiseResolveBlock)resolve
143
+ reject:(RCTPromiseRejectBlock)reject)
144
+
145
+ RCT_EXTERN_METHOD(clearLocationThumbnailCache:(RCTPromiseResolveBlock)resolve
146
+ reject:(RCTPromiseRejectBlock)reject)
147
+
104
148
  RCT_EXTERN_METHOD(getDefaultAnimationConfig:(RCTPromiseResolveBlock)resolve
105
149
  reject:(RCTPromiseRejectBlock)reject)
106
150