@react-native-kakao/share 3.0.0 → 3.1.0-next.1

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 +41 -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 +359 -11
  5. package/android/src/main/java/net/mjstudio/rnkakao/share/RNCKakaoShareTemplates.kt +5 -2
  6. package/ios/RNCKakaoShare.mm +69 -5
  7. package/ios/RNCKakaoShareManager.swift +349 -36
  8. package/lib/commonjs/index.js +145 -29
  9. package/lib/commonjs/index.js.map +1 -1
  10. package/lib/commonjs/index.web.js +267 -48
  11. package/lib/commonjs/index.web.js.map +1 -1
  12. package/lib/commonjs/spec/NativeKakaoShare.ts +89 -3
  13. package/lib/commonjs/validateSharePickerSettings.js +33 -0
  14. package/lib/commonjs/validateSharePickerSettings.js.map +1 -0
  15. package/lib/module/index.js +135 -29
  16. package/lib/module/index.js.map +1 -1
  17. package/lib/module/index.web.js +258 -49
  18. package/lib/module/index.web.js.map +1 -1
  19. package/lib/module/spec/NativeKakaoShare.ts +89 -3
  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 +88 -23
  23. package/lib/typescript/src/index.d.ts.map +1 -1
  24. package/lib/typescript/src/spec/NativeKakaoShare.d.ts +84 -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 +271 -15
  30. package/src/index.web.ts +305 -62
  31. package/src/spec/NativeKakaoShare.ts +89 -3
  32. package/src/validateSharePickerSettings.ts +38 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,47 @@
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.1.0-next.1
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
+ - 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.
24
+ - Updated dependencies [d5c5fb8]
25
+ - Updated dependencies [3027207]
26
+ - Updated dependencies [dfd32ca]
27
+ - Updated dependencies [2b223bf]
28
+ - Updated dependencies [67cd4e2]
29
+ - Updated dependencies [d81f9b7]
30
+ - @react-native-kakao/core@3.1.0-next.1
31
+
32
+ ## 3.1.0-next.0
33
+
34
+ ### Minor Changes
35
+
36
+ - ea4208b: Add image URL scraping with consistent image metadata on Android, iOS, and web.
37
+
38
+ ### Patch Changes
39
+
40
+ - 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.
41
+ - 71700aa: Align web SDK options, response shapes, timestamps, and session errors with native APIs.
42
+ - Updated dependencies [20c253b]
43
+ - Updated dependencies [3260517]
44
+ - Updated dependencies [71700aa]
45
+ - @react-native-kakao/core@3.1.0-next.0
46
+
6
47
  ## 3.0.0
7
48
 
8
49
  ### 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,257 @@ 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
+
244
+ @ReactMethod
245
+ override fun scrapImage(
246
+ imageUrl: String,
247
+ secureResource: Boolean,
248
+ promise: Promise,
249
+ ) = onMain {
250
+ ShareClient.instance.scrapImage(imageUrl, secureResource) { result, error ->
251
+ if (error != null) {
252
+ promise.rejectWith(error)
253
+ } else if (result != null) {
254
+ val original = result.infos.original
255
+ promise.resolve(
256
+ argMap().apply {
257
+ putMap(
258
+ "infos",
259
+ argMap().apply {
260
+ putMap(
261
+ "original",
262
+ argMap().apply {
263
+ putString("url", original.url)
264
+ putString("contentType", original.contentType)
265
+ putDouble("length", original.length.toDouble())
266
+ putDouble("width", original.width.toDouble())
267
+ putDouble("height", original.height.toDouble())
268
+ },
269
+ )
270
+ },
271
+ )
272
+ },
273
+ )
274
+ } else {
275
+ promise.rejectWith(RNCKakaoResponseNotFoundException("imageUploadResult"))
276
+ }
277
+ }
278
+ }
279
+
30
280
  @ReactMethod
31
281
  override fun shareOrSendMeOrSendFriendOrWhatever(
32
282
  sendType: String,
@@ -37,6 +287,7 @@ class RNCKakaoShareModule internal constructor(
37
287
  useWebBrowserIfKakaoTalkNotAvailable: Boolean,
38
288
  templateArgs: ReadableMap,
39
289
  serverCallbackArgs: ReadableMap,
290
+ pickerSettings: ReadableMap?,
40
291
  promise: Promise,
41
292
  ) = onMain {
42
293
  if (templateType == "custom") {
@@ -48,8 +299,36 @@ class RNCKakaoShareModule internal constructor(
48
299
  useWebBrowserIfKakaoTalkNotAvailable,
49
300
  templateArgs,
50
301
  serverCallbackArgs,
302
+ pickerSettings,
51
303
  promise,
52
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
+ }
53
332
  } else {
54
333
  shareOrSendDefaultTemplate(
55
334
  sendType,
@@ -58,6 +337,7 @@ class RNCKakaoShareModule internal constructor(
58
337
  receiverUuids = receiverUuids.filterIsInstance<String>(),
59
338
  useWebBrowserIfKakaoTalkNotAvailable,
60
339
  serverCallbackArgs,
340
+ pickerSettings,
61
341
  promise,
62
342
  )
63
343
  }
@@ -71,6 +351,7 @@ class RNCKakaoShareModule internal constructor(
71
351
  useWebBrowserIfKakaoTalkNotAvailable: Boolean,
72
352
  templateArgs: ReadableMap?,
73
353
  serverCallbackArgs: ReadableMap?,
354
+ pickerSettings: ReadableMap?,
74
355
  promise: Promise,
75
356
  ) = when (sendType) {
76
357
  "share" -> {
@@ -81,6 +362,7 @@ class RNCKakaoShareModule internal constructor(
81
362
  useWebBrowserIfKakaoTalkNotAvailable,
82
363
  templateArgs,
83
364
  serverCallbackArgs,
365
+ pickerSettings,
84
366
  promise,
85
367
  )
86
368
  }
@@ -105,6 +387,7 @@ class RNCKakaoShareModule internal constructor(
105
387
  receiverUuids: List<String>,
106
388
  useWebBrowserIfKakaoTalkNotAvailable: Boolean,
107
389
  serverCallbackArgs: ReadableMap?,
390
+ pickerSettings: ReadableMap?,
108
391
  promise: Promise,
109
392
  ) = when (sendType) {
110
393
  "share" -> {
@@ -115,6 +398,7 @@ class RNCKakaoShareModule internal constructor(
115
398
  useWebBrowserIfKakaoTalkNotAvailable,
116
399
  argMap(),
117
400
  serverCallbackArgs,
401
+ pickerSettings,
118
402
  promise,
119
403
  )
120
404
  }
@@ -138,6 +422,7 @@ class RNCKakaoShareModule internal constructor(
138
422
  defaultTemplate: ReadableMap? = null,
139
423
  templateArgs: ReadableMap? = null,
140
424
  promise: Promise,
425
+ requestUrl: String? = null,
141
426
  ) {
142
427
  val callback = { e: Throwable? ->
143
428
  if (e != null) {
@@ -146,7 +431,9 @@ class RNCKakaoShareModule internal constructor(
146
431
  promise.resolve(42)
147
432
  }
148
433
  }
149
- if (templateId != null) {
434
+ if (requestUrl != null) {
435
+ TalkApiClient.instance.sendScrapMemo(requestUrl, templateId?.toLong(), templateArgs?.toStringMap(), callback)
436
+ } else if (templateId != null) {
150
437
  TalkApiClient.instance.sendCustomMemo(templateId.toLong(), templateArgs?.toStringMap(), callback)
151
438
  } else if (defaultTemplate != null) {
152
439
  runCatching {
@@ -167,6 +454,7 @@ class RNCKakaoShareModule internal constructor(
167
454
  defaultTemplate: ReadableMap? = null,
168
455
  templateArgs: ReadableMap? = null,
169
456
  promise: Promise,
457
+ requestUrl: String? = null,
170
458
  ) {
171
459
  val callback = { messageSendResult: MessageSendResult?, e: Throwable? ->
172
460
  if (e != null) {
@@ -174,10 +462,30 @@ class RNCKakaoShareModule internal constructor(
174
462
  } else if (messageSendResult == null) {
175
463
  promise.rejectWith(RNCKakaoResponseNotFoundException("messageSendResult"))
176
464
  } else {
177
- 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
+ )
178
484
  }
179
485
  }
180
- if (templateId != null) {
486
+ if (requestUrl != null) {
487
+ TalkApiClient.instance.sendScrapMessage(receiverUuids, requestUrl, templateId?.toLong(), templateArgs?.toStringMap(), callback)
488
+ } else if (templateId != null) {
181
489
  TalkApiClient.instance.sendCustomMessage(receiverUuids, templateId.toLong(), templateArgs?.toStringMap(), callback)
182
490
  } else if (defaultTemplate != null) {
183
491
  runCatching {
@@ -199,32 +507,63 @@ class RNCKakaoShareModule internal constructor(
199
507
  useWebBrowserIfKakaoTalkNotAvailable: Boolean,
200
508
  templateArgs: ReadableMap? = null,
201
509
  serverCallbackArgs: ReadableMap? = null,
510
+ pickerSettings: ReadableMap?,
202
511
  promise: Promise,
512
+ requestUrl: String? = null,
513
+ diagnosticsOnly: Boolean = false,
203
514
  ) {
204
- val context = currentActivity
515
+ val context = if (diagnosticsOnly) reactApplicationContext else reactApplicationContext.currentActivity
205
516
  if (context == null) {
206
517
  promise.rejectWith(ActivityNotFoundException())
207
518
  return
208
519
  }
209
520
 
521
+ val result = if (requestUrl != null) null else 42
210
522
  val callback = { sharingResult: SharingResult?, error: Throwable? ->
211
523
  if (error != null) {
212
524
  promise.rejectWith(error)
213
525
  } else if (sharingResult != null) {
214
- context.startActivity(sharingResult.intent)
215
- promise.resolve(42)
526
+ if (diagnosticsOnly) {
527
+ promise.resolve(
528
+ argMap().apply {
529
+ putMap("warningMsg", argMap().apply { sharingResult.warningMsg.forEach { (key, value) -> putString(key, value) } })
530
+ putMap("argumentMsg", argMap().apply { sharingResult.argumentMsg.forEach { (key, value) -> putString(key, value) } })
531
+ },
532
+ )
533
+ } else {
534
+ context.startActivity(sharingResult.intent)
535
+ promise.resolve(result)
536
+ }
537
+ } else if (diagnosticsOnly || requestUrl != null) {
538
+ promise.rejectWith(RNCKakaoResponseNotFoundException("sharingResult"))
216
539
  } else {
217
- promise.resolve(42)
540
+ promise.resolve(result)
218
541
  }
219
542
  }
220
543
 
221
544
  if (ShareClient.instance.isKakaoTalkSharingAvailable(context)) {
222
- if (templateId != null) {
545
+ val pickerType = pickerSettings?.takeIf { it.hasKey("type") }?.getString("type")
546
+ val shareType = ShareType.entries.firstOrNull { it.value == pickerType }
547
+ val limit = pickerSettings?.takeIf { it.hasKey("limit") }?.getDouble("limit")?.toInt()
548
+ if (requestUrl != null) {
549
+ ShareClient.instance.shareScrap(
550
+ context,
551
+ requestUrl,
552
+ templateId = templateId?.toLong(),
553
+ templateArgs = templateArgs?.toStringMap(),
554
+ serverCallbackArgs = serverCallbackArgs?.toStringMap(),
555
+ shareType = shareType,
556
+ limit = limit,
557
+ callback = callback,
558
+ )
559
+ } else if (templateId != null) {
223
560
  ShareClient.instance.shareCustom(
224
561
  context,
225
562
  templateId.toLong(),
226
563
  templateArgs = templateArgs?.toStringMap(),
227
564
  serverCallbackArgs = serverCallbackArgs?.toStringMap(),
565
+ shareType = shareType,
566
+ limit = limit,
228
567
  callback = callback,
229
568
  )
230
569
  } else if (defaultTemplate != null) {
@@ -233,6 +572,8 @@ class RNCKakaoShareModule internal constructor(
233
572
  context,
234
573
  RNCKakaoShareTemplates.createDefaultTemplate(defaultTemplate, templateType),
235
574
  serverCallbackArgs = serverCallbackArgs?.toStringMap(),
575
+ shareType = shareType,
576
+ limit = limit,
236
577
  callback = callback,
237
578
  )
238
579
  }.onFailure {
@@ -242,7 +583,14 @@ class RNCKakaoShareModule internal constructor(
242
583
  } else if (useWebBrowserIfKakaoTalkNotAvailable) {
243
584
  runCatching {
244
585
  val sharerUrl =
245
- if (templateId != null) {
586
+ if (requestUrl != null) {
587
+ WebSharerClient.instance.makeScrapUrl(
588
+ requestUrl,
589
+ templateId = templateId?.toLong(),
590
+ templateArgs = templateArgs?.toStringMap(),
591
+ serverCallbackArgs = serverCallbackArgs?.toStringMap(),
592
+ )
593
+ } else if (templateId != null) {
246
594
  WebSharerClient.instance.makeCustomUrl(
247
595
  templateId.toLong(),
248
596
  templateArgs = templateArgs?.toStringMap(),
@@ -261,13 +609,13 @@ class RNCKakaoShareModule internal constructor(
261
609
  }
262
610
  try {
263
611
  KakaoCustomTabsClient.openWithDefault(context, sharerUrl)
264
- promise.resolve(42)
612
+ promise.resolve(result)
265
613
  return
266
614
  } catch (_: UnsupportedOperationException) {
267
615
  }
268
616
  try {
269
617
  KakaoCustomTabsClient.open(context, sharerUrl)
270
- promise.resolve(42)
618
+ promise.resolve(result)
271
619
  return
272
620
  } catch (_: ActivityNotFoundException) {
273
621
  }
@@ -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"),