@react-native-kakao/user 3.1.0-next.0 → 3.1.0-next.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # Change Log
2
2
 
3
+ ## 3.1.0-next.2
4
+
5
+ ### Minor Changes
6
+
7
+ - f466ffa: Expose the iOS Kakao Talk launch method for login and reject web-only token helpers on native platforms.
8
+ - 23ca142: Expose consent revocation results, service-term consent sources, Talk profile IDs, friend pagination links, and masked picker nicknames. Forward Android scope filters, preserve nullable friend nicknames, and return iOS friend thumbnails as URL strings.
9
+ - 845f1f2: Expose original user properties and property-key filters on Android, iOS, and web.
10
+
11
+ ### Patch Changes
12
+
13
+ - f466ffa: Normalize APIs without success data to resolve without a value across platforms, while preserving friend-message results.
14
+ - @react-native-kakao/core@3.1.0-next.2
15
+
16
+ ## 3.1.0-next.1
17
+
18
+ ### Minor Changes
19
+
20
+ - 250d066: Expose group user tokens consistently on Android, iOS, and web.
21
+ - 3d92882: Expose the iOS token refresh request logging option without changing global SDK logging or Android behavior.
22
+ - f1fd1e2: Add manual signup and profile connection state on Android, iOS, and web.
23
+ - f6e0207: Add a shared loginHint option for Kakao Account login on Android, iOS, and web.
24
+ - a8a2e24: Expose partner legal identity fields and consent flags on Android, iOS, and web.
25
+ - d81f9b7: Expose iOS authorization-code and certification-code token exchange with PKCE options, normalized web token issuance, and explicit unsupported-platform errors.
26
+ - 7fe7820: Add native login method selection and explicit refresh of stored OAuth tokens.
27
+ - 524be2c: Expose profile image defaults, leap-month flags, and the user's KakaoTalk UUID on Android, iOS, and web.
28
+ - e25e428: Allow explicit native tokens when refreshing and normalize remaining token lifetimes across native responses.
29
+ - 97a99b5: Add updateProfile to store registered custom user properties on Android, iOS, and web.
30
+ - b6ff8a9: Add channel selection to login on Android, iOS, and web.
31
+ - bd1bb64: Add the secureResource profile image URL option on Android, iOS, and web.
32
+ - 87e8aa7: Allow a custom Android KakaoTalk login request code while preserving the SDK default when omitted. Accept the SDK's full 32-bit request code range for regular and certification login.
33
+
34
+ ### Patch Changes
35
+
36
+ - dfd32ca: Upgrade all Kakao Android SDK modules to 2.25.1 and preserve web shipping-address queries with JavaScript SDK 2.8.4.
37
+ - da4db1c: Return canonical profile enum strings on Android and preserve birthday type strings on iOS.
38
+ - Updated dependencies [d5c5fb8]
39
+ - Updated dependencies [3027207]
40
+ - Updated dependencies [dfd32ca]
41
+ - Updated dependencies [2b223bf]
42
+ - Updated dependencies [67cd4e2]
43
+ - Updated dependencies [d81f9b7]
44
+ - @react-native-kakao/core@3.1.0-next.1
45
+
3
46
  ## 3.1.0-next.0
4
47
 
5
48
  ### Minor Changes
@@ -5,8 +5,11 @@ import com.facebook.react.bridge.Promise
5
5
  import com.facebook.react.bridge.ReactApplicationContext
6
6
  import com.facebook.react.bridge.ReactMethod
7
7
  import com.facebook.react.bridge.ReadableArray
8
+ import com.facebook.react.bridge.ReadableMap
8
9
  import com.kakao.sdk.auth.AuthApiClient
10
+ import com.kakao.sdk.auth.AuthCodeClient
9
11
  import com.kakao.sdk.auth.model.OAuthToken
12
+ import com.kakao.sdk.auth.model.Prompt
10
13
  import com.kakao.sdk.auth.model.Prompt.CERT
11
14
  import com.kakao.sdk.auth.model.Prompt.CREATE
12
15
  import com.kakao.sdk.auth.model.Prompt.LOGIN
@@ -14,8 +17,15 @@ import com.kakao.sdk.auth.model.Prompt.SELECT_ACCOUNT
14
17
  import com.kakao.sdk.common.model.ClientError
15
18
  import com.kakao.sdk.common.model.ClientErrorCause
16
19
  import com.kakao.sdk.user.UserApiClient
20
+ import com.kakao.sdk.user.model.AccountLoginParams
21
+ import com.kakao.sdk.user.model.AgeRange
22
+ import com.kakao.sdk.user.model.BirthdayType
23
+ import com.kakao.sdk.user.model.Gender
24
+ import com.kakao.sdk.user.model.LoginUiMode
25
+ import com.kakao.sdk.user.model.Scope
17
26
  import com.kakao.sdk.user.model.UserShippingAddresses
18
27
  import net.mjstudio.rnkakao.core.util.RNCKakaoResponseNotFoundException
28
+ import net.mjstudio.rnkakao.core.util.RNCKakaoUnsupportedPlatformException
19
29
  import net.mjstudio.rnkakao.core.util.RNCKakaoUtil
20
30
  import net.mjstudio.rnkakao.core.util.argArr
21
31
  import net.mjstudio.rnkakao.core.util.argMap
@@ -28,9 +38,12 @@ import net.mjstudio.rnkakao.core.util.putD
28
38
  import net.mjstudio.rnkakao.core.util.putI
29
39
  import net.mjstudio.rnkakao.core.util.putL
30
40
  import net.mjstudio.rnkakao.core.util.rejectWith
41
+ import net.mjstudio.rnkakao.core.util.toStringMap
31
42
  import net.mjstudio.rnkakao.core.util.unix
32
43
  import java.util.Date
33
44
 
45
+ private const val MILLISECONDS_PER_SECOND = 1000.0
46
+
34
47
  class RNCKakaoUserModule internal constructor(
35
48
  context: ReactApplicationContext,
36
49
  ) : NativeKakaoUserSpec(context) {
@@ -39,8 +52,12 @@ class RNCKakaoUserModule internal constructor(
39
52
  serviceTerms: ReadableArray?,
40
53
  prompts: ReadableArray?,
41
54
  useKakaoAccountLogin: Boolean,
55
+ iosLaunchMethod: String,
42
56
  scopes: ReadableArray?,
43
57
  nonce: String?,
58
+ loginHint: String?,
59
+ channelPublicIds: ReadableArray?,
60
+ androidRequestCode: Double?,
44
61
  promise: Promise,
45
62
  ) = onMain {
46
63
  val context =
@@ -48,63 +65,18 @@ class RNCKakaoUserModule internal constructor(
48
65
  promise.rejectWith(ActivityNotFoundException())
49
66
  return@onMain
50
67
  }
51
- val callback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
52
- if (error != null) {
53
- promise.rejectWith(error)
54
- } else if (token == null) {
55
- promise.rejectWith(RNCKakaoResponseNotFoundException("token"))
56
- } else {
57
- promise.resolve(
58
- argMap().apply {
59
- putString("accessToken", token.accessToken)
60
- putString("refreshToken", token.refreshToken)
61
- putString("tokenType", null)
62
- putString("idToken", token.idToken)
63
- putDouble(
64
- "accessTokenExpiresAt",
65
- token.accessTokenExpiresAt.unix.toDouble(),
66
- )
67
- putDouble(
68
- "refreshTokenExpiresAt",
69
- token.refreshTokenExpiresAt.unix.toDouble(),
70
- )
71
- putDouble(
72
- "accessTokenExpiresIn",
73
- RNCKakaoUtil.diffSec(token.accessTokenExpiresAt, Date()).toDouble(),
74
- )
75
- putDouble(
76
- "refreshTokenExpiresIn",
77
- RNCKakaoUtil.diffSec(token.refreshTokenExpiresAt, Date()).toDouble(),
78
- )
79
- putArray(
80
- "scopes",
81
- argArr().apply {
82
- pushStringList(token.scopes ?: listOf())
83
- },
84
- )
85
- },
86
- )
87
- }
88
- }
68
+ val callback = tokenCallback(promise)
89
69
  val requestedServiceTerms = serviceTerms?.filterIsInstance<String>()?.ifEmpty { null }
90
- val requestedPrompts =
91
- prompts
92
- ?.filterIsInstance<String>()
93
- ?.mapNotNull {
94
- when (it) {
95
- "Login" -> LOGIN
96
- "Create" -> CREATE
97
- "Cert" -> CERT
98
- "SelectAccount" -> SELECT_ACCOUNT
99
- else -> null
100
- }
101
- }?.ifEmpty { null }
70
+ val requestedChannelPublicIds = channelPublicIds?.filterIsInstance<String>()?.ifEmpty { null }
71
+ val requestedPrompts = loginPrompts(prompts)
102
72
  val accountLogin = {
103
73
  UserApiClient.instance.loginWithKakaoAccount(
104
74
  context,
105
75
  prompts = requestedPrompts,
76
+ loginHint = loginHint,
106
77
  nonce = nonce,
107
78
  serviceTerms = requestedServiceTerms,
79
+ channelPublicIds = requestedChannelPublicIds,
108
80
  callback = callback,
109
81
  )
110
82
  }
@@ -134,8 +106,10 @@ class RNCKakaoUserModule internal constructor(
134
106
 
135
107
  UserApiClient.instance.loginWithKakaoTalk(
136
108
  context,
109
+ requestCode = androidRequestCode?.toInt() ?: AuthCodeClient.DEFAULT_REQUEST_CODE,
137
110
  nonce = nonce,
138
111
  serviceTerms = requestedServiceTerms,
112
+ channelPublicIds = requestedChannelPublicIds,
139
113
  callback = talkCallback,
140
114
  )
141
115
  } else {
@@ -143,12 +117,92 @@ class RNCKakaoUserModule internal constructor(
143
117
  }
144
118
  }
145
119
 
120
+ @ReactMethod
121
+ override fun loginWithKakao(
122
+ uiMode: String,
123
+ orientation: String,
124
+ launchMethod: String,
125
+ prompts: ReadableArray,
126
+ loginHint: String?,
127
+ nonce: String?,
128
+ channelPublicIds: ReadableArray?,
129
+ serviceTerms: ReadableArray?,
130
+ promise: Promise,
131
+ ) = onMain {
132
+ val context =
133
+ reactApplicationContext.currentActivity ?: run {
134
+ promise.rejectWith(ActivityNotFoundException())
135
+ return@onMain
136
+ }
137
+ UserApiClient.instance.loginWithKakao(
138
+ context,
139
+ uiMode = LoginUiMode.entries.firstOrNull { it.name.equals(uiMode, ignoreCase = true) } ?: LoginUiMode.AUTO,
140
+ accountParams = AccountLoginParams(prompts = loginPrompts(prompts), loginHint = loginHint),
141
+ nonce = nonce,
142
+ channelPublicIds = channelPublicIds?.filterIsInstance<String>()?.ifEmpty { null },
143
+ serviceTerms = serviceTerms?.filterIsInstance<String>()?.ifEmpty { null },
144
+ callback = tokenCallback(promise),
145
+ )
146
+ }
147
+
148
+ @ReactMethod
149
+ override fun issueAccessTokenWithCode(
150
+ code: String,
151
+ codeVerifier: String?,
152
+ redirectUri: String?,
153
+ promise: Promise,
154
+ ) {
155
+ promise.rejectWith(RNCKakaoUnsupportedPlatformException("issueAccessTokenWithCode", "iOS and web"))
156
+ }
157
+
158
+ @ReactMethod
159
+ override fun refreshToken(
160
+ token: ReadableMap?,
161
+ iosLoggingEnabled: Boolean?,
162
+ promise: Promise,
163
+ ) = onMain {
164
+ try {
165
+ val oldToken =
166
+ token?.let {
167
+ OAuthToken(
168
+ accessToken = requireNotNull(it.getString("accessToken")),
169
+ accessTokenExpiresAt = Date((it.getDouble("accessTokenExpiresAt") * MILLISECONDS_PER_SECOND).toLong()),
170
+ refreshToken = requireNotNull(it.getString("refreshToken")),
171
+ refreshTokenExpiresAt = Date((it.getDouble("refreshTokenExpiresAt") * MILLISECONDS_PER_SECOND).toLong()),
172
+ idToken = it.getString("idToken"),
173
+ scopes = it.getArray("scopes")?.filterIsInstance<String>(),
174
+ )
175
+ }
176
+ if (oldToken == null) {
177
+ AuthApiClient.instance.refreshToken(callback = tokenCallback(promise))
178
+ } else {
179
+ AuthApiClient.instance.refreshToken(oldToken = oldToken, callback = tokenCallback(promise))
180
+ }
181
+ } catch (error: Exception) {
182
+ promise.rejectWith(error)
183
+ }
184
+ }
185
+
146
186
  @ReactMethod
147
187
  override fun isKakaoTalkLoginAvailable(promise: Promise) =
148
188
  onMain {
149
189
  promise.resolve(UserApiClient.instance.isKakaoTalkLoginAvailable(reactApplicationContext))
150
190
  }
151
191
 
192
+ @ReactMethod
193
+ override fun signup(
194
+ properties: ReadableMap?,
195
+ promise: Promise,
196
+ ) = onMain {
197
+ UserApiClient.instance.signup(properties?.toStringMap()) { error ->
198
+ if (error != null) {
199
+ promise.rejectWith(error)
200
+ } else {
201
+ promise.resolve(null)
202
+ }
203
+ }
204
+ }
205
+
152
206
  @ReactMethod
153
207
  override fun logout(promise: Promise) =
154
208
  onMain {
@@ -156,7 +210,7 @@ class RNCKakaoUserModule internal constructor(
156
210
  if (it != null) {
157
211
  promise.rejectWith(it)
158
212
  } else {
159
- promise.resolve(42)
213
+ promise.resolve(null)
160
214
  }
161
215
  }
162
216
  }
@@ -168,7 +222,7 @@ class RNCKakaoUserModule internal constructor(
168
222
  if (it != null) {
169
223
  promise.rejectWith(it)
170
224
  } else {
171
- promise.resolve(42)
225
+ promise.resolve(null)
172
226
  }
173
227
  }
174
228
  }
@@ -200,29 +254,13 @@ class RNCKakaoUserModule internal constructor(
200
254
  scopes: ReadableArray?,
201
255
  promise: Promise,
202
256
  ) = onMain {
203
- UserApiClient.instance.scopes { scopeInfo, error ->
257
+ UserApiClient.instance.scopes(scopes = scopes?.filterIsInstance<String>()?.ifEmpty { null }) { scopeInfo, error ->
204
258
  if (error != null) {
205
259
  promise.rejectWith(error)
206
- } else if (scopeInfo?.scopes == null) {
260
+ } else if (scopeInfo == null) {
207
261
  promise.rejectWith(RNCKakaoResponseNotFoundException("scopes"))
208
262
  } else {
209
- promise.resolve(
210
- argArr().also { arr ->
211
- scopeInfo.scopes!!.forEach { scope ->
212
- arr.pushMap(
213
- argMap().also { map ->
214
- map.putString("id", scope.id)
215
- map.putString("displayName", scope.displayName)
216
- map.putString("type", scope.type.name)
217
- map.putBoolean("using", scope.using)
218
- map.putB("delegated", scope.delegated)
219
- map.putBoolean("agreed", scope.agreed)
220
- map.putB("revocable", scope.revocable)
221
- },
222
- )
223
- }
224
- },
225
- )
263
+ promise.resolve(mapScopes(scopeInfo.scopes))
226
264
  }
227
265
  }
228
266
  }
@@ -240,7 +278,7 @@ class RNCKakaoUserModule internal constructor(
240
278
  } else if (scopeInfo == null) {
241
279
  promise.rejectWith(RNCKakaoResponseNotFoundException("scopeInfo"))
242
280
  } else {
243
- promise.resolve(42)
281
+ promise.resolve(mapScopes(scopeInfo.scopes))
244
282
  }
245
283
  }
246
284
  }
@@ -269,6 +307,9 @@ class RNCKakaoUserModule internal constructor(
269
307
  putBoolean("agreed", term.agreed)
270
308
  putBoolean("required", term.required)
271
309
  putBoolean("revocable", term.revocable)
310
+ term.referer?.let {
311
+ putString("agreedBy", if (it.name == "KAUTH" || it.name == "KAPI") it.name else "unknown")
312
+ }
272
313
  }
273
314
  } ?: listOf(),
274
315
  ),
@@ -378,87 +419,123 @@ class RNCKakaoUserModule internal constructor(
378
419
  }
379
420
 
380
421
  @ReactMethod
381
- override fun me(promise: Promise) =
382
- onMain {
383
- UserApiClient.instance.me { user, error ->
384
- if (error != null) {
385
- promise.rejectWith(error)
386
- } else if (user == null) {
387
- promise.rejectWith(RNCKakaoResponseNotFoundException("user"))
388
- } else {
389
- promise.resolve(
390
- argMap().apply {
391
- putD("id", user.id?.toDouble())
392
- putL("connectedAt", user.connectedAt?.unix)
393
- putL("synchedAt", user.synchedAt?.unix)
394
- putString("name", user.kakaoAccount?.name)
422
+ override fun updateProfile(
423
+ properties: ReadableMap,
424
+ promise: Promise,
425
+ ) = onMain {
426
+ UserApiClient.instance.updateProfile(properties.toStringMap()) { error ->
427
+ if (error != null) {
428
+ promise.rejectWith(error)
429
+ } else {
430
+ promise.resolve(null)
431
+ }
432
+ }
433
+ }
395
434
 
396
- putString("email", user.kakaoAccount?.email)
397
- putString("nickname", user.kakaoAccount?.profile?.nickname)
398
- putString("profileImageUrl", user.kakaoAccount?.profile?.profileImageUrl)
399
- putString("thumbnailImageUrl", user.kakaoAccount?.profile?.thumbnailImageUrl)
400
- putString("phoneNumber", user.kakaoAccount?.phoneNumber)
401
- putString("ageRange", user.kakaoAccount?.ageRange?.name)
402
- putString("birthday", user.kakaoAccount?.birthday)
403
- putString("birthdayType", user.kakaoAccount?.birthdayType?.name)
404
- putString("birthyear", user.kakaoAccount?.birthyear)
405
- putString("gender", user.kakaoAccount?.gender?.name)
406
- putB("isEmailValid", user.kakaoAccount?.isEmailValid)
407
- putB("isEmailVerified", user.kakaoAccount?.isEmailVerified)
408
- putB("isKorean", user.kakaoAccount?.isKorean)
409
- putB(
410
- "ageRangeNeedsAgreement",
411
- user.kakaoAccount?.ageRangeNeedsAgreement,
412
- )
413
- putB(
414
- "birthdayNeedsAgreement",
415
- user.kakaoAccount?.birthdayNeedsAgreement,
416
- )
417
- putB(
418
- "birthyearNeedsAgreement",
419
- user.kakaoAccount?.birthyearNeedsAgreement,
420
- )
421
- putB(
422
- "emailNeedsAgreement",
423
- user.kakaoAccount?.emailNeedsAgreement,
424
- )
425
- putB(
426
- "genderNeedsAgreement",
427
- user.kakaoAccount?.genderNeedsAgreement,
428
- )
429
- putB(
430
- "isKoreanNeedsAgreement",
431
- user.kakaoAccount?.isKoreanNeedsAgreement,
432
- )
433
- putB(
434
- "phoneNumberNeedsAgreement",
435
- user.kakaoAccount?.phoneNumberNeedsAgreement,
436
- )
437
- putB(
438
- "profileNeedsAgreement",
439
- user.kakaoAccount?.profileNeedsAgreement,
440
- )
441
- putB(
442
- "nameNeedsAgreement",
443
- user.kakaoAccount?.nameNeedsAgreement,
444
- )
445
- putB(
446
- "profileImageNeedsAgreement",
447
- user.kakaoAccount?.profileImageNeedsAgreement,
448
- )
449
- putB(
450
- "profileNicknameNeedsAgreement",
451
- user.kakaoAccount?.profileNicknameNeedsAgreement,
452
- )
453
- putB(
454
- "legalBirthDateNeedsAgreement",
455
- user.kakaoAccount?.legalBirthDateNeedsAgreement,
456
- )
457
- },
458
- )
459
- }
435
+ @ReactMethod
436
+ override fun me(
437
+ propertyKeys: ReadableArray?,
438
+ secureResource: Boolean?,
439
+ promise: Promise,
440
+ ) = onMain {
441
+ UserApiClient.instance.me(
442
+ propertyKeys?.filterIsInstance<String>(),
443
+ secureResource = secureResource ?: true,
444
+ ) { user, error ->
445
+ if (error != null) {
446
+ promise.rejectWith(error)
447
+ } else if (user == null) {
448
+ promise.rejectWith(RNCKakaoResponseNotFoundException("user"))
449
+ } else {
450
+ promise.resolve(
451
+ argMap().apply {
452
+ putD("id", user.id?.toDouble())
453
+ putMap(
454
+ "properties",
455
+ argMap().apply {
456
+ user.properties?.forEach { (key, value) -> putString(key, value) }
457
+ },
458
+ )
459
+ putB("hasSignedUp", user.hasSignedUp)
460
+ putString("uuid", user.uuid)
461
+ putString("groupUserToken", user.groupUserToken)
462
+ putL("connectedAt", user.connectedAt?.unix)
463
+ putL("synchedAt", user.synchedAt?.unix)
464
+ putString("name", user.kakaoAccount?.name)
465
+
466
+ putString("email", user.kakaoAccount?.email)
467
+ putString("nickname", user.kakaoAccount?.profile?.nickname)
468
+ putString("profileImageUrl", user.kakaoAccount?.profile?.profileImageUrl)
469
+ putString("thumbnailImageUrl", user.kakaoAccount?.profile?.thumbnailImageUrl)
470
+ putB("isDefaultImage", user.kakaoAccount?.profile?.isDefaultImage)
471
+ putString("phoneNumber", user.kakaoAccount?.phoneNumber)
472
+ putString("ageRange", user.kakaoAccount?.ageRange.toBridgeValue())
473
+ putString("birthday", user.kakaoAccount?.birthday)
474
+ putString("birthdayType", user.kakaoAccount?.birthdayType.toBridgeValue())
475
+ putB("isLeapMonth", user.kakaoAccount?.isLeapMonth)
476
+ putString("birthyear", user.kakaoAccount?.birthyear)
477
+ putString("gender", user.kakaoAccount?.gender.toBridgeValue())
478
+ putB("isEmailValid", user.kakaoAccount?.isEmailValid)
479
+ putB("isEmailVerified", user.kakaoAccount?.isEmailVerified)
480
+ putB("isKorean", user.kakaoAccount?.isKorean)
481
+ putB(
482
+ "ageRangeNeedsAgreement",
483
+ user.kakaoAccount?.ageRangeNeedsAgreement,
484
+ )
485
+ putB(
486
+ "birthdayNeedsAgreement",
487
+ user.kakaoAccount?.birthdayNeedsAgreement,
488
+ )
489
+ putB(
490
+ "birthyearNeedsAgreement",
491
+ user.kakaoAccount?.birthyearNeedsAgreement,
492
+ )
493
+ putB(
494
+ "emailNeedsAgreement",
495
+ user.kakaoAccount?.emailNeedsAgreement,
496
+ )
497
+ putB(
498
+ "genderNeedsAgreement",
499
+ user.kakaoAccount?.genderNeedsAgreement,
500
+ )
501
+ putB(
502
+ "isKoreanNeedsAgreement",
503
+ user.kakaoAccount?.isKoreanNeedsAgreement,
504
+ )
505
+ putB(
506
+ "phoneNumberNeedsAgreement",
507
+ user.kakaoAccount?.phoneNumberNeedsAgreement,
508
+ )
509
+ putB(
510
+ "profileNeedsAgreement",
511
+ user.kakaoAccount?.profileNeedsAgreement,
512
+ )
513
+ putB(
514
+ "nameNeedsAgreement",
515
+ user.kakaoAccount?.nameNeedsAgreement,
516
+ )
517
+ putB(
518
+ "profileImageNeedsAgreement",
519
+ user.kakaoAccount?.profileImageNeedsAgreement,
520
+ )
521
+ putB(
522
+ "profileNicknameNeedsAgreement",
523
+ user.kakaoAccount?.profileNicknameNeedsAgreement,
524
+ )
525
+ putB(
526
+ "legalBirthDateNeedsAgreement",
527
+ user.kakaoAccount?.legalBirthDateNeedsAgreement,
528
+ )
529
+ putString("legalName", user.kakaoAccount?.legalName)
530
+ putB("legalNameNeedsAgreement", user.kakaoAccount?.legalNameNeedsAgreement)
531
+ putString("legalBirthDate", user.kakaoAccount?.legalBirthDate)
532
+ putString("legalGender", user.kakaoAccount?.legalGender.toBridgeValue())
533
+ putB("legalGenderNeedsAgreement", user.kakaoAccount?.legalGenderNeedsAgreement)
534
+ },
535
+ )
460
536
  }
461
537
  }
538
+ }
462
539
 
463
540
  @ReactMethod
464
541
  override fun getAccessToken(promise: Promise) =
@@ -479,4 +556,102 @@ class RNCKakaoUserModule internal constructor(
479
556
  }
480
557
  }
481
558
  }
559
+
560
+ private fun mapScopes(scopes: List<Scope>?) =
561
+ argArr().pushMapList(
562
+ scopes.orEmpty().map { scope ->
563
+ argMap().apply {
564
+ putString("id", scope.id)
565
+ putString("displayName", scope.displayName)
566
+ putString("type", scope.type.name)
567
+ putBoolean("using", scope.using)
568
+ putB("delegated", scope.delegated)
569
+ putBoolean("agreed", scope.agreed)
570
+ putB("revocable", scope.revocable)
571
+ }
572
+ },
573
+ )
574
+
575
+ private fun tokenCallback(promise: Promise): (OAuthToken?, Throwable?) -> Unit =
576
+ { token, error ->
577
+ if (error != null) {
578
+ promise.rejectWith(error)
579
+ } else if (token == null) {
580
+ promise.rejectWith(RNCKakaoResponseNotFoundException("token"))
581
+ } else {
582
+ promise.resolve(
583
+ argMap().apply {
584
+ putString("accessToken", token.accessToken)
585
+ putString("refreshToken", token.refreshToken)
586
+ putString("tokenType", null)
587
+ putString("idToken", token.idToken)
588
+ putDouble(
589
+ "accessTokenExpiresAt",
590
+ token.accessTokenExpiresAt.unix.toDouble(),
591
+ )
592
+ putDouble(
593
+ "refreshTokenExpiresAt",
594
+ token.refreshTokenExpiresAt.unix.toDouble(),
595
+ )
596
+ putDouble(
597
+ "accessTokenExpiresIn",
598
+ RNCKakaoUtil.diffSec(token.accessTokenExpiresAt, Date()).toDouble(),
599
+ )
600
+ putDouble(
601
+ "refreshTokenExpiresIn",
602
+ RNCKakaoUtil.diffSec(token.refreshTokenExpiresAt, Date()).toDouble(),
603
+ )
604
+ putArray(
605
+ "scopes",
606
+ argArr().apply {
607
+ pushStringList(token.scopes ?: listOf())
608
+ },
609
+ )
610
+ },
611
+ )
612
+ }
613
+ }
614
+
615
+ private fun loginPrompts(prompts: ReadableArray?): List<Prompt>? =
616
+ prompts
617
+ ?.filterIsInstance<String>()
618
+ ?.mapNotNull {
619
+ when (it) {
620
+ "Login" -> LOGIN
621
+ "Create" -> CREATE
622
+ "Cert" -> CERT
623
+ "SelectAccount" -> SELECT_ACCOUNT
624
+ else -> null
625
+ }
626
+ }?.ifEmpty { null }
482
627
  }
628
+
629
+ private fun AgeRange?.toBridgeValue(): String? =
630
+ when (this) {
631
+ AgeRange.AGE_0_9 -> "0~9"
632
+ AgeRange.AGE_10_14 -> "10~14"
633
+ AgeRange.AGE_15_19 -> "15~19"
634
+ AgeRange.AGE_20_29 -> "20~29"
635
+ AgeRange.AGE_30_39 -> "30~39"
636
+ AgeRange.AGE_40_49 -> "40~49"
637
+ AgeRange.AGE_50_59 -> "50~59"
638
+ AgeRange.AGE_60_69 -> "60~69"
639
+ AgeRange.AGE_70_79 -> "70~79"
640
+ AgeRange.AGE_80_89 -> "80~89"
641
+ AgeRange.AGE_90_ABOVE -> "90~"
642
+ AgeRange.UNKNOWN, null -> null
643
+ }
644
+
645
+ private fun Gender?.toBridgeValue(): String? =
646
+ when (this) {
647
+ Gender.FEMALE -> "female"
648
+ Gender.MALE -> "male"
649
+ Gender.UNKNOWN, null -> null
650
+ }
651
+
652
+ private fun BirthdayType?.toBridgeValue(): String? =
653
+ when (this) {
654
+ BirthdayType.SOLAR -> "SOLAR"
655
+ BirthdayType.LUNAR -> "LUNAR"
656
+ BirthdayType.UNKNOWN, null -> null
657
+ }