expo-line-login 0.2.0 → 1.0.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.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # Expo Line Login
2
+ ## Installation
3
+ ```bash
4
+ npx expo install expo-line-login
5
+ ```
6
+ ## Prerequisites
7
+ - Please create a Line Login channel and get the channel ID from [Line Developer Console](https://developers.line.biz/console/).
8
+ - Please add your app schema and package name into the "App settings" fields in the "LINE Login" section of the [Line Developer Console](https://developers.line.biz/console/).
9
+ - Please add the following config in your app.json
10
+ ```json
11
+ {
12
+ "expo": {
13
+ "plugins": [
14
+ [
15
+ "expo-line-login", {
16
+ "channelId": "YOUR_CHANNEL_ID", // repleace with your channel ID
17
+ }
18
+ ]
19
+ ]
20
+ }
21
+ }
22
+ ```
23
+
24
+ ## Usage
25
+ ### Login
26
+ ```js
27
+ import {
28
+ login,
29
+ LoginPermission,
30
+ BotPrompt,
31
+ } from 'expo-line-login';
32
+
33
+ const result = await login({
34
+ permissions: [
35
+ LoginPermission.PROFILE,
36
+ LoginPermission.OPEN_ID,
37
+ LoginPermission.EMAIL,
38
+ ],
39
+ botPrompt: BotPrompt.NORMAL,
40
+ });
41
+ console.log(result);
42
+ ```
43
+
44
+ ### Get Profile
45
+ ```js
46
+ import { getProfile } from 'expo-line-login';
47
+
48
+ const profile = await getProfile();
49
+ console.log(profile);
50
+ ```
51
+
52
+ ### Logout
53
+ ```js
54
+ import { logout } from 'expo-line-login';
55
+
56
+ await logout();
57
+ ```
58
+
59
+ ### Get Access Token
60
+ ```js
61
+ import { getAccessToken } from 'expo-line-login';
62
+
63
+ const accessToken = await getAccessToken();
64
+ console.log(accessToken);
65
+ ```
66
+
67
+ ### Get Friendship Status
68
+ ```js
69
+ import { lineGetBotFriendshipStatus } from 'expo-line-login';
70
+
71
+ const friendshipStatus = await lineGetBotFriendshipStatus();
72
+ console.log(friendshipStatus);
73
+ ```
@@ -64,7 +64,7 @@ android {
64
64
 
65
65
  namespace "dev.stanma.line"
66
66
  defaultConfig {
67
- minSdkVersion safeExtGet("minSdkVersion", 21)
67
+ minSdkVersion safeExtGet("minSdkVersion", 24)
68
68
  targetSdkVersion safeExtGet("targetSdkVersion", 33)
69
69
  versionCode 1
70
70
  versionName "0.1.0"
@@ -86,4 +86,5 @@ repositories {
86
86
  dependencies {
87
87
  implementation project(':expo-modules-core')
88
88
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
89
+ implementation "com.linecorp.linesdk:linesdk:latest.release"
89
90
  }
@@ -1,2 +1,3 @@
1
- <manifest>
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <uses-permission android:name="android.permission.INTERNET" />
2
3
  </manifest>
@@ -1,16 +1,160 @@
1
1
  package dev.stanma.line
2
2
 
3
+ import android.content.Context
4
+ import android.content.pm.ApplicationInfo
5
+ import android.content.pm.PackageManager
6
+ import expo.modules.kotlin.Promise
3
7
  import expo.modules.kotlin.modules.Module
4
8
  import expo.modules.kotlin.modules.ModuleDefinition
9
+ import com.linecorp.linesdk.*
10
+ import com.linecorp.linesdk.api.LineApiClient
11
+ import com.linecorp.linesdk.api.LineApiClientBuilder
12
+ import com.linecorp.linesdk.auth.LineAuthenticationConfig
13
+ import com.linecorp.linesdk.auth.LineAuthenticationParams
14
+ import com.linecorp.linesdk.auth.LineLoginApi
15
+ import com.linecorp.linesdk.auth.LineLoginResult
16
+ import expo.modules.kotlin.exception.Exceptions
5
17
 
6
18
  class ExpoLineLoginModule : Module() {
7
- // Each module class must implement the definition function. The definition consists of components
8
- // that describes the module's functionality and behavior.
9
- // See https://docs.expo.dev/modules/module-api for more details about available components.
19
+ private val LOGIN_REQUEST_CODE = 1;
20
+ private var loginPromise: Promise? = null;
21
+
22
+ private lateinit var context: Context;
23
+ private var applicationInfo: ApplicationInfo? = null;
10
24
  override fun definition() = ModuleDefinition {
11
25
  // Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument.
12
26
  // Can be inferred from module's class name, but it's recommended to set it explicitly for clarity.
13
27
  // The module will be accessible from `requireNativeModule('ExpoLineLogin')` in JavaScript.
14
28
  Name("ExpoLineLogin")
29
+
30
+ OnCreate {
31
+ context = appContext.reactContext ?: throw Exceptions.ReactContextLost();
32
+ applicationInfo = context.packageManager?.getApplicationInfo(context.packageName.toString(), PackageManager.GET_META_DATA);
33
+ }
34
+
35
+
36
+
37
+ AsyncFunction("login") { scopes: List<String>, botPrompt: String, promise: Promise ->
38
+ val channelId = applicationInfo?.metaData?.getInt("line.sdk.channelId").toString()
39
+ val loginConfig = LineAuthenticationConfig.Builder(channelId).build()
40
+
41
+ val authenticationParams = LineAuthenticationParams.Builder()
42
+ .scopes(Scope.convertToScopeList(scopes))
43
+ .apply {
44
+ botPrompt(LineAuthenticationParams.BotPrompt.valueOf(botPrompt))
45
+ }
46
+ .build()
47
+
48
+ val currentActivity = appContext.currentActivity
49
+ val context: Context = appContext.reactContext ?: throw Exceptions.ReactContextLost()
50
+
51
+ val loginIntent = LineLoginApi.getLoginIntent(
52
+ context,
53
+ loginConfig,
54
+ authenticationParams
55
+ )
56
+
57
+ currentActivity?.startActivityForResult(loginIntent, LOGIN_REQUEST_CODE)
58
+ loginPromise = promise
59
+ }
60
+
61
+ OnActivityResult {_, (requestCode, resultCode, data) ->
62
+ if (requestCode == LOGIN_REQUEST_CODE) {
63
+ val result: LineLoginResult = LineLoginApi.getLoginResultFromIntent(data)
64
+
65
+ when (result.responseCode) {
66
+ LineApiResponseCode.SUCCESS -> {
67
+ val resultDict = mapOf(
68
+ "friendshipStatusChanged" to result.friendshipStatusChanged,
69
+ "scope" to result.lineCredential?.scopes?.let {
70
+ Scope.join(it)
71
+ },
72
+ "IDTokenNonce" to result.lineIdToken?.nonce,
73
+ "accessToken" to mapOf(
74
+ "access_token" to result.lineCredential?.accessToken?.tokenString,
75
+ "expires_in" to result.lineCredential?.accessToken?.expiresInMillis,
76
+ "id_token" to result.lineIdToken?.rawString,
77
+ "createdAt" to result.lineCredential?.accessToken?.issuedClientTimeMillis,
78
+ ),
79
+ "userProfile" to mapOf(
80
+ "displayName" to result.lineProfile?.displayName,
81
+ "userId" to result.lineProfile?.userId,
82
+ "pictureUrl" to result.lineProfile?.pictureUrl,
83
+ ),
84
+ )
85
+
86
+ loginPromise?.resolve(resultDict)
87
+ loginPromise = null
88
+ }
89
+ LineApiResponseCode.CANCEL -> {
90
+ loginPromise?.reject(result.responseCode.name, result.errorData.message, Exception(result.errorData.message))
91
+ loginPromise = null
92
+ }
93
+ else -> {
94
+ loginPromise?.reject(result.responseCode.name, result.errorData.message, Exception(result.errorData.message))
95
+ loginPromise = null
96
+ }
97
+ }
98
+
99
+ loginPromise = null
100
+ }
101
+ }
102
+
103
+ AsyncFunction("logout") { promise: Promise ->
104
+ val channelId = applicationInfo?.metaData?.getInt("line.sdk.channelId").toString()
105
+ val client: LineApiClient = LineApiClientBuilder(context, channelId).build()
106
+ val logoutRes = client.logout()
107
+ if (logoutRes.isSuccess) {
108
+ promise.resolve(null)
109
+ } else {
110
+ promise.reject(logoutRes.responseCode.name, logoutRes.errorData.message, Exception(logoutRes.errorData.message))
111
+ }
112
+ }
113
+
114
+ AsyncFunction("getProfile") { promise: Promise ->
115
+ val channelId = applicationInfo?.metaData?.getInt("line.sdk.channelId").toString()
116
+ val client: LineApiClient = LineApiClientBuilder(context, channelId).build()
117
+ val profileRes = client.profile
118
+ if (profileRes.isSuccess) {
119
+ val profile = profileRes.responseData
120
+ val resultDict = mapOf(
121
+ "displayName" to profile.displayName,
122
+ "userId" to profile.userId,
123
+ "pictureUrl" to profile.pictureUrl,
124
+ )
125
+ promise.resolve(resultDict)
126
+ } else {
127
+ promise.reject(profileRes.responseCode.name, profileRes.errorData.message, Exception(profileRes.errorData.message))
128
+ }
129
+ }
130
+
131
+ AsyncFunction("getAccessToken") { promise: Promise ->
132
+ val channelId = applicationInfo?.metaData?.getInt("line.sdk.channelId").toString()
133
+ val client: LineApiClient = LineApiClientBuilder(context, channelId).build()
134
+ val accessTokenRes = client.currentAccessToken
135
+ if (accessTokenRes.isSuccess) {
136
+ val accessToken = accessTokenRes.responseData
137
+ val resultDict = mapOf(
138
+ "access_token" to accessToken.tokenString,
139
+ "expires_in" to accessToken.expiresInMillis,
140
+ "createdAt" to accessToken.issuedClientTimeMillis,
141
+ )
142
+ promise.resolve(resultDict)
143
+ } else {
144
+ promise.reject(accessTokenRes.responseCode.name, accessTokenRes.errorData.message, Exception(accessTokenRes.errorData.message))
145
+ }
146
+ }
147
+
148
+ AsyncFunction("getBotFriendshipStatus") {promise: Promise ->
149
+ val channelId = applicationInfo?.metaData?.getInt("line.sdk.channelId").toString()
150
+ val client: LineApiClient = LineApiClientBuilder(context, channelId).build()
151
+ val botFriendshipStatusRes = client.friendshipStatus
152
+ if (botFriendshipStatusRes.isSuccess) {
153
+ val botFriendshipStatus = botFriendshipStatusRes.responseData
154
+ promise.resolve(botFriendshipStatus.isFriend)
155
+ } else {
156
+ promise.reject(botFriendshipStatusRes.responseCode.name, botFriendshipStatusRes.errorData.message, Exception(botFriendshipStatusRes.errorData.message))
157
+ }
158
+ }
15
159
  }
16
160
  }
@@ -1,7 +1,7 @@
1
1
  export interface AccessToken {
2
- token_type: string;
3
- scope: string;
4
- refresh_token: string;
2
+ token_type?: string;
3
+ scope?: string;
4
+ refresh_token?: string;
5
5
  createdAt: number;
6
6
  access_token: string;
7
7
  id_token?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,WAAW;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,WAAW;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"","sourcesContent":["export interface AccessToken {\n token_type: string;\n scope: string;\n refresh_token: string;\n createdAt: number;\n access_token: string;\n id_token?: string;\n expires_in: number;\n}\n\ninterface UserProfile {\n pictureUrl: string;\n userId: string;\n displayName: string;\n}\n\nexport interface LoginResult {\n friendshipStatusChanged?: boolean;\n scope: string;\n IDTokenNonce?: string;\n accessToken: AccessToken;\n userProfile?: UserProfile;\n}\n\nexport interface ProfileResult {\n displayName: string;\n pictureUrl: string;\n userId: string;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"","sourcesContent":["export interface AccessToken {\n token_type?: string;\n scope?: string;\n refresh_token?: string;\n createdAt: number;\n access_token: string;\n id_token?: string;\n expires_in: number;\n}\n\ninterface UserProfile {\n pictureUrl: string;\n userId: string;\n displayName: string;\n}\n\nexport interface LoginResult {\n friendshipStatusChanged?: boolean;\n scope: string;\n IDTokenNonce?: string;\n accessToken: AccessToken;\n userProfile?: UserProfile;\n}\n\nexport interface ProfileResult {\n displayName: string;\n pictureUrl: string;\n userId: string;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-line-login",
3
- "version": "0.2.0",
3
+ "version": "1.0.0",
4
4
  "description": "Integrate LINE login to Expo App",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -22,10 +22,7 @@ const withMyApiKey = (config, { channelId, universalLink }) => {
22
22
  });
23
23
  config = (0, config_plugins_1.withAndroidManifest)(config, (config) => {
24
24
  const mainApplication = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults);
25
- config_plugins_1.AndroidConfig.Manifest.addMetaDataItemToMainApplication(mainApplication, "LINE_CHANNEL_ID", channelId);
26
- if (universalLink) {
27
- config_plugins_1.AndroidConfig.Manifest.addMetaDataItemToMainApplication(mainApplication, "LINE_UNIVERSAL_LINK_URL", universalLink);
28
- }
25
+ config_plugins_1.AndroidConfig.Manifest.addMetaDataItemToMainApplication(mainApplication, "line.sdk.channelId", channelId);
29
26
  return config;
30
27
  });
31
28
  return config;
@@ -37,18 +37,10 @@ const withMyApiKey: ConfigPlugin<{
37
37
 
38
38
  AndroidConfig.Manifest.addMetaDataItemToMainApplication(
39
39
  mainApplication,
40
- "LINE_CHANNEL_ID",
40
+ "line.sdk.channelId",
41
41
  channelId,
42
42
  );
43
43
 
44
- if (universalLink) {
45
- AndroidConfig.Manifest.addMetaDataItemToMainApplication(
46
- mainApplication,
47
- "LINE_UNIVERSAL_LINK_URL",
48
- universalLink,
49
- );
50
- }
51
-
52
44
  return config;
53
45
  });
54
46
 
@@ -1,7 +1,7 @@
1
1
  export interface AccessToken {
2
- token_type: string;
3
- scope: string;
4
- refresh_token: string;
2
+ token_type?: string;
3
+ scope?: string;
4
+ refresh_token?: string;
5
5
  createdAt: number;
6
6
  access_token: string;
7
7
  id_token?: string;