@flybits/react-native-concierge 0.0.0 → 0.1.7

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.
@@ -0,0 +1,460 @@
1
+ package com.concierge
2
+
3
+ import com.facebook.react.bridge.Callback
4
+ import com.facebook.react.bridge.Promise
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
7
+ import com.facebook.react.bridge.ReactMethod
8
+ import com.facebook.react.bridge.ReadableMap
9
+ import com.flybits.android.push.provider.FcmV2DeliveryProvider
10
+ import com.flybits.android.push.provider.PushProvider
11
+ import com.flybits.commons.library.SharedElementsFactory.get
12
+ import com.flybits.commons.library.api.results.callbacks.BasicResultCallback
13
+ import com.flybits.commons.library.exceptions.FlybitsException
14
+ import com.flybits.commons.library.logging.Logger
15
+ import com.flybits.commons.library.logging.VerbosityLevel
16
+ import com.flybits.commons.library.models.PushProviderType
17
+ import com.flybits.concierge.Concierge
18
+ import com.flybits.concierge.ConciergeConstants.HUAWEI_PACKAGE_CONSTANT
19
+ import com.flybits.concierge.FlybitsConciergeConfiguration
20
+ import com.flybits.concierge.enums.ConciergePush
21
+ import com.flybits.concierge.enums.PushTokenStatus
22
+ import com.flybits.concierge.models.toMap
23
+ import com.flybits.context.ContextManager
24
+ import com.flybits.context.ReservedContextPlugin
25
+ import com.flybits.flybitscoreconcierge.actiontypes.ActionTypeSchemeParser
26
+ import com.flybits.flybitscoreconcierge.idps.AnonymousConciergeIDP
27
+ import com.flybits.flybitscoreconcierge.idps.JwtLoginConciergeIDP
28
+ import com.google.gson.Gson
29
+ import com.google.gson.reflect.TypeToken
30
+ import handleError
31
+ import handleSuccess
32
+ import org.json.JSONObject
33
+ import readableMapToHashMap
34
+ import java.lang.reflect.Type
35
+ import java.net.URLEncoder
36
+ import java.nio.charset.StandardCharsets
37
+ import java.util.Locale
38
+
39
+ const val TAG_FLYBITS_MODULE = "FlybitsModule"
40
+
41
+ class FlybitsModule internal constructor(private val context: ReactApplicationContext) : ReactContextBaseJavaModule
42
+ (context) {
43
+ override fun getName(): String {
44
+ return "FlybitsModule"
45
+ }
46
+
47
+ private var pushNetwork: PushProviderType = PushProviderType.FCM
48
+
49
+ @ReactMethod
50
+ fun configureFlybits(config: ReadableMap, promise: Promise) {
51
+ val projectId = config.getString("projectId")
52
+ val gateway = config.getString("gateway")
53
+ val webService = config.getString("webService")
54
+
55
+ val logLevel = config.getString("logLevel") ?: ""
56
+ if (logLevel.lowercase() == "debug") {
57
+ Concierge.setLoggingVerbosity(VerbosityLevel.ALL)
58
+ } else {
59
+ Concierge.setLoggingVerbosity(VerbosityLevel.NONE)
60
+ }
61
+
62
+ if (config.hasKey("pushNetwork")) {
63
+ pushNetwork = when (config.getString("pushNetwork")?.lowercase()) {
64
+ "fcm" -> PushProviderType.FCM
65
+ "huawei" -> PushProviderType.HUAWEI
66
+ else -> PushProviderType.FCM
67
+ }
68
+ }
69
+
70
+ var webPoolSize = 0
71
+ if (config.hasKey("webPoolSize")) {
72
+ try {
73
+ webPoolSize = config.getInt("webPoolSize")
74
+ } catch (_: Exception) {
75
+ }
76
+ }
77
+
78
+ var uploadPushTokenOnConnect = false
79
+ if (config.hasKey("uploadPushTokenOnConnect")) {
80
+ try {
81
+ uploadPushTokenOnConnect = config.getBoolean("uploadPushTokenOnConnect")
82
+ } catch (_: Exception) {
83
+ }
84
+ }
85
+
86
+ var timeToRefreshInterval = 10
87
+ if (config.hasKey("timeToRefreshInterval")) {
88
+ try {
89
+ timeToRefreshInterval = config.getInt("timeToRefreshInterval")
90
+ } catch (_: Exception) {
91
+ }
92
+ }
93
+
94
+ var timeToRefreshNoDataInterval = 2
95
+ if (config.hasKey("timeToRefreshNoDataInterval")) {
96
+ try {
97
+ timeToRefreshNoDataInterval = config.getInt("timeToRefreshNoDataInterval")
98
+ } catch (_: Exception) {
99
+ }
100
+ }
101
+
102
+ var locationPlugin = false
103
+ if (config.hasKey("locationPlugin")) {
104
+ try {
105
+ locationPlugin = config.getBoolean("locationPlugin")
106
+ } catch (_: Exception) {
107
+ }
108
+ }
109
+
110
+ if (projectId != null && gateway != null && gateway.isNotEmpty() && projectId.isNotEmpty
111
+ ()) {
112
+ val conciergeConfiguration = FlybitsConciergeConfiguration.Builder(context)
113
+ .setProjectId(projectId)
114
+ .setGatewayUrl(gateway)
115
+ .setWebService(webService ?: "https://static-files-concierge.demo.flybits.com/latest")
116
+ .setWebViewPoolingSize(webPoolSize)
117
+ .setUploadPushtokenOnConnect(uploadPushTokenOnConnect)
118
+ .setTimeToRefreshInterval(timeToRefreshInterval)
119
+ .setTimeToRefreshNoDataInterval(timeToRefreshNoDataInterval)
120
+ when (pushNetwork) {
121
+
122
+ PushProviderType.HUAWEI -> {
123
+ try {
124
+ val huaweiPushProvider = Class.forName(HUAWEI_PACKAGE_CONSTANT).kotlin.objectInstance
125
+ if (huaweiPushProvider != null) {
126
+ get(context).storePushProviderType(PushProviderType.HUAWEI)
127
+ conciergeConfiguration.setPushProvider(huaweiPushProvider as PushProvider)
128
+ }
129
+ } catch (_: Exception) { }
130
+ }
131
+
132
+ else -> {
133
+ conciergeConfiguration.setPushProvider(FcmV2DeliveryProvider)
134
+ }
135
+ }
136
+
137
+ val pluginArrayList = arrayListOf<ContextManager.PluginType.ReservedPlugin>()
138
+ if (locationPlugin) {
139
+ pluginArrayList.add(ContextManager.PluginType.ReservedPlugin
140
+ (ReservedContextPlugin
141
+ .LOCATION))
142
+ }
143
+
144
+ val locales = config.getString("locales")
145
+ val localeList = ArrayList<Locale>()
146
+ if (locales != null) {
147
+ val cleanedString = locales.removePrefix("[").removeSuffix("]").replace("\"", "")
148
+ val languages = cleanedString.split(",")
149
+ languages.forEach {
150
+ if (it.isNotEmpty()) {
151
+ if (it.contains("_")) {
152
+ val localePart = it.split("_")
153
+ localeList.add(Locale(localePart[0], localePart[1]))
154
+ } else {
155
+ localeList.add(Locale(it))
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ Concierge.configure(conciergeConfiguration.build(), pluginArrayList, context, arrayListOf(),
162
+ customLanguages = localeList)
163
+
164
+ val result = handleSuccess()
165
+ promise.resolve(result)
166
+ } else {
167
+ val result = handleError(code = 1)
168
+ promise.reject(TAG_FLYBITS_MODULE, result)
169
+ }
170
+ }
171
+
172
+ @ReactMethod
173
+ fun anonymousConnect(promise: Promise) {
174
+ Concierge.connect(
175
+ context = context,
176
+ idp = AnonymousConciergeIDP(),
177
+ fetchConfiguration = true,
178
+ basicResultCallback = object : BasicResultCallback {
179
+ override fun onSuccess() {
180
+ val result = handleSuccess()
181
+ promise.resolve(result)
182
+ }
183
+
184
+ override fun onException(exception: FlybitsException) {
185
+ val result = handleError(exception)
186
+ promise.resolve(result)
187
+ }
188
+ }
189
+ )
190
+ }
191
+
192
+ @ReactMethod
193
+ fun authenticateJwtSignIn(jwt: String, promise: Promise) {
194
+ Concierge.connect(
195
+ context = context,
196
+ idp = JwtLoginConciergeIDP(clientJwt = jwt),
197
+ fetchConfiguration = true,
198
+ basicResultCallback = object : BasicResultCallback {
199
+ override fun onSuccess() {
200
+ val result = handleSuccess()
201
+ promise.resolve(result)
202
+ }
203
+
204
+ override fun onException(exception: FlybitsException) {
205
+ val result = handleError(exception)
206
+ promise.resolve(result)
207
+ }
208
+ }
209
+ )
210
+ }
211
+
212
+ @ReactMethod
213
+ fun disconnect(promise: Promise) {
214
+ Concierge.disconnect(
215
+ context = context,
216
+ callback = object : BasicResultCallback {
217
+ override fun onException(exception: FlybitsException) {
218
+ val result = handleError(exception)
219
+ promise.resolve(result)
220
+ }
221
+
222
+ override fun onSuccess() {
223
+ val result = handleSuccess()
224
+ promise.resolve(result)
225
+ }
226
+ }
227
+ )
228
+ }
229
+
230
+ @ReactMethod
231
+ fun isConnected(promise: Promise) {
232
+ try {
233
+ val isConnected = Concierge.isConnected(context = context)
234
+ if (isConnected) {
235
+ val result = handleSuccess(result = "true")
236
+ promise.resolve(result)
237
+ } else {
238
+ val result = handleSuccess(result = "false")
239
+ promise.resolve(result)
240
+ }
241
+ } catch (e: FlybitsException) {
242
+ val result = handleError(e)
243
+ promise.reject(TAG_FLYBITS_MODULE, result)
244
+ }
245
+ }
246
+
247
+ @ReactMethod
248
+ fun actionlink(notification: ReadableMap, promise: Promise) {
249
+ try {
250
+ var dataMap: HashMap<String, Any?> = HashMap()
251
+ var conciergePush: ConciergePush? = null
252
+
253
+ when (pushNetwork) {
254
+ PushProviderType.FCM -> {
255
+ dataMap = readableMapToHashMap(notification.getMap("data"))
256
+ conciergePush = Concierge.handlePush(dataMap as HashMap<String, String>)
257
+ }
258
+
259
+ PushProviderType.HUAWEI -> {
260
+ dataMap = parseHuaweiNotification(notification)
261
+ val title = dataMap["title"] as? String
262
+ if (title != null) {
263
+ val encodedTitle = URLEncoder.encode(title, StandardCharsets.UTF_8.toString())
264
+ dataMap["title"] = encodedTitle
265
+ }
266
+ val alert = dataMap["alert"] as? String
267
+ if (alert != null) {
268
+ val encodedAlert = URLEncoder.encode(alert, StandardCharsets.UTF_8.toString())
269
+ dataMap["alert"] = encodedAlert
270
+ }
271
+
272
+ val body = dataMap["body"] as? HashMap<String, Any>
273
+ if (body != null) {
274
+ val actionScheme = body["actionScheme"] as? String
275
+ if (actionScheme != null) {
276
+ val encodedActionScheme = URLEncoder.encode(actionScheme, StandardCharsets.UTF_8.toString())
277
+ body["actionScheme"] = encodedActionScheme
278
+ }
279
+ }
280
+ conciergePush = Concierge.handlePush(dataMap.toString())
281
+ }
282
+
283
+ null -> {
284
+ // No-op
285
+ }
286
+
287
+ else -> {
288
+ dataMap = HashMap()
289
+ }
290
+ }
291
+ conciergePush?.let {
292
+ val uri = Concierge.getActionableLink(it)
293
+ val result = handleSuccess(result = uri.toString())
294
+ promise.resolve(result)
295
+ } ?: run {
296
+ val result = handleError(code = 2)
297
+ promise.resolve(result)
298
+ }
299
+ } catch (e: Exception) {
300
+ val flybitsException = FlybitsException(e)
301
+ val result = handleError(exception = flybitsException)
302
+ promise.reject(TAG_FLYBITS_MODULE, result)
303
+ }
304
+ }
305
+
306
+ @ReactMethod
307
+ fun actionTypeSchemeParser(actionableLink: String, promise: Promise) {
308
+ try {
309
+ if (actionableLink.isNotEmpty()) {
310
+ val type: Type = object : TypeToken<Map<String, Any>>() {}.type
311
+ val map: Map<String, Any> = Gson().fromJson(actionableLink, type)
312
+ val resultValue = map["result"]
313
+ val unescapedResult = resultValue.toString()
314
+ .replace("\\u003d", "=")
315
+ .replace("\\u0026", "&")
316
+
317
+ val actionTypeParser = ActionTypeSchemeParser.parse(unescapedResult)
318
+ val jsonObject = Gson().toJson(actionTypeParser)
319
+ val result = handleSuccess(result = jsonObject)
320
+ promise.resolve(result)
321
+ } else {
322
+ val result = handleError(code = 3)
323
+ promise.resolve(result)
324
+ }
325
+ } catch (e: Exception) {
326
+ val flybitsException = FlybitsException(e)
327
+ val result = handleError(exception = flybitsException)
328
+ promise.reject(TAG_FLYBITS_MODULE, result)
329
+ }
330
+ }
331
+
332
+ @ReactMethod
333
+ fun isFlybitsPush(notification: ReadableMap, promise: Promise) {
334
+ try {
335
+ var map: HashMap<String, Any?> = HashMap()
336
+ when (pushNetwork) {
337
+ PushProviderType.FCM -> {
338
+ map = readableMapToHashMap(notification.getMap("data"))
339
+ }
340
+
341
+ PushProviderType.HUAWEI -> {
342
+ map = parseHuaweiNotification(notification)
343
+ }
344
+
345
+ null -> {
346
+ }
347
+
348
+ else -> {
349
+ map = HashMap()
350
+ }
351
+ }
352
+
353
+ if (map.containsKey("provider") && map["provider"].toString().lowercase() == "flybits") {
354
+ val result = handleSuccess(result = "true")
355
+ promise.resolve(result)
356
+ } else {
357
+ val result = handleSuccess(result = "false")
358
+ promise.resolve(result)
359
+ }
360
+ } catch (e: Exception) {
361
+ val flybitsException = FlybitsException(e)
362
+ val result = handleError(exception = flybitsException)
363
+ promise.reject(TAG_FLYBITS_MODULE, result)
364
+ }
365
+ }
366
+
367
+ private fun extractHuaweiPushMap(
368
+ outerMap: Map<String, *>,
369
+ key: String
370
+ ): Map<String, *>? {
371
+ val innerValue = outerMap[key]
372
+ return innerValue as? Map<String, *>
373
+ }
374
+
375
+ private fun parseHuaweiNotification(notification: ReadableMap): HashMap<String, Any?> {
376
+ val dateOfMapObject = notification.getString("dataOfMap")
377
+ dateOfMapObject?.let {
378
+ val pushBodyObject = extractHuaweiPushMap(JSONObject(it).toMap(), "pushbody")
379
+ pushBodyObject?.let {
380
+ return HashMap(it)
381
+ }
382
+ } ?: run {
383
+ val dateOfMapObjectNotification = notification.getMap("extras")
384
+ dateOfMapObjectNotification?.let {
385
+ val innerBody = it.getMap("notification")
386
+ innerBody?.getString("message")?.let {
387
+ val pushBodyObject = extractHuaweiPushMap(JSONObject(it).toMap(), "pushbody")
388
+ pushBodyObject?.let {
389
+ return HashMap(it)
390
+ }
391
+ }
392
+ }
393
+ }
394
+ return HashMap()
395
+ }
396
+
397
+ @ReactMethod
398
+ public fun notificationStatus(promise: Promise) {
399
+ val status = Concierge.pushTokenUploadStatus(context = context)
400
+ when (status) {
401
+ PushTokenStatus.SENT -> {
402
+ val result = handleSuccess(result = "sent")
403
+ promise.resolve(result)
404
+ }
405
+
406
+ PushTokenStatus.DELETED -> {
407
+ val result = handleSuccess(result = "deleted")
408
+ promise.resolve(result)
409
+ }
410
+
411
+ PushTokenStatus.UNKNOWN -> {
412
+ val result = handleSuccess(result = "unknown")
413
+ promise.resolve(result)
414
+ }
415
+
416
+ else -> {
417
+ val result = handleSuccess(result = "unknown")
418
+ promise.resolve(result)
419
+ }
420
+ }
421
+ }
422
+
423
+ @ReactMethod
424
+ public fun addToken(token: String, promise: Promise) {
425
+ Concierge.sendPush(
426
+ token = token,
427
+ context = context,
428
+ parameters = null,
429
+ callback = object : BasicResultCallback {
430
+ override fun onException(exception: FlybitsException) {
431
+ val result = handleError(exception)
432
+ promise.resolve(result)
433
+ }
434
+
435
+ override fun onSuccess() {
436
+ val result = handleSuccess()
437
+ promise.resolve(result)
438
+ }
439
+ }
440
+ )
441
+ }
442
+
443
+ @ReactMethod
444
+ public fun removeToken(promise: Promise) {
445
+ Concierge.deletePushToken(
446
+ context = context,
447
+ callback = object : BasicResultCallback {
448
+ override fun onException(exception: FlybitsException) {
449
+ val result = handleError(exception)
450
+ promise.resolve(result)
451
+ }
452
+
453
+ override fun onSuccess() {
454
+ val result = handleSuccess()
455
+ promise.resolve(result)
456
+ }
457
+ }
458
+ )
459
+ }
460
+ }
@@ -0,0 +1,57 @@
1
+ import com.facebook.react.bridge.Arguments
2
+ import com.facebook.react.bridge.ReadableMap
3
+ import com.facebook.react.bridge.ReadableType
4
+ import com.facebook.react.bridge.WritableMap
5
+ import com.flybits.commons.library.exceptions.FlybitsException
6
+ import com.flybits.flybitscoreconcierge.actiontypes.ActionTypeSchemeParser
7
+ import com.google.gson.Gson
8
+ import okhttp3.Response
9
+
10
+ fun readableMapToHashMap(readableMap: ReadableMap?): HashMap<String, Any?> {
11
+ val hashMap = HashMap<String, Any?>()
12
+ if (readableMap == null) {
13
+ return hashMap
14
+ }
15
+
16
+ val iterator = readableMap.entryIterator
17
+ while (iterator.hasNext()) {
18
+ val entry = iterator.next()
19
+ val key = entry.key
20
+ val type = readableMap.getType(key)
21
+ when (type) {
22
+ ReadableType.Null -> hashMap[key] = null
23
+ ReadableType.Boolean -> hashMap[key] = readableMap.getBoolean(key)
24
+ ReadableType.Number -> hashMap[key] = readableMap.getDouble(key)
25
+ ReadableType.String -> hashMap[key] = readableMap.getString(key)
26
+ ReadableType.Map -> hashMap[key] = readableMapToHashMap(readableMap.getMap(key))
27
+ else -> throw IllegalArgumentException("Could not convert object with key: $key.")
28
+ }
29
+ }
30
+ return hashMap
31
+ }
32
+
33
+ fun handleSuccess(result: String = "ok") : String {
34
+ val responseMap: Map<String, Any?> = mapOf(
35
+ "success" to true,
36
+ "result" to (result ?: "")
37
+ )
38
+ return constructJsonResponse(responseMap)
39
+ }
40
+
41
+ fun handleError(exception: FlybitsException?=null, code:Int? = -1) :String {
42
+ val responseMap: Map<String, Any?> = mapOf(
43
+ "success" to false,
44
+ "code" to (exception?.errorCode ?: code),
45
+ "message" to (exception?.message ?: "")
46
+ )
47
+ return constructJsonResponse(responseMap)
48
+ }
49
+
50
+ fun constructJsonResponse(responseMap: Map<String, Any?>): String {
51
+ try {
52
+ val gson = Gson()
53
+ return gson.toJson(responseMap)
54
+ } catch (e: Exception) {
55
+ return ""
56
+ }
57
+ }
@@ -0,0 +1 @@
1
+ #import <React/RCTViewManager.h>
@@ -0,0 +1,40 @@
1
+ #import <React/RCTEventEmitter.h>
2
+ #import <React/RCTViewManager.h>
3
+ #import <React/RCTBridgeModule.h>
4
+
5
+
6
+ @interface RCT_EXTERN_MODULE(ConciergeViewManager, RCTViewManager)
7
+
8
+ RCT_EXPORT_VIEW_PROPERTY(onConciergeChange, RCTBubblingEventBlock)
9
+ RCT_EXPORT_VIEW_PROPERTY(zoneReferenceIDs, NSArray<NSString>)
10
+ RCT_EXPORT_VIEW_PROPERTY(onActionEvent, RCTBubblingEventBlock)
11
+ RCT_EXPORT_VIEW_PROPERTY(onAnalyticsEvent, RCTBubblingEventBlock)
12
+ RCT_EXPORT_VIEW_PROPERTY(onContentArrived, RCTBubblingEventBlock)
13
+ RCT_EXPORT_VIEW_PROPERTY(actionlink, NSDictionary)
14
+
15
+ @end
16
+
17
+
18
+ @interface RCT_EXTERN_MODULE(FlybitsModule, NSObject)
19
+
20
+ RCT_EXTERN_METHOD(configureFlybits: (NSDictionary) configuration resolved: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
21
+ RCT_EXTERN_METHOD(anonymousConnect: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
22
+ RCT_EXTERN_METHOD(isConnected: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
23
+ RCT_EXTERN_METHOD(disconnect: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
24
+ RCT_EXTERN_METHOD(notificationStatus: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
25
+ RCT_EXTERN_METHOD(addToken: (NSString)token resolved: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
26
+ RCT_EXTERN_METHOD(removeToken: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
27
+ RCT_EXTERN_METHOD(actionlink: (NSDictionary)userInfo resolved: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
28
+ RCT_EXTERN_METHOD(authenticateJwtSignIn: (NSString)jwt resolved: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
29
+ RCT_EXTERN_METHOD(isFlybitsPush: (NSDictionary)notification resolved: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
30
+ RCT_EXTERN_METHOD(actionTypeSchemeParser: (NSString)actionableLink resolved: (RCTPromiseResolveBlock)resolved rejected:(RCTPromiseRejectBlock))
31
+
32
+
33
+ @end
34
+
35
+ @interface RCT_EXTERN_MODULE(Auth, RCTEventEmitter)
36
+ @end
37
+
38
+ @interface RCT_EXTERN_MODULE(ConciergeView, RCTEventEmitter)
39
+ @end
40
+