@react-native-kakao/share 3.1.0 → 3.2.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 (32) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/RNCKakaoShare.podspec +0 -1
  3. package/android/build.gradle +1 -2
  4. package/android/src/main/java/net/mjstudio/rnkakao/share/RNCKakaoShareModule.kt +321 -12
  5. package/android/src/main/java/net/mjstudio/rnkakao/share/RNCKakaoShareTemplates.kt +5 -2
  6. package/ios/RNCKakaoShare.mm +64 -5
  7. package/ios/RNCKakaoShareManager.swift +315 -37
  8. package/lib/commonjs/index.js +140 -30
  9. package/lib/commonjs/index.js.map +1 -1
  10. package/lib/commonjs/index.web.js +199 -21
  11. package/lib/commonjs/index.web.js.map +1 -1
  12. package/lib/commonjs/spec/NativeKakaoShare.ts +77 -4
  13. package/lib/commonjs/validateSharePickerSettings.js +33 -0
  14. package/lib/commonjs/validateSharePickerSettings.js.map +1 -0
  15. package/lib/module/index.js +131 -30
  16. package/lib/module/index.js.map +1 -1
  17. package/lib/module/index.web.js +190 -21
  18. package/lib/module/index.web.js.map +1 -1
  19. package/lib/module/spec/NativeKakaoShare.ts +77 -4
  20. package/lib/module/validateSharePickerSettings.js +29 -0
  21. package/lib/module/validateSharePickerSettings.js.map +1 -0
  22. package/lib/typescript/src/index.d.ts +97 -37
  23. package/lib/typescript/src/index.d.ts.map +1 -1
  24. package/lib/typescript/src/spec/NativeKakaoShare.d.ts +72 -1
  25. package/lib/typescript/src/spec/NativeKakaoShare.d.ts.map +1 -1
  26. package/lib/typescript/src/validateSharePickerSettings.d.ts +6 -0
  27. package/lib/typescript/src/validateSharePickerSettings.d.ts.map +1 -0
  28. package/package.json +5 -5
  29. package/src/index.ts +278 -17
  30. package/src/index.web.ts +234 -38
  31. package/src/spec/NativeKakaoShare.ts +77 -4
  32. package/src/validateSharePickerSettings.ts +38 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,83 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 3.2.0
7
+
8
+ ### Minor Changes
9
+
10
+ - d117308: Add local image uploads on Android, iOS, and web with image metadata and secure URL options.
11
+ - 483fd00: Add URL scrap messages to My Chatroom and friends on Android, iOS, and web.
12
+ - bf027c0: Add URL-scraped message sharing with custom template arguments and native browser fallback on Android, iOS, and web.
13
+ - ff35f20: Expose native URL builders for default, custom, and scraped-page sharing without launching a sharing interface. Report unsupported URL building explicitly on web.
14
+
15
+ Encode Android template execution-parameter keys and values before the SDK serializes them, preserving special characters across sharing URLs, sharing, and message sending.
16
+ - 1efca29: Expose Kakao Talk sharing availability and Kakao Navi installation checks on Android and iOS, with explicit false results on web.
17
+ - ceafbcf: Add optional friend message results with successful recipients and failure details on Android, iOS, and web.
18
+ - 314d493: Expose validated share target picker settings across native and web share templates.
19
+ - 18df9b3: Expose native sharing template warnings and argument diagnostics without opening a sharing interface. Normalize missing diagnostic maps to empty objects and reject the unsupported API explicitly on web.
20
+
21
+ ### Patch Changes
22
+
23
+ - f466ffa: Normalize APIs without success data to resolve without a value across platforms, while preserving friend-message results.
24
+ - 61c270a: Remove unused React Native module dependencies so Channel, Social, and Share can each be installed with Core alone. Declare Share's Android Talk SDK dependency directly.
25
+ - Updated dependencies [d5c5fb8]
26
+ - Updated dependencies [3027207]
27
+ - Updated dependencies [dfd32ca]
28
+ - Updated dependencies [2b223bf]
29
+ - Updated dependencies [20c253b]
30
+ - Updated dependencies [67cd4e2]
31
+ - Updated dependencies [d81f9b7]
32
+ - Updated dependencies [3260517]
33
+ - @react-native-kakao/core@3.2.0
34
+
35
+ ## 3.1.0-next.2
36
+
37
+ ### Patch Changes
38
+
39
+ - f466ffa: Normalize APIs without success data to resolve without a value across platforms, while preserving friend-message results.
40
+ - @react-native-kakao/core@3.1.0-next.2
41
+
42
+ ## 3.1.0-next.1
43
+
44
+ ### Minor Changes
45
+
46
+ - d117308: Add local image uploads on Android, iOS, and web with image metadata and secure URL options.
47
+ - 483fd00: Add URL scrap messages to My Chatroom and friends on Android, iOS, and web.
48
+ - bf027c0: Add URL-scraped message sharing with custom template arguments and native browser fallback on Android, iOS, and web.
49
+ - ff35f20: Expose native URL builders for default, custom, and scraped-page sharing without launching a sharing interface. Report unsupported URL building explicitly on web.
50
+
51
+ Encode Android template execution-parameter keys and values before the SDK serializes them, preserving special characters across sharing URLs, sharing, and message sending.
52
+ - 1efca29: Expose Kakao Talk sharing availability and Kakao Navi installation checks on Android and iOS, with explicit false results on web.
53
+ - ceafbcf: Add optional friend message results with successful recipients and failure details on Android, iOS, and web.
54
+ - 314d493: Expose validated share target picker settings across native and web share templates.
55
+ - 18df9b3: Expose native sharing template warnings and argument diagnostics without opening a sharing interface. Normalize missing diagnostic maps to empty objects and reject the unsupported API explicitly on web.
56
+
57
+ ### Patch Changes
58
+
59
+ - 61c270a: Remove unused React Native module dependencies so Channel, Social, and Share can each be installed with Core alone. Declare Share's Android Talk SDK dependency directly.
60
+ - Updated dependencies [d5c5fb8]
61
+ - Updated dependencies [3027207]
62
+ - Updated dependencies [dfd32ca]
63
+ - Updated dependencies [2b223bf]
64
+ - Updated dependencies [67cd4e2]
65
+ - Updated dependencies [d81f9b7]
66
+ - @react-native-kakao/core@3.1.0-next.1
67
+
68
+ ## 3.1.0-next.0
69
+
70
+ ### Minor Changes
71
+
72
+ - ea4208b: Add image URL scraping with consistent image metadata on Android, iOS, and web.
73
+
74
+ ### Patch Changes
75
+
76
+ - 4772981: Use `reactApplicationContext.currentActivity` in the Android modules instead of the bare `currentActivity` getter, so they compile on React Native releases where `ReactContextBaseJavaModule` no longer exposes it to Kotlin subclasses.
77
+ - 71700aa: Align web SDK options, response shapes, timestamps, and session errors with native APIs.
78
+ - Updated dependencies [20c253b]
79
+ - Updated dependencies [3260517]
80
+ - Updated dependencies [71700aa]
81
+ - @react-native-kakao/core@3.1.0-next.0
82
+
6
83
  ## 3.1.0
7
84
 
8
85
  ### Minor Changes
@@ -16,6 +93,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
16
93
  - Updated dependencies [3afe0d3]
17
94
  - @react-native-kakao/core@3.1.0
18
95
 
96
+
19
97
  ## 3.0.0
20
98
 
21
99
  ### Major Changes
@@ -28,7 +28,6 @@ Pod::Spec.new do |s|
28
28
  end
29
29
 
30
30
  s.dependency 'RNCKakaoCore'
31
- s.dependency 'RNCKakaoSocial'
32
31
 
33
32
  # Kakao dependencies
34
33
  RNCKakaoSPM.dependency(s, sdk_version, %w[KakaoSDKCommon KakaoSDKAuth KakaoSDKUser KakaoSDKTemplate KakaoSDKTalk KakaoSDKShare])
@@ -1,7 +1,6 @@
1
1
  apply from: file('./../../core/android/package-json.gradle')
2
2
  def Versions = project.ext.RNCKakaoSdkVersions
3
3
  Project coreProject = project.ext.getWorkspaceProject("core")
4
- Project socialProject = project.ext.getWorkspaceProject("social")
5
4
 
6
5
  project.ext.applyPlugins()
7
6
 
@@ -40,6 +39,6 @@ dependencies {
40
39
  implementation "com.facebook.react:react-android"
41
40
 
42
41
  api coreProject
43
- api socialProject
44
42
  implementation "com.kakao.sdk:v2-share:${Versions.share}"
43
+ implementation "com.kakao.sdk:v2-talk:${Versions.talk}"
45
44
  }
@@ -1,6 +1,8 @@
1
1
  package net.mjstudio.rnkakao.share
2
2
 
3
3
  import android.content.ActivityNotFoundException
4
+ import android.net.Uri
5
+ import android.util.Base64
4
6
  import com.facebook.react.bridge.Promise
5
7
  import com.facebook.react.bridge.ReactApplicationContext
6
8
  import com.facebook.react.bridge.ReactMethod
@@ -9,6 +11,7 @@ import com.facebook.react.bridge.ReadableMap
9
11
  import com.kakao.sdk.common.util.KakaoCustomTabsClient
10
12
  import com.kakao.sdk.share.ShareClient
11
13
  import com.kakao.sdk.share.WebSharerClient
14
+ import com.kakao.sdk.share.model.ShareType
12
15
  import com.kakao.sdk.share.model.SharingResult
13
16
  import com.kakao.sdk.talk.TalkApiClient
14
17
  import com.kakao.sdk.talk.model.MessageSendResult
@@ -23,10 +26,221 @@ import net.mjstudio.rnkakao.core.util.onMain
23
26
  import net.mjstudio.rnkakao.core.util.pushStringList
24
27
  import net.mjstudio.rnkakao.core.util.rejectWith
25
28
  import net.mjstudio.rnkakao.core.util.toStringMap
29
+ import java.io.File
30
+ import kotlin.concurrent.thread
31
+
32
+ private const val NO_CUSTOM_TEMPLATE_ID = -1.0
26
33
 
27
34
  class RNCKakaoShareModule internal constructor(
28
35
  context: ReactApplicationContext,
29
36
  ) : NativeKakaoShareSpec(context) {
37
+ @ReactMethod
38
+ override fun isKakaoTalkSharingAvailable(promise: Promise) =
39
+ onMain {
40
+ promise.resolve(ShareClient.instance.isKakaoTalkSharingAvailable(reactApplicationContext))
41
+ }
42
+
43
+ @ReactMethod
44
+ override fun makeTemplateUrl(
45
+ templateType: String,
46
+ templateId: Double,
47
+ templateJson: ReadableMap,
48
+ templateArgs: ReadableMap,
49
+ serverCallbackArgs: ReadableMap,
50
+ promise: Promise,
51
+ ) = onMain {
52
+ runCatching {
53
+ val args = templateArgs.toStringMap()
54
+ val callbacks = serverCallbackArgs.toStringMap()
55
+ val url =
56
+ when (templateType) {
57
+ "custom" -> {
58
+ WebSharerClient.instance.makeCustomUrl(templateId.toLong(), args, callbacks)
59
+ }
60
+
61
+ "scrap" -> {
62
+ WebSharerClient.instance.makeScrapUrl(
63
+ requireNotNull(templateJson.getString("requestUrl")) { "requestUrl is required" },
64
+ templateId.takeUnless { it == NO_CUSTOM_TEMPLATE_ID }?.toLong(),
65
+ args,
66
+ callbacks,
67
+ )
68
+ }
69
+
70
+ else -> {
71
+ WebSharerClient.instance.makeDefaultUrl(
72
+ RNCKakaoShareTemplates.createDefaultTemplate(templateJson, templateType),
73
+ callbacks,
74
+ )
75
+ }
76
+ }
77
+ url.toString()
78
+ }.onSuccess { promise.resolve(it) }.onFailure { promise.rejectWith(it) }
79
+ }
80
+
81
+ @ReactMethod
82
+ override fun getTemplateDiagnostics(
83
+ templateType: String,
84
+ templateId: Double,
85
+ templateJson: ReadableMap,
86
+ templateArgs: ReadableMap,
87
+ serverCallbackArgs: ReadableMap,
88
+ pickerSettings: ReadableMap?,
89
+ promise: Promise,
90
+ ) = onMain {
91
+ runCatching {
92
+ val requestUrl =
93
+ if (templateType == "scrap") {
94
+ requireNotNull(templateJson.getString("requestUrl")) { "requestUrl is required" }
95
+ } else {
96
+ null
97
+ }
98
+ runShare(
99
+ templateType = templateType,
100
+ templateId = templateId.takeUnless { templateType != "custom" && it == NO_CUSTOM_TEMPLATE_ID },
101
+ defaultTemplate = templateJson,
102
+ useWebBrowserIfKakaoTalkNotAvailable = false,
103
+ templateArgs = templateArgs,
104
+ serverCallbackArgs = serverCallbackArgs,
105
+ pickerSettings = pickerSettings,
106
+ promise = promise,
107
+ requestUrl = requestUrl,
108
+ diagnosticsOnly = true,
109
+ )
110
+ }.onFailure { promise.rejectWith(it) }
111
+ }
112
+
113
+ @ReactMethod
114
+ override fun uploadImage(
115
+ imageUri: String,
116
+ secureResource: Boolean,
117
+ promise: Promise,
118
+ ) {
119
+ thread {
120
+ var temporaryFile: File? = null
121
+ try {
122
+ val maxImageBytes = 5 * 1024 * 1024
123
+ val imageSizeError = "Image must be no larger than 5 MB"
124
+
125
+ fun createTemporaryImageFile(): File =
126
+ File.createTempFile("kakao-share-image-", ".tmp", reactApplicationContext.cacheDir).also {
127
+ temporaryFile = it
128
+ }
129
+ val uri = Uri.parse(imageUri)
130
+ val file =
131
+ when (uri.scheme) {
132
+ null -> {
133
+ File(imageUri)
134
+ }
135
+
136
+ "file" -> {
137
+ File(requireNotNull(uri.path))
138
+ }
139
+
140
+ "content" -> {
141
+ val copiedFile = createTemporaryImageFile()
142
+ requireNotNull(reactApplicationContext.contentResolver.openInputStream(uri)).use { input ->
143
+ copiedFile.outputStream().use { output ->
144
+ val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
145
+ var total = 0
146
+ var count = input.read(buffer)
147
+ while (count != -1) {
148
+ total += count
149
+ require(total <= maxImageBytes) { imageSizeError }
150
+ output.write(buffer, 0, count)
151
+ count = input.read(buffer)
152
+ }
153
+ }
154
+ }
155
+ copiedFile
156
+ }
157
+
158
+ "data" -> {
159
+ require(imageUri.startsWith("data:image/") && imageUri.substringBefore(',').endsWith(";base64")) {
160
+ "Expected a base64 image data URI"
161
+ }
162
+ val maxEncodedImageLength = ((maxImageBytes + 2) / 3) * 4
163
+ val encodedImage =
164
+ buildString {
165
+ for (index in imageUri.indexOf(',') + 1 until imageUri.length) {
166
+ val character = imageUri[index]
167
+ if (!character.isWhitespace()) {
168
+ require(length < maxEncodedImageLength) { imageSizeError }
169
+ append(character)
170
+ }
171
+ }
172
+ }
173
+ val bytes = Base64.decode(encodedImage, Base64.DEFAULT)
174
+ require(bytes.size <= maxImageBytes) { imageSizeError }
175
+ val decodedFile = createTemporaryImageFile()
176
+ decodedFile.writeBytes(bytes)
177
+ decodedFile
178
+ }
179
+
180
+ else -> {
181
+ throw IllegalArgumentException("Expected a local file, content, or base64 image data URI")
182
+ }
183
+ }
184
+ require(file.isFile && file.canRead()) { "Image file cannot be read" }
185
+ require(file.length() <= maxImageBytes) { imageSizeError }
186
+ ShareClient.instance.uploadImage(file, secureResource) { result, error ->
187
+ temporaryFile?.delete()
188
+ if (error != null) {
189
+ promise.rejectWith(error)
190
+ } else if (result != null) {
191
+ val original = result.infos.original
192
+ promise.resolve(
193
+ argMap().apply {
194
+ putMap(
195
+ "infos",
196
+ argMap().apply {
197
+ putMap(
198
+ "original",
199
+ argMap().apply {
200
+ putString("url", original.url)
201
+ putString("contentType", original.contentType)
202
+ putDouble("length", original.length.toDouble())
203
+ putDouble("width", original.width.toDouble())
204
+ putDouble("height", original.height.toDouble())
205
+ },
206
+ )
207
+ },
208
+ )
209
+ },
210
+ )
211
+ } else {
212
+ promise.rejectWith(RNCKakaoResponseNotFoundException("imageUploadResult"))
213
+ }
214
+ }
215
+ } catch (error: Exception) {
216
+ temporaryFile?.delete()
217
+ promise.rejectWith(error)
218
+ }
219
+ }
220
+ }
221
+
222
+ @ReactMethod
223
+ override fun shareScrapTemplate(
224
+ requestUrl: String,
225
+ useWebBrowserIfKakaoTalkNotAvailable: Boolean,
226
+ templateId: Double?,
227
+ templateArgs: ReadableMap?,
228
+ serverCallbackArgs: ReadableMap?,
229
+ pickerSettings: ReadableMap?,
230
+ promise: Promise,
231
+ ) = onMain {
232
+ runShare(
233
+ templateType = "scrap",
234
+ templateId = templateId,
235
+ useWebBrowserIfKakaoTalkNotAvailable = useWebBrowserIfKakaoTalkNotAvailable,
236
+ templateArgs = templateArgs,
237
+ serverCallbackArgs = serverCallbackArgs,
238
+ pickerSettings = pickerSettings,
239
+ requestUrl = requestUrl,
240
+ promise = promise,
241
+ )
242
+ }
243
+
30
244
  @ReactMethod
31
245
  override fun scrapImage(
32
246
  imageUrl: String,
@@ -73,6 +287,7 @@ class RNCKakaoShareModule internal constructor(
73
287
  useWebBrowserIfKakaoTalkNotAvailable: Boolean,
74
288
  templateArgs: ReadableMap,
75
289
  serverCallbackArgs: ReadableMap,
290
+ pickerSettings: ReadableMap?,
76
291
  promise: Promise,
77
292
  ) = onMain {
78
293
  if (templateType == "custom") {
@@ -84,8 +299,36 @@ class RNCKakaoShareModule internal constructor(
84
299
  useWebBrowserIfKakaoTalkNotAvailable,
85
300
  templateArgs,
86
301
  serverCallbackArgs,
302
+ pickerSettings,
87
303
  promise,
88
304
  )
305
+ } else if (templateType == "scrap") {
306
+ val requestUrl = templateJson.toHashMap()["requestUrl"] as? String
307
+ if (requestUrl == null) {
308
+ promise.rejectWith(RNCKakaoUnknownException("requestUrl is required for scrap messages"))
309
+ return@onMain
310
+ }
311
+ val customTemplateId = templateId.takeUnless { it == NO_CUSTOM_TEMPLATE_ID }
312
+ when (sendType) {
313
+ "send-me" -> {
314
+ runSendToMe(templateType, customTemplateId, templateArgs = templateArgs, promise = promise, requestUrl = requestUrl)
315
+ }
316
+
317
+ "send-friend" -> {
318
+ runSendToFriends(
319
+ templateType,
320
+ receiverUuids.filterIsInstance<String>(),
321
+ customTemplateId,
322
+ templateArgs = templateArgs,
323
+ promise = promise,
324
+ requestUrl = requestUrl,
325
+ )
326
+ }
327
+
328
+ else -> {
329
+ promise.rejectWith(RNCKakaoUnknownException("Unknown sendType for scrap messages: $sendType"))
330
+ }
331
+ }
89
332
  } else {
90
333
  shareOrSendDefaultTemplate(
91
334
  sendType,
@@ -94,6 +337,7 @@ class RNCKakaoShareModule internal constructor(
94
337
  receiverUuids = receiverUuids.filterIsInstance<String>(),
95
338
  useWebBrowserIfKakaoTalkNotAvailable,
96
339
  serverCallbackArgs,
340
+ pickerSettings,
97
341
  promise,
98
342
  )
99
343
  }
@@ -107,6 +351,7 @@ class RNCKakaoShareModule internal constructor(
107
351
  useWebBrowserIfKakaoTalkNotAvailable: Boolean,
108
352
  templateArgs: ReadableMap?,
109
353
  serverCallbackArgs: ReadableMap?,
354
+ pickerSettings: ReadableMap?,
110
355
  promise: Promise,
111
356
  ) = when (sendType) {
112
357
  "share" -> {
@@ -117,6 +362,7 @@ class RNCKakaoShareModule internal constructor(
117
362
  useWebBrowserIfKakaoTalkNotAvailable,
118
363
  templateArgs,
119
364
  serverCallbackArgs,
365
+ pickerSettings,
120
366
  promise,
121
367
  )
122
368
  }
@@ -141,6 +387,7 @@ class RNCKakaoShareModule internal constructor(
141
387
  receiverUuids: List<String>,
142
388
  useWebBrowserIfKakaoTalkNotAvailable: Boolean,
143
389
  serverCallbackArgs: ReadableMap?,
390
+ pickerSettings: ReadableMap?,
144
391
  promise: Promise,
145
392
  ) = when (sendType) {
146
393
  "share" -> {
@@ -151,6 +398,7 @@ class RNCKakaoShareModule internal constructor(
151
398
  useWebBrowserIfKakaoTalkNotAvailable,
152
399
  argMap(),
153
400
  serverCallbackArgs,
401
+ pickerSettings,
154
402
  promise,
155
403
  )
156
404
  }
@@ -174,15 +422,18 @@ class RNCKakaoShareModule internal constructor(
174
422
  defaultTemplate: ReadableMap? = null,
175
423
  templateArgs: ReadableMap? = null,
176
424
  promise: Promise,
425
+ requestUrl: String? = null,
177
426
  ) {
178
427
  val callback = { e: Throwable? ->
179
428
  if (e != null) {
180
429
  promise.rejectWith(e)
181
430
  } else {
182
- promise.resolve(42)
431
+ promise.resolve(null)
183
432
  }
184
433
  }
185
- if (templateId != null) {
434
+ if (requestUrl != null) {
435
+ TalkApiClient.instance.sendScrapMemo(requestUrl, templateId?.toLong(), templateArgs?.toStringMap(), callback)
436
+ } else if (templateId != null) {
186
437
  TalkApiClient.instance.sendCustomMemo(templateId.toLong(), templateArgs?.toStringMap(), callback)
187
438
  } else if (defaultTemplate != null) {
188
439
  runCatching {
@@ -203,6 +454,7 @@ class RNCKakaoShareModule internal constructor(
203
454
  defaultTemplate: ReadableMap? = null,
204
455
  templateArgs: ReadableMap? = null,
205
456
  promise: Promise,
457
+ requestUrl: String? = null,
206
458
  ) {
207
459
  val callback = { messageSendResult: MessageSendResult?, e: Throwable? ->
208
460
  if (e != null) {
@@ -210,10 +462,30 @@ class RNCKakaoShareModule internal constructor(
210
462
  } else if (messageSendResult == null) {
211
463
  promise.rejectWith(RNCKakaoResponseNotFoundException("messageSendResult"))
212
464
  } else {
213
- promise.resolve(argArr().pushStringList(messageSendResult.successfulReceiverUuids ?: listOf()))
465
+ promise.resolve(
466
+ argMap().apply {
467
+ putArray("successfulReceiverUuids", argArr().pushStringList(messageSendResult.successfulReceiverUuids ?: listOf()))
468
+ putArray(
469
+ "failureInfos",
470
+ argArr().apply {
471
+ messageSendResult.failureInfos?.forEach { failure ->
472
+ pushMap(
473
+ argMap().apply {
474
+ putInt("code", failure.code)
475
+ putString("msg", failure.msg)
476
+ putArray("receiverUuids", argArr().pushStringList(failure.receiverUuids))
477
+ },
478
+ )
479
+ }
480
+ },
481
+ )
482
+ },
483
+ )
214
484
  }
215
485
  }
216
- if (templateId != null) {
486
+ if (requestUrl != null) {
487
+ TalkApiClient.instance.sendScrapMessage(receiverUuids, requestUrl, templateId?.toLong(), templateArgs?.toStringMap(), callback)
488
+ } else if (templateId != null) {
217
489
  TalkApiClient.instance.sendCustomMessage(receiverUuids, templateId.toLong(), templateArgs?.toStringMap(), callback)
218
490
  } else if (defaultTemplate != null) {
219
491
  runCatching {
@@ -235,9 +507,12 @@ class RNCKakaoShareModule internal constructor(
235
507
  useWebBrowserIfKakaoTalkNotAvailable: Boolean,
236
508
  templateArgs: ReadableMap? = null,
237
509
  serverCallbackArgs: ReadableMap? = null,
510
+ pickerSettings: ReadableMap?,
238
511
  promise: Promise,
512
+ requestUrl: String? = null,
513
+ diagnosticsOnly: Boolean = false,
239
514
  ) {
240
- val context = reactApplicationContext.currentActivity
515
+ val context = if (diagnosticsOnly) reactApplicationContext else reactApplicationContext.currentActivity
241
516
  if (context == null) {
242
517
  promise.rejectWith(ActivityNotFoundException())
243
518
  return
@@ -247,20 +522,45 @@ class RNCKakaoShareModule internal constructor(
247
522
  if (error != null) {
248
523
  promise.rejectWith(error)
249
524
  } else if (sharingResult != null) {
250
- context.startActivity(sharingResult.intent)
251
- promise.resolve(42)
525
+ if (diagnosticsOnly) {
526
+ promise.resolve(
527
+ argMap().apply {
528
+ putMap("warningMsg", argMap().apply { sharingResult.warningMsg.forEach { (key, value) -> putString(key, value) } })
529
+ putMap("argumentMsg", argMap().apply { sharingResult.argumentMsg.forEach { (key, value) -> putString(key, value) } })
530
+ },
531
+ )
532
+ } else {
533
+ context.startActivity(sharingResult.intent)
534
+ promise.resolve(null)
535
+ }
252
536
  } else {
253
- promise.resolve(42)
537
+ promise.rejectWith(RNCKakaoResponseNotFoundException("sharingResult"))
254
538
  }
255
539
  }
256
540
 
257
541
  if (ShareClient.instance.isKakaoTalkSharingAvailable(context)) {
258
- if (templateId != null) {
542
+ val pickerType = pickerSettings?.takeIf { it.hasKey("type") }?.getString("type")
543
+ val shareType = ShareType.entries.firstOrNull { it.value == pickerType }
544
+ val limit = pickerSettings?.takeIf { it.hasKey("limit") }?.getDouble("limit")?.toInt()
545
+ if (requestUrl != null) {
546
+ ShareClient.instance.shareScrap(
547
+ context,
548
+ requestUrl,
549
+ templateId = templateId?.toLong(),
550
+ templateArgs = templateArgs?.toStringMap(),
551
+ serverCallbackArgs = serverCallbackArgs?.toStringMap(),
552
+ shareType = shareType,
553
+ limit = limit,
554
+ callback = callback,
555
+ )
556
+ } else if (templateId != null) {
259
557
  ShareClient.instance.shareCustom(
260
558
  context,
261
559
  templateId.toLong(),
262
560
  templateArgs = templateArgs?.toStringMap(),
263
561
  serverCallbackArgs = serverCallbackArgs?.toStringMap(),
562
+ shareType = shareType,
563
+ limit = limit,
264
564
  callback = callback,
265
565
  )
266
566
  } else if (defaultTemplate != null) {
@@ -269,6 +569,8 @@ class RNCKakaoShareModule internal constructor(
269
569
  context,
270
570
  RNCKakaoShareTemplates.createDefaultTemplate(defaultTemplate, templateType),
271
571
  serverCallbackArgs = serverCallbackArgs?.toStringMap(),
572
+ shareType = shareType,
573
+ limit = limit,
272
574
  callback = callback,
273
575
  )
274
576
  }.onFailure {
@@ -278,7 +580,14 @@ class RNCKakaoShareModule internal constructor(
278
580
  } else if (useWebBrowserIfKakaoTalkNotAvailable) {
279
581
  runCatching {
280
582
  val sharerUrl =
281
- if (templateId != null) {
583
+ if (requestUrl != null) {
584
+ WebSharerClient.instance.makeScrapUrl(
585
+ requestUrl,
586
+ templateId = templateId?.toLong(),
587
+ templateArgs = templateArgs?.toStringMap(),
588
+ serverCallbackArgs = serverCallbackArgs?.toStringMap(),
589
+ )
590
+ } else if (templateId != null) {
282
591
  WebSharerClient.instance.makeCustomUrl(
283
592
  templateId.toLong(),
284
593
  templateArgs = templateArgs?.toStringMap(),
@@ -297,13 +606,13 @@ class RNCKakaoShareModule internal constructor(
297
606
  }
298
607
  try {
299
608
  KakaoCustomTabsClient.openWithDefault(context, sharerUrl)
300
- promise.resolve(42)
609
+ promise.resolve(null)
301
610
  return
302
611
  } catch (_: UnsupportedOperationException) {
303
612
  }
304
613
  try {
305
614
  KakaoCustomTabsClient.open(context, sharerUrl)
306
- promise.resolve(42)
615
+ promise.resolve(null)
307
616
  return
308
617
  } catch (_: ActivityNotFoundException) {
309
618
  }
@@ -1,5 +1,6 @@
1
1
  package net.mjstudio.rnkakao.share
2
2
 
3
+ import android.net.Uri
3
4
  import com.facebook.react.bridge.ReadableMap
4
5
  import com.kakao.sdk.template.model.Button
5
6
  import com.kakao.sdk.template.model.CalendarTemplate
@@ -41,10 +42,12 @@ object RNCKakaoShareTemplates {
41
42
  Link(
42
43
  webUrl = getString("webUrl"),
43
44
  mobileWebUrl = getString("mobileWebUrl"),
44
- androidExecutionParams = getMap("androidExecutionParams")?.toStringMap(),
45
- iosExecutionParams = getMap("iosExecutionParams")?.toStringMap(),
45
+ androidExecutionParams = getMap("androidExecutionParams")?.toExecutionParams(),
46
+ iosExecutionParams = getMap("iosExecutionParams")?.toExecutionParams(),
46
47
  )
47
48
 
49
+ private fun ReadableMap.toExecutionParams() = toStringMap().entries.associate { (key, value) -> Uri.encode(key) to Uri.encode(value) }
50
+
48
51
  private fun ReadableMap.toContent() =
49
52
  Content(
50
53
  title = getString("title"),