@hawcx/react-native-sdk 1.0.2 → 1.0.4
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 +8 -2
- package/README.md +6 -1
- package/android/build.gradle +145 -0
- package/android/consumer-rules.pro +2 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +6 -0
- package/android/gradle.properties +5 -0
- package/android/gradlew +185 -0
- package/android/gradlew.bat +89 -0
- package/android/libs/hawcx-5.1.1.aar +0 -0
- package/android/settings.gradle +53 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/hawcx/reactnative/AuthCallbackProxy.kt +53 -0
- package/android/src/main/java/com/hawcx/reactnative/HawcxEventDispatcher.kt +43 -0
- package/android/src/main/java/com/hawcx/reactnative/HawcxReactNativeLogger.kt +19 -0
- package/android/src/main/java/com/hawcx/reactnative/HawcxReactNativeModule.kt +387 -0
- package/android/src/main/java/com/hawcx/reactnative/HawcxReactNativePackage.kt +16 -0
- package/android/src/main/java/com/hawcx/reactnative/PushDelegateProxy.kt +31 -0
- package/android/src/main/java/com/hawcx/reactnative/SessionCallbackProxy.kt +35 -0
- package/docs/RELEASE.md +13 -2
- package/example/README.md +1 -1
- package/example/android/build.gradle +12 -0
- package/example/android/settings.gradle +34 -0
- package/example/ios/HawcxExampleApp.xcodeproj/project.xcworkspace/xcuserdata/agambhullar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/Podfile.lock +2 -2
- package/example/package-lock.json +3 -3
- package/example/package.json +1 -1
- package/example/src/hawcx.config.ts +7 -1
- package/ios/Frameworks/HawcxFramework.xcframework/Info.plist +5 -5
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/HawcxFramework +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.abi.json +12 -258
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.private.swiftinterface +1 -1
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftinterface +1 -1
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/HawcxFramework +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.abi.json +12 -258
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +1 -1
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftinterface +1 -1
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.abi.json +12 -258
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +1 -1
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +1 -1
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/_CodeSignature/CodeResources +16 -16
- package/ios/HawcxReactNative.swift +23 -1
- package/lib/commonjs/index.js +8 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +8 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/index.d.ts +8 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/__tests__/index.test.ts +4 -0
- package/src/index.ts +20 -3
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
package com.hawcx.reactnative
|
|
2
|
+
|
|
3
|
+
import android.os.Handler
|
|
4
|
+
import android.os.Looper
|
|
5
|
+
import com.facebook.react.bridge.Arguments
|
|
6
|
+
import com.facebook.react.bridge.Promise
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
9
|
+
import com.facebook.react.bridge.ReactMethod
|
|
10
|
+
import com.facebook.react.bridge.ReadableMap
|
|
11
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
12
|
+
import com.hawcx.internal.HawcxSDK
|
|
13
|
+
import com.hawcx.utils.AuthV5Callback
|
|
14
|
+
|
|
15
|
+
internal const val AUTH_EVENT_NAME = "hawcx.auth.event"
|
|
16
|
+
internal const val SESSION_EVENT_NAME = "hawcx.session.event"
|
|
17
|
+
internal const val PUSH_EVENT_NAME = "hawcx.push.event"
|
|
18
|
+
|
|
19
|
+
@ReactModule(name = HawcxReactNativeModule.NAME)
|
|
20
|
+
class HawcxReactNativeModule(reactContext: ReactApplicationContext) :
|
|
21
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
22
|
+
|
|
23
|
+
companion object {
|
|
24
|
+
const val NAME = "HawcxReactNative"
|
|
25
|
+
private const val CODE_CONFIG = "hawcx.config"
|
|
26
|
+
private const val CODE_SDK = "hawcx.sdk"
|
|
27
|
+
private const val CODE_INPUT = "hawcx.input"
|
|
28
|
+
private const val CODE_STORAGE = "hawcx.storage"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
32
|
+
private val applicationContext = reactApplicationContext.applicationContext
|
|
33
|
+
|
|
34
|
+
private val eventDispatcher = HawcxEventDispatcher(reactApplicationContext)
|
|
35
|
+
|
|
36
|
+
@Volatile
|
|
37
|
+
private var hawcxSDK: HawcxSDK? = null
|
|
38
|
+
@Volatile
|
|
39
|
+
private var authCallbackProxy: AuthV5Callback? = null
|
|
40
|
+
@Volatile
|
|
41
|
+
private var sessionCallbackProxy: SessionCallbackProxy? = null
|
|
42
|
+
@Volatile
|
|
43
|
+
private var pushDelegateProxy: PushDelegateProxy? = null
|
|
44
|
+
|
|
45
|
+
override fun getName(): String = NAME
|
|
46
|
+
|
|
47
|
+
@ReactMethod
|
|
48
|
+
fun initialize(config: ReadableMap?, promise: Promise) {
|
|
49
|
+
val configMap = config ?: run {
|
|
50
|
+
promise.reject(CODE_CONFIG, "initialize requires a configuration map")
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
val projectApiKey = configMap.getString("projectApiKey")?.trim().orEmpty()
|
|
55
|
+
if (projectApiKey.isEmpty()) {
|
|
56
|
+
promise.reject(CODE_CONFIG, "projectApiKey is required")
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
val baseUrl = when {
|
|
61
|
+
configMap.hasKey("baseUrl") && !configMap.isNull("baseUrl") ->
|
|
62
|
+
configMap.getString("baseUrl")?.trim().orEmpty()
|
|
63
|
+
configMap.hasKey("endpoints") && !configMap.isNull("endpoints") -> {
|
|
64
|
+
configMap.getMap("endpoints")?.getString("authBaseUrl")?.trim().orEmpty()
|
|
65
|
+
}
|
|
66
|
+
else -> ""
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (baseUrl.isEmpty()) {
|
|
70
|
+
promise.reject(CODE_CONFIG, "baseUrl is required")
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (configMap.hasKey("oauthConfig") && !configMap.isNull("oauthConfig")) {
|
|
75
|
+
HawcxReactNativeLogger.w(
|
|
76
|
+
"oauthConfig was provided but the Android SDK no longer accepts client credentials on-device. " +
|
|
77
|
+
"Remove this field from initialize()."
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
runOnUiThread {
|
|
82
|
+
try {
|
|
83
|
+
val sdk = HawcxSDK(
|
|
84
|
+
context = applicationContext,
|
|
85
|
+
projectApiKey = projectApiKey,
|
|
86
|
+
baseUrl = baseUrl
|
|
87
|
+
)
|
|
88
|
+
hawcxSDK = sdk
|
|
89
|
+
val authProxy = AuthCallbackProxy(eventDispatcher)
|
|
90
|
+
val sessionProxy = SessionCallbackProxy(eventDispatcher)
|
|
91
|
+
val pushProxy = PushDelegateProxy(eventDispatcher)
|
|
92
|
+
authCallbackProxy = authProxy
|
|
93
|
+
sessionCallbackProxy = sessionProxy
|
|
94
|
+
pushDelegateProxy = pushProxy
|
|
95
|
+
sdk.pushAuthDelegate = pushProxy
|
|
96
|
+
promise.resolve(null)
|
|
97
|
+
} catch (error: Exception) {
|
|
98
|
+
promise.reject(CODE_SDK, error.message, error)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@ReactMethod
|
|
104
|
+
fun authenticate(userId: String?, promise: Promise) {
|
|
105
|
+
val sdk = hawcxSDK ?: run {
|
|
106
|
+
promise.reject(CODE_SDK, "initialize must be called before authenticate")
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
val callback = authCallbackProxy ?: run {
|
|
110
|
+
promise.reject(CODE_SDK, "Auth callback not configured")
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
val sanitizedUserId = userId?.trim().orEmpty()
|
|
114
|
+
if (sanitizedUserId.isEmpty()) {
|
|
115
|
+
promise.reject(CODE_INPUT, "userId cannot be empty")
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
runOnUiThread {
|
|
120
|
+
sdk.authenticateV5(sanitizedUserId, callback)
|
|
121
|
+
promise.resolve(null)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@ReactMethod
|
|
126
|
+
fun submitOtp(otp: String?, promise: Promise) {
|
|
127
|
+
val sdk = hawcxSDK ?: run {
|
|
128
|
+
promise.reject(CODE_SDK, "initialize must be called before submitOtp")
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
val sanitizedOtp = otp?.trim().orEmpty()
|
|
132
|
+
if (sanitizedOtp.isEmpty()) {
|
|
133
|
+
promise.reject(CODE_INPUT, "otp cannot be empty")
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
runOnUiThread {
|
|
138
|
+
sdk.submitOtpV5(sanitizedOtp)
|
|
139
|
+
promise.resolve(null)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@ReactMethod
|
|
144
|
+
fun getDeviceDetails(promise: Promise) {
|
|
145
|
+
val sdk = hawcxSDK ?: run {
|
|
146
|
+
promise.reject(CODE_SDK, "initialize must be called before getDeviceDetails")
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
val callback = sessionCallbackProxy ?: run {
|
|
150
|
+
promise.reject(CODE_SDK, "Session callback not configured")
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
runOnUiThread {
|
|
155
|
+
sdk.getDeviceDetails(callback)
|
|
156
|
+
promise.resolve(null)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@ReactMethod
|
|
161
|
+
fun webLogin(pin: String?, promise: Promise) {
|
|
162
|
+
val sdk = hawcxSDK ?: run {
|
|
163
|
+
promise.reject(CODE_SDK, "initialize must be called before webLogin")
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
val callback = sessionCallbackProxy ?: run {
|
|
167
|
+
promise.reject(CODE_SDK, "Session callback not configured")
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
val sanitizedPin = pin?.trim().orEmpty()
|
|
171
|
+
if (sanitizedPin.isEmpty()) {
|
|
172
|
+
promise.reject(CODE_INPUT, "pin cannot be empty")
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
runOnUiThread {
|
|
177
|
+
sdk.webLogin(sanitizedPin, callback)
|
|
178
|
+
promise.resolve(null)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@ReactMethod
|
|
183
|
+
fun webApprove(token: String?, promise: Promise) {
|
|
184
|
+
val sdk = hawcxSDK ?: run {
|
|
185
|
+
promise.reject(CODE_SDK, "initialize must be called before webApprove")
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
val callback = sessionCallbackProxy ?: run {
|
|
189
|
+
promise.reject(CODE_SDK, "Session callback not configured")
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
val sanitizedToken = token?.trim().orEmpty()
|
|
193
|
+
if (sanitizedToken.isEmpty()) {
|
|
194
|
+
promise.reject(CODE_INPUT, "token cannot be empty")
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
runOnUiThread {
|
|
199
|
+
sdk.webApprove(sanitizedToken, callback)
|
|
200
|
+
promise.resolve(null)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@ReactMethod
|
|
205
|
+
fun storeBackendOAuthTokens(userId: String?, accessToken: String?, refreshToken: String?, promise: Promise) {
|
|
206
|
+
val sdk = hawcxSDK ?: run {
|
|
207
|
+
promise.reject(CODE_SDK, "initialize must be called before storeBackendOAuthTokens")
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
val trimmedUser = userId?.trim().orEmpty()
|
|
211
|
+
if (trimmedUser.isEmpty()) {
|
|
212
|
+
promise.reject(CODE_INPUT, "userId cannot be empty")
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
val trimmedAccess = accessToken?.trim().orEmpty()
|
|
217
|
+
if (trimmedAccess.isEmpty()) {
|
|
218
|
+
promise.reject(CODE_INPUT, "accessToken cannot be empty")
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
val refresh = refreshToken?.trim().orEmpty().ifBlank { null }
|
|
223
|
+
|
|
224
|
+
runCatching {
|
|
225
|
+
sdk.storeBackendOAuthTokens(
|
|
226
|
+
accessToken = trimmedAccess,
|
|
227
|
+
refreshToken = refresh,
|
|
228
|
+
userId = trimmedUser
|
|
229
|
+
)
|
|
230
|
+
}.onSuccess { stored ->
|
|
231
|
+
if (stored) {
|
|
232
|
+
promise.resolve(true)
|
|
233
|
+
} else {
|
|
234
|
+
promise.reject(CODE_STORAGE, "Failed to persist backend-issued tokens")
|
|
235
|
+
}
|
|
236
|
+
}.onFailure { error ->
|
|
237
|
+
promise.reject(CODE_STORAGE, error.message, error)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@ReactMethod
|
|
242
|
+
fun getLastLoggedInUser(promise: Promise) {
|
|
243
|
+
val sdk = hawcxSDK ?: run {
|
|
244
|
+
promise.reject(CODE_SDK, "initialize must be called before getLastLoggedInUser")
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
promise.resolve(sdk.getLastLoggedInUser())
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@ReactMethod
|
|
251
|
+
fun clearSessionTokens(userId: String?, promise: Promise) {
|
|
252
|
+
val sdk = hawcxSDK ?: run {
|
|
253
|
+
promise.reject(CODE_SDK, "initialize must be called before clearSessionTokens")
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
val sanitizedUserId = userId?.trim().orEmpty()
|
|
257
|
+
if (sanitizedUserId.isEmpty()) {
|
|
258
|
+
promise.reject(CODE_INPUT, "userId cannot be empty")
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
runOnUiThread {
|
|
263
|
+
sdk.clearSessionTokens(sanitizedUserId)
|
|
264
|
+
promise.resolve(null)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@ReactMethod
|
|
269
|
+
fun clearUserKeychainData(userId: String?, promise: Promise) {
|
|
270
|
+
val sdk = hawcxSDK ?: run {
|
|
271
|
+
promise.reject(CODE_SDK, "initialize must be called before clearUserKeychainData")
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
val sanitizedUserId = userId?.trim().orEmpty()
|
|
275
|
+
if (sanitizedUserId.isEmpty()) {
|
|
276
|
+
promise.reject(CODE_INPUT, "userId cannot be empty")
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
runOnUiThread {
|
|
281
|
+
sdk.clearUserKeychainData(sanitizedUserId)
|
|
282
|
+
promise.resolve(null)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
@ReactMethod
|
|
287
|
+
fun setFcmToken(token: String?, promise: Promise) {
|
|
288
|
+
val sdk = hawcxSDK ?: run {
|
|
289
|
+
promise.reject(CODE_SDK, "initialize must be called before setFcmToken")
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
val sanitizedToken = token?.trim().orEmpty()
|
|
293
|
+
if (sanitizedToken.isEmpty()) {
|
|
294
|
+
promise.reject(CODE_INPUT, "token cannot be empty")
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
runOnUiThread {
|
|
298
|
+
sdk.setFcmToken(sanitizedToken)
|
|
299
|
+
promise.resolve(null)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
@ReactMethod
|
|
304
|
+
fun userDidAuthenticate(promise: Promise) {
|
|
305
|
+
val sdk = hawcxSDK ?: run {
|
|
306
|
+
promise.reject(CODE_SDK, "initialize must be called before userDidAuthenticate")
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
runOnUiThread {
|
|
310
|
+
sdk.userDidAuthenticate()
|
|
311
|
+
promise.resolve(null)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
@ReactMethod
|
|
316
|
+
fun handlePushNotification(payload: ReadableMap?, promise: Promise) {
|
|
317
|
+
val sdk = hawcxSDK ?: run {
|
|
318
|
+
promise.reject(CODE_SDK, "initialize must be called before handlePushNotification")
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
val payloadMap = payload ?: run {
|
|
322
|
+
promise.reject(CODE_INPUT, "payload is required")
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
val stringMap = mutableMapOf<String, String>()
|
|
327
|
+
payloadMap.toHashMap().forEach { (key, value) ->
|
|
328
|
+
val valueString = value?.toString()
|
|
329
|
+
if (valueString != null) {
|
|
330
|
+
stringMap[key] = valueString
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
runOnUiThread {
|
|
335
|
+
sdk.handlePushNotification(stringMap)
|
|
336
|
+
promise.resolve(stringMap.containsKey("request_id"))
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@ReactMethod
|
|
341
|
+
fun approvePushRequest(requestId: String?, promise: Promise) {
|
|
342
|
+
val sdk = hawcxSDK ?: run {
|
|
343
|
+
promise.reject(CODE_SDK, "initialize must be called before approvePushRequest")
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
val sanitizedId = requestId?.trim().orEmpty()
|
|
347
|
+
if (sanitizedId.isEmpty()) {
|
|
348
|
+
promise.reject(CODE_INPUT, "requestId cannot be empty")
|
|
349
|
+
return
|
|
350
|
+
}
|
|
351
|
+
sdk.approveLoginRequest(sanitizedId) { error ->
|
|
352
|
+
if (error == null) {
|
|
353
|
+
promise.resolve(null)
|
|
354
|
+
} else {
|
|
355
|
+
promise.reject(CODE_SDK, error.message, error)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
@ReactMethod
|
|
361
|
+
fun declinePushRequest(requestId: String?, promise: Promise) {
|
|
362
|
+
val sdk = hawcxSDK ?: run {
|
|
363
|
+
promise.reject(CODE_SDK, "initialize must be called before declinePushRequest")
|
|
364
|
+
return
|
|
365
|
+
}
|
|
366
|
+
val sanitizedId = requestId?.trim().orEmpty()
|
|
367
|
+
if (sanitizedId.isEmpty()) {
|
|
368
|
+
promise.reject(CODE_INPUT, "requestId cannot be empty")
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
sdk.declineLoginRequest(sanitizedId) { error ->
|
|
372
|
+
if (error == null) {
|
|
373
|
+
promise.resolve(null)
|
|
374
|
+
} else {
|
|
375
|
+
promise.reject(CODE_SDK, error.message, error)
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private fun runOnUiThread(block: () -> Unit) {
|
|
381
|
+
if (Looper.getMainLooper() == Looper.myLooper()) {
|
|
382
|
+
block()
|
|
383
|
+
} else {
|
|
384
|
+
mainHandler.post(block)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
package com.hawcx.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
class HawcxReactNativePackage : ReactPackage {
|
|
9
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
|
+
return listOf(HawcxReactNativeModule(reactContext))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
14
|
+
return emptyList()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.hawcx.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.hawcx.model.PushLoginRequestDetails
|
|
5
|
+
import com.hawcx.utils.HawcxPushAuthDelegate
|
|
6
|
+
|
|
7
|
+
internal class PushDelegateProxy(
|
|
8
|
+
private val dispatcher: HawcxEventDispatcher
|
|
9
|
+
) : HawcxPushAuthDelegate {
|
|
10
|
+
|
|
11
|
+
override fun hawcx(didReceiveLoginRequest: String, details: PushLoginRequestDetails) {
|
|
12
|
+
val payload = Arguments.createMap().apply {
|
|
13
|
+
putString("requestId", didReceiveLoginRequest)
|
|
14
|
+
putString("ipAddress", details.ipAddress)
|
|
15
|
+
putString("deviceInfo", details.deviceInfo)
|
|
16
|
+
putString("timestamp", details.timestamp)
|
|
17
|
+
if (!details.location.isNullOrBlank()) {
|
|
18
|
+
putString("location", details.location)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
dispatcher.emitPushEvent("push_login_request", payload)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override fun hawcx(failedToFetchLoginRequestDetails: Throwable) {
|
|
25
|
+
val payload = Arguments.createMap().apply {
|
|
26
|
+
putString("code", "push_error")
|
|
27
|
+
putString("message", failedToFetchLoginRequestDetails.message ?: "Failed to fetch login request details")
|
|
28
|
+
}
|
|
29
|
+
dispatcher.emitPushEvent("push_error", payload)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
package com.hawcx.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.hawcx.utils.DevSessionCallback
|
|
5
|
+
import com.hawcx.utils.WebLoginCallback
|
|
6
|
+
import com.hawcx.utils.WebLoginError
|
|
7
|
+
|
|
8
|
+
internal class SessionCallbackProxy(
|
|
9
|
+
private val dispatcher: HawcxEventDispatcher
|
|
10
|
+
) : DevSessionCallback, WebLoginCallback {
|
|
11
|
+
|
|
12
|
+
override fun onSuccess() {
|
|
13
|
+
dispatcher.emitSessionEvent("session_success")
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
override fun onError() {
|
|
17
|
+
dispatcher.emitSessionEvent(
|
|
18
|
+
"session_error",
|
|
19
|
+
Arguments.createMap().apply {
|
|
20
|
+
putString("code", "session_error")
|
|
21
|
+
putString("message", "Failed to fetch device session")
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override fun onError(webLoginErrorCode: WebLoginError, errorMessage: String) {
|
|
27
|
+
dispatcher.emitSessionEvent(
|
|
28
|
+
"session_error",
|
|
29
|
+
Arguments.createMap().apply {
|
|
30
|
+
putString("code", webLoginErrorCode.name)
|
|
31
|
+
putString("message", errorMessage)
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
}
|
package/docs/RELEASE.md
CHANGED
|
@@ -36,11 +36,13 @@ This document outlines every command, directory, and deliverable required to pub
|
|
|
36
36
|
npm test
|
|
37
37
|
npm run build # produces lib/
|
|
38
38
|
./gradlew -p android clean lintRelease testReleaseUnitTest assembleRelease publishToMavenLocal
|
|
39
|
+
Ensure to udpate api("api.hawcx:hawcx:5.1.1") version in dev_react/android/build.gradle if android aar file version changed
|
|
40
|
+
Then run ./gradlew clean assembleRelease --refresh-dependencies to test if android picks up new aar maven release
|
|
39
41
|
pod lib lint HawcxReactNative.podspec
|
|
40
42
|
```
|
|
41
43
|
- `./gradlew … publishToMavenLocal` validates the Android bridge (with the bundled Hawcx AAR) builds and can be published.
|
|
42
44
|
- `pod lib lint` confirms the Podspec + `HawcxFramework.xcframework` remain valid.
|
|
43
|
-
|
|
45
|
+
|
|
44
46
|
3. **Update version + changelog (repo root)**
|
|
45
47
|
- Edit `package.json` → bump `version` (run `npm install` afterward so `package-lock.json` picks up the new number).
|
|
46
48
|
- Update `android/build.gradle`’s fallback `version` string and `example/package.json` (re-run `npm install` inside `example/` so its lockfile updates).
|
|
@@ -54,10 +56,19 @@ This document outlines every command, directory, and deliverable required to pub
|
|
|
54
56
|
Include refreshed native artifacts (`ios/Frameworks/*.xcframework`, `android/libs/*.aar`) in the same commit when they change.
|
|
55
57
|
|
|
56
58
|
4. **Smoke test the example app**
|
|
59
|
+
|
|
60
|
+
rm -rf ~/Library/Developer/Xcode/DerivedData/HawcxExampleApp*
|
|
61
|
+
|
|
57
62
|
```bash
|
|
58
63
|
cd example
|
|
59
64
|
npm install
|
|
60
|
-
|
|
65
|
+
For ios:
|
|
66
|
+
cd example/ios: pod install
|
|
67
|
+
cd ..
|
|
68
|
+
npm run ios or npm run ios -- --no-packager
|
|
69
|
+
For android:
|
|
70
|
+
cd example/android: ./gradlew clean
|
|
71
|
+
cd ..
|
|
61
72
|
npm run android
|
|
62
73
|
# Use your dev credentials in example/src/hawcx.config.ts
|
|
63
74
|
cd ..
|
package/example/README.md
CHANGED
|
@@ -9,7 +9,7 @@ This example demonstrates the cross-platform APIs exported by `@hawcx/react-nati
|
|
|
9
9
|
cd example
|
|
10
10
|
npm install
|
|
11
11
|
```
|
|
12
|
-
2. Configure credentials in `src/hawcx.config.ts`. The file ships with dev defaults—replace the API key and
|
|
12
|
+
2. Configure credentials in `src/hawcx.config.ts`. The file ships with dev defaults—replace the API key **and** `HAWCX_BASE_URL` host with your tenant’s values.
|
|
13
13
|
3. **iOS only:** install pods:
|
|
14
14
|
```bash
|
|
15
15
|
cd ios && pod install && cd ..
|
|
@@ -21,3 +21,15 @@ buildscript {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
apply plugin: "com.facebook.react.rootproject"
|
|
24
|
+
|
|
25
|
+
subprojects { subproject ->
|
|
26
|
+
subproject.configurations.all { config ->
|
|
27
|
+
config.resolutionStrategy.eachDependency { details ->
|
|
28
|
+
if (details.requested.group == "androidx.core" &&
|
|
29
|
+
(details.requested.name == "core" || details.requested.name == "core-ktx")) {
|
|
30
|
+
details.useVersion("1.13.1")
|
|
31
|
+
details.because("core-ktx 1.16.0 requires compileSdk/AGP 35+, but the demo targets 34/AGP 8.1.1")
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
import org.gradle.api.initialization.resolve.RepositoriesMode
|
|
2
|
+
|
|
3
|
+
dependencyResolutionManagement {
|
|
4
|
+
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
|
|
5
|
+
repositories {
|
|
6
|
+
google()
|
|
7
|
+
mavenCentral()
|
|
8
|
+
maven { url = uri("https://www.jitpack.io") }
|
|
9
|
+
maven {
|
|
10
|
+
url = uri("https://raw.githubusercontent.com/hawcx/hawcx_android_sdk/main/maven")
|
|
11
|
+
content {
|
|
12
|
+
includeGroup("api.hawcx")
|
|
13
|
+
}
|
|
14
|
+
metadataSources {
|
|
15
|
+
mavenPom()
|
|
16
|
+
artifact()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
def localHawcxRepo = new File(rootDir, "../../../prod_android/hawcx_android_sdk/maven")
|
|
20
|
+
if (localHawcxRepo.exists()) {
|
|
21
|
+
maven {
|
|
22
|
+
url = localHawcxRepo.toURI()
|
|
23
|
+
content {
|
|
24
|
+
includeGroup("api.hawcx")
|
|
25
|
+
}
|
|
26
|
+
metadataSources {
|
|
27
|
+
mavenPom()
|
|
28
|
+
artifact()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
1
35
|
rootProject.name = 'com.hawcx.example'
|
|
2
36
|
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
|
3
37
|
include ':app'
|
|
Binary file
|
package/example/ios/Podfile.lock
CHANGED
|
@@ -11,7 +11,7 @@ PODS:
|
|
|
11
11
|
- ReactCommon/turbomodule/core (= 0.73.9)
|
|
12
12
|
- fmt (6.2.1)
|
|
13
13
|
- glog (0.3.5)
|
|
14
|
-
- HawcxReactNative (1.0.
|
|
14
|
+
- HawcxReactNative (1.0.4):
|
|
15
15
|
- React-Core
|
|
16
16
|
- hermes-engine (0.73.9):
|
|
17
17
|
- hermes-engine/Pre-built (= 0.73.9)
|
|
@@ -1236,7 +1236,7 @@ SPEC CHECKSUMS:
|
|
|
1236
1236
|
FBReactNativeSpec: 4fe1d8c2fadc7949344b197d933f76b40401aac5
|
|
1237
1237
|
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
|
1238
1238
|
glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2
|
|
1239
|
-
HawcxReactNative:
|
|
1239
|
+
HawcxReactNative: 089e5e5b790f77ab853f4ff96723c8dd7ad1a02c
|
|
1240
1240
|
hermes-engine: ed62e0dcd013bf4a3b487f164feec1c4e705b5b5
|
|
1241
1241
|
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
|
1242
1242
|
RCT-Folly: cd21f1661364f975ae76b3308167ad66b09f53f5
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hawcx-react-native-sdk-example",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "hawcx-react-native-sdk-example",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.4",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@hawcx/react-native-sdk": "file:..",
|
|
12
12
|
"react": "18.2.0",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"..": {
|
|
31
31
|
"name": "@hawcx/react-native-sdk",
|
|
32
|
-
"version": "1.0.
|
|
32
|
+
"version": "1.0.4",
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@react-native/eslint-config": "^0.74.0",
|
package/example/package.json
CHANGED
|
@@ -4,15 +4,21 @@ import type { HawcxInitializeConfig } from '@hawcx/react-native-sdk';
|
|
|
4
4
|
* Populate the API key locally for testing or use the in-app form.
|
|
5
5
|
* Leaving it blank ensures we never ship real credentials in git history.
|
|
6
6
|
*/
|
|
7
|
-
export const HAWCX_PROJECT_API_KEY = '
|
|
7
|
+
export const HAWCX_PROJECT_API_KEY = 'ceasar2';
|
|
8
|
+
export const HAWCX_BASE_URL = 'https://ceasar-api.hawcx.com';
|
|
8
9
|
|
|
9
10
|
const buildDefaultConfig = (): HawcxInitializeConfig | null => {
|
|
10
11
|
const trimmedKey = HAWCX_PROJECT_API_KEY.trim();
|
|
11
12
|
if (!trimmedKey) {
|
|
12
13
|
return null;
|
|
13
14
|
}
|
|
15
|
+
const trimmedBase = HAWCX_BASE_URL.trim();
|
|
16
|
+
if (!trimmedBase) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
14
19
|
return {
|
|
15
20
|
projectApiKey: trimmedKey,
|
|
21
|
+
baseUrl: trimmedBase,
|
|
16
22
|
};
|
|
17
23
|
};
|
|
18
24
|
|
|
@@ -8,32 +8,32 @@
|
|
|
8
8
|
<key>BinaryPath</key>
|
|
9
9
|
<string>HawcxFramework.framework/HawcxFramework</string>
|
|
10
10
|
<key>LibraryIdentifier</key>
|
|
11
|
-
<string>ios-
|
|
11
|
+
<string>ios-arm64</string>
|
|
12
12
|
<key>LibraryPath</key>
|
|
13
13
|
<string>HawcxFramework.framework</string>
|
|
14
14
|
<key>SupportedArchitectures</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>arm64</string>
|
|
17
|
-
<string>x86_64</string>
|
|
18
17
|
</array>
|
|
19
18
|
<key>SupportedPlatform</key>
|
|
20
19
|
<string>ios</string>
|
|
21
|
-
<key>SupportedPlatformVariant</key>
|
|
22
|
-
<string>simulator</string>
|
|
23
20
|
</dict>
|
|
24
21
|
<dict>
|
|
25
22
|
<key>BinaryPath</key>
|
|
26
23
|
<string>HawcxFramework.framework/HawcxFramework</string>
|
|
27
24
|
<key>LibraryIdentifier</key>
|
|
28
|
-
<string>ios-
|
|
25
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
29
26
|
<key>LibraryPath</key>
|
|
30
27
|
<string>HawcxFramework.framework</string>
|
|
31
28
|
<key>SupportedArchitectures</key>
|
|
32
29
|
<array>
|
|
33
30
|
<string>arm64</string>
|
|
31
|
+
<string>x86_64</string>
|
|
34
32
|
</array>
|
|
35
33
|
<key>SupportedPlatform</key>
|
|
36
34
|
<string>ios</string>
|
|
35
|
+
<key>SupportedPlatformVariant</key>
|
|
36
|
+
<string>simulator</string>
|
|
37
37
|
</dict>
|
|
38
38
|
</array>
|
|
39
39
|
<key>CFBundlePackageType</key>
|