infobip-mobile-messaging-react-native-plugin 14.8.3 → 14.9.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.
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +2 -6
- package/android/src/main/java/org/infobip/reactlibrary/mobilemessaging/CacheManager.java +35 -86
- package/android/src/main/java/org/infobip/reactlibrary/mobilemessaging/RNMMChatService.kt +12 -8
- package/android/src/main/java/org/infobip/reactlibrary/mobilemessaging/ReactNativeBroadcastReceiver.kt +10 -5
- package/android/src/main/java/org/infobip/reactlibrary/mobilemessaging/ReactNativeMobileMessagingService.kt +31 -10
- package/android/src/newarchitecture/java/org/infobip/reactlibrary/mobilemessaging/MobileMessagingModule.kt +8 -0
- package/android/src/oldarchitecture/java/org/infobip/reactlibrary/mobilemessaging/MobileMessagingModule.kt +9 -0
- package/infobip-mobile-messaging-react-native-plugin-14.9.0.tgz +0 -0
- package/infobip-mobile-messaging-react-native-plugin.podspec +4 -2
- package/ios/MobileMessagingPlugin/RNMobileMessaging.swift +108 -35
- package/ios/MobileMessagingPlugin/RNMobileMessagingBridge.m +3 -0
- package/ios/MobileMessagingPlugin/RNMobileMessagingConfiguration.swift +65 -17
- package/ios/MobileMessagingPlugin/RNMobileMessagingErrorExtension.swift +5 -0
- package/ios/MobileMessagingPlugin/RNMobileMessagingUtils.swift +6 -0
- package/package.json +1 -1
- package/src/index.d.ts +16 -2
- package/src/index.js +29 -3
- package/src/specs/NativeMobileMessaging.ts +3 -0
- package/infobip-mobile-messaging-react-native-plugin-14.8.2.tgz +0 -0
package/android/build.gradle
CHANGED
|
@@ -66,7 +66,7 @@ repositories {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
dependencies {
|
|
69
|
-
def mmVersion = '
|
|
69
|
+
def mmVersion = '15.0.0'
|
|
70
70
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:2.1.20"
|
|
71
71
|
compileOnly "com.facebook.react:react-android"
|
|
72
72
|
implementation "androidx.annotation:annotation:1.9.1"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
-
xmlns:tools="http://schemas.android.com/tools">
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
2
|
|
|
4
3
|
<!-- Mobile Messaging permissions -->
|
|
5
4
|
|
|
@@ -20,10 +19,7 @@
|
|
|
20
19
|
<uses-permission android:name="android.permission.CAMERA" />
|
|
21
20
|
<!-- Infobip rtc ui permissions -->
|
|
22
21
|
|
|
23
|
-
<application
|
|
24
|
-
tools:replace="android:usesCleartextTraffic"
|
|
25
|
-
android:usesCleartextTraffic="true"
|
|
26
|
-
>
|
|
22
|
+
<application>
|
|
27
23
|
<!-- Mobile Messaging components -->
|
|
28
24
|
<service
|
|
29
25
|
android:name="org.infobip.mobile.messaging.platform.MobileMessagingJobService"
|
|
@@ -12,23 +12,17 @@ import android.content.Context;
|
|
|
12
12
|
import android.content.SharedPreferences;
|
|
13
13
|
import android.preference.PreferenceManager;
|
|
14
14
|
|
|
15
|
-
import android.util.Log;
|
|
16
|
-
|
|
17
|
-
import org.infobip.mobile.messaging.api.support.http.serialization.JsonSerializer;
|
|
18
|
-
import org.infobip.mobile.messaging.dal.json.JSONArrayAdapter;
|
|
19
|
-
import org.infobip.mobile.messaging.dal.json.JSONObjectAdapter;
|
|
20
15
|
import org.json.JSONObject;
|
|
21
16
|
|
|
22
17
|
import java.util.ArrayList;
|
|
23
|
-
import java.util.
|
|
24
|
-
import java.util.HashSet;
|
|
18
|
+
import java.util.Iterator;
|
|
25
19
|
import java.util.List;
|
|
26
|
-
import java.util.Set;
|
|
27
20
|
|
|
28
21
|
class CacheManager {
|
|
29
|
-
private static final String
|
|
22
|
+
private static final String LEGACY_EVENTS_KEY = Utils.TAG + ".cache.events";
|
|
30
23
|
private static final Object cacheLock = new Object();
|
|
31
|
-
private static final
|
|
24
|
+
private static final List<Event> cachedEvents = new ArrayList<>();
|
|
25
|
+
private static final int MAX_CACHE_SIZE = 100;
|
|
32
26
|
|
|
33
27
|
static class Event {
|
|
34
28
|
String type;
|
|
@@ -45,102 +39,57 @@ class CacheManager {
|
|
|
45
39
|
public String toString() {
|
|
46
40
|
return type;
|
|
47
41
|
}
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
static void saveEvent(Context context, String event, JSONObject object, String actionId, String actionInputText) {
|
|
52
|
-
if (context == null) {
|
|
53
|
-
RNMMLogger.e(Utils.TAG, "context is null, can't cache event " + event);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
String serialized = serializer.serialize(new Event(event, object, actionId, actionInputText));
|
|
57
|
-
saveStringsToSet(context, EVENTS_KEY, serialized);
|
|
58
42
|
}
|
|
59
43
|
|
|
60
|
-
static void saveEvent(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//int `unreadMessagesCounter` isn't a JSONObject, so it'll go as a second argument
|
|
66
|
-
String serialized = serializer.serialize(new Event(event, null, unreadMessagesCounter));
|
|
67
|
-
saveStringsToSet(context, EVENTS_KEY, serialized);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
static Event[] loadEvents(Context context, String eventType) {
|
|
71
|
-
if (context == null) {
|
|
72
|
-
RNMMLogger.e(Utils.TAG, "context is null, can't load cached events " + eventType);
|
|
73
|
-
return new Event[0];
|
|
74
|
-
}
|
|
75
|
-
Set<String> serialized = getStringSet(context, EVENTS_KEY);
|
|
76
|
-
if (serialized == null || serialized.isEmpty()) {
|
|
77
|
-
return new Event[0];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
List<Event> matched = new ArrayList<>();
|
|
81
|
-
Set<String> unmatchedSerialized = new HashSet<>();
|
|
82
|
-
for (String s : serialized) {
|
|
83
|
-
Event e = serializer.deserialize(s, Event.class);
|
|
84
|
-
if (eventType.equals(e.type)) {
|
|
85
|
-
matched.add(e);
|
|
86
|
-
} else {
|
|
87
|
-
unmatchedSerialized.add(s);
|
|
44
|
+
static void saveEvent(String event, JSONObject object, String actionId, String actionInputText) {
|
|
45
|
+
synchronized (cacheLock) {
|
|
46
|
+
if (cachedEvents.size() >= MAX_CACHE_SIZE) {
|
|
47
|
+
Event removed = cachedEvents.remove(0);
|
|
48
|
+
RNMMLogger.w(Utils.TAG, "Cache full, dropping oldest event: " + removed.type);
|
|
88
49
|
}
|
|
50
|
+
cachedEvents.add(new Event(event, object, actionId, actionInputText));
|
|
89
51
|
}
|
|
90
|
-
|
|
91
|
-
updateStringsSet(context, EVENTS_KEY, unmatchedSerialized);
|
|
92
|
-
|
|
93
|
-
return matched.toArray(new Event[matched.size()]);
|
|
94
52
|
}
|
|
95
53
|
|
|
96
|
-
|
|
97
|
-
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
54
|
+
static void saveEvent(String event, int unreadMessagesCounter) {
|
|
98
55
|
synchronized (cacheLock) {
|
|
99
|
-
|
|
56
|
+
if (cachedEvents.size() >= MAX_CACHE_SIZE) {
|
|
57
|
+
Event removed = cachedEvents.remove(0);
|
|
58
|
+
RNMMLogger.w(Utils.TAG, "Cache full, dropping oldest event: " + removed.type);
|
|
59
|
+
}
|
|
60
|
+
cachedEvents.add(new Event(event, null, unreadMessagesCounter));
|
|
100
61
|
}
|
|
101
62
|
}
|
|
102
63
|
|
|
103
|
-
|
|
104
|
-
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
105
|
-
Set<String> set;
|
|
64
|
+
static Event[] loadEvents(String eventType) {
|
|
106
65
|
synchronized (cacheLock) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
66
|
+
List<Event> matched = new ArrayList<>();
|
|
67
|
+
Iterator<Event> iterator = cachedEvents.iterator();
|
|
68
|
+
while (iterator.hasNext()) {
|
|
69
|
+
Event e = iterator.next();
|
|
70
|
+
if (eventType.equals(e.type)) {
|
|
71
|
+
matched.add(e);
|
|
72
|
+
iterator.remove();
|
|
73
|
+
}
|
|
110
74
|
}
|
|
111
|
-
|
|
112
|
-
.edit()
|
|
113
|
-
.remove(key)
|
|
114
|
-
.apply();
|
|
75
|
+
return matched.toArray(new Event[0]);
|
|
115
76
|
}
|
|
116
|
-
return set;
|
|
117
77
|
}
|
|
118
78
|
|
|
119
|
-
|
|
120
|
-
private static Set<String> saveStringsToSet(Context context, String key, String... strings) {
|
|
121
|
-
return saveStringSet(context, key, new HashSet<String>(Arrays.asList(strings)));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
private static Set<String> saveStringSet(Context context, String key, Set<String> newSet) {
|
|
125
|
-
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
79
|
+
static void clearCache() {
|
|
126
80
|
synchronized (cacheLock) {
|
|
127
|
-
|
|
128
|
-
newSet.addAll(set);
|
|
129
|
-
sharedPreferences
|
|
130
|
-
.edit()
|
|
131
|
-
.putStringSet(key, newSet)
|
|
132
|
-
.apply();
|
|
133
|
-
return set;
|
|
81
|
+
cachedEvents.clear();
|
|
134
82
|
}
|
|
135
83
|
}
|
|
136
84
|
|
|
137
|
-
|
|
85
|
+
static void cleanupLegacyCache(Context context) {
|
|
86
|
+
if (context == null) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
138
89
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
139
|
-
|
|
140
|
-
sharedPreferences
|
|
141
|
-
|
|
142
|
-
.putStringSet(key, newSet)
|
|
143
|
-
.apply();
|
|
90
|
+
if (sharedPreferences.contains(LEGACY_EVENTS_KEY)) {
|
|
91
|
+
sharedPreferences.edit().remove(LEGACY_EVENTS_KEY).apply();
|
|
92
|
+
RNMMLogger.d(Utils.TAG, "Cleaned up legacy SharedPreferences event cache");
|
|
144
93
|
}
|
|
145
94
|
}
|
|
146
95
|
}
|
|
@@ -392,18 +392,22 @@ class RNMMChatEventReceiver : ReactNativeBroadcastReceiver() {
|
|
|
392
392
|
emitOrCache(context, RNMMChatService.EVENT_INAPPCHAT_UNREAD_MESSAGES_COUNT_UPDATED, unreadChatMessagesCounter)
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
+
// Early-exit when plugin is not initialized avoids calling getReactContext() which throws
|
|
396
|
+
// UnsupportedOperationException on new architecture when app is killed. When reactContext is
|
|
397
|
+
// null (e.g. during lifecycle transitions), events are cached regardless of jsHasListeners
|
|
398
|
+
// since sending requires a valid ReactContext. This preserves the original behavior.
|
|
395
399
|
private fun emitOrCache(context: Context?, eventType: String, unreadMessagesCounter: Int) {
|
|
400
|
+
if (!pluginInitialized) {
|
|
401
|
+
CacheManager.saveEvent(eventType, unreadMessagesCounter)
|
|
402
|
+
return
|
|
403
|
+
}
|
|
396
404
|
val reactContext: ReactContext? = getReactContext(context)
|
|
397
|
-
if (
|
|
398
|
-
CacheManager.saveEvent(
|
|
399
|
-
} else if (jsHasListeners
|
|
405
|
+
if (reactContext == null) {
|
|
406
|
+
CacheManager.saveEvent(eventType, unreadMessagesCounter)
|
|
407
|
+
} else if (jsHasListeners) {
|
|
400
408
|
ReactNativeEvent.send(eventType, reactContext, unreadMessagesCounter)
|
|
401
|
-
} else if (reactContext != null) {
|
|
402
|
-
CacheManager.saveEvent(reactContext, eventType, unreadMessagesCounter)
|
|
403
|
-
} else if (context != null) {
|
|
404
|
-
CacheManager.saveEvent(context, eventType, unreadMessagesCounter)
|
|
405
409
|
} else {
|
|
406
|
-
|
|
410
|
+
CacheManager.saveEvent(eventType, unreadMessagesCounter)
|
|
407
411
|
}
|
|
408
412
|
}
|
|
409
413
|
}
|
|
@@ -27,11 +27,16 @@ abstract class ReactNativeBroadcastReceiver : BroadcastReceiver() {
|
|
|
27
27
|
get() = ReactNativeMobileMessagingService.jsHasListeners
|
|
28
28
|
|
|
29
29
|
protected fun getReactContext(context: Context?): ReactContext? {
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
-
?.
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
return try {
|
|
31
|
+
ReactNativeMobileMessagingService.lastReactContext
|
|
32
|
+
?: (context?.applicationContext as? ReactApplication)
|
|
33
|
+
?.reactNativeHost
|
|
34
|
+
?.reactInstanceManager
|
|
35
|
+
?.currentReactContext
|
|
36
|
+
} catch (e: Exception) {
|
|
37
|
+
RNMMLogger.w(Utils.TAG, "Failed to get ReactContext: ${e.message}")
|
|
38
|
+
null
|
|
39
|
+
}
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
}
|
|
@@ -417,6 +417,7 @@ class ReactNativeMobileMessagingService(
|
|
|
417
417
|
ctx.userAttributes,
|
|
418
418
|
ctx.forceDepersonalize,
|
|
419
419
|
ctx.keepAsLead,
|
|
420
|
+
ctx.setDeviceAsPrimary,
|
|
420
421
|
object : MobileMessaging.ResultListener<User>() {
|
|
421
422
|
override fun onResult(result: Result<User, MobileMessagingError>) {
|
|
422
423
|
if (result.isSuccess()) {
|
|
@@ -448,6 +449,7 @@ class ReactNativeMobileMessagingService(
|
|
|
448
449
|
mobileMessaging.depersonalize(object : MobileMessaging.ResultListener<SuccessPending>() {
|
|
449
450
|
override fun onResult(result: Result<SuccessPending, MobileMessagingError>) {
|
|
450
451
|
if (result.isSuccess) {
|
|
452
|
+
mobileMessaging.setJwtSupplier { null }
|
|
451
453
|
val state = when (result.data) {
|
|
452
454
|
SuccessPending.Pending -> "pending"
|
|
453
455
|
SuccessPending.Success -> "success"
|
|
@@ -510,10 +512,25 @@ class ReactNativeMobileMessagingService(
|
|
|
510
512
|
successCallback.invoke(readableMap)
|
|
511
513
|
}
|
|
512
514
|
|
|
515
|
+
fun cleanup(successCallback: Callback, errorCallback: Callback) {
|
|
516
|
+
RNMMLogger.d(Utils.TAG, "Cleanup...")
|
|
517
|
+
try {
|
|
518
|
+
mobileMessaging.setJwtSupplier { null }
|
|
519
|
+
mobileMessaging.cleanup()
|
|
520
|
+
successCallback.invoke()
|
|
521
|
+
} catch (e: Exception) {
|
|
522
|
+
errorCallback.invoke(Utils.callbackError(e.message, null))
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
513
526
|
fun setUserDataJwt(jwt: String?, successCallback: Callback, errorCallback: Callback) {
|
|
514
527
|
RNMMLogger.d(Utils.TAG, "SetUserDataJwt...")
|
|
515
528
|
try {
|
|
516
|
-
|
|
529
|
+
if (jwt.isNullOrEmpty()) {
|
|
530
|
+
mobileMessaging.setJwtSupplier(null)
|
|
531
|
+
} else {
|
|
532
|
+
mobileMessaging.setJwtSupplier { jwt }
|
|
533
|
+
}
|
|
517
534
|
successCallback.invoke()
|
|
518
535
|
} catch (e: Exception) {
|
|
519
536
|
errorCallback.invoke(Utils.callbackError(e.message, null))
|
|
@@ -951,7 +968,7 @@ class ReactNativeMobileMessagingService(
|
|
|
951
968
|
fun addListener(eventName: String) {
|
|
952
969
|
RNMMLogger.d(Utils.TAG, "addListener: $eventName")
|
|
953
970
|
jsHasListeners = true
|
|
954
|
-
val events = CacheManager.loadEvents(
|
|
971
|
+
val events = CacheManager.loadEvents(eventName)
|
|
955
972
|
for (event in events) {
|
|
956
973
|
if (eventName == event.type) {
|
|
957
974
|
ReactNativeEvent.send(event.type, reactContext, event.jsonObject, *event.objects)
|
|
@@ -1088,18 +1105,22 @@ class MessageEventReceiver : ReactNativeBroadcastReceiver() {
|
|
|
1088
1105
|
emitOrCache(event, context, message, actionId, actionInputText)
|
|
1089
1106
|
}
|
|
1090
1107
|
|
|
1108
|
+
// Early-exit when plugin is not initialized avoids calling getReactContext() which throws
|
|
1109
|
+
// UnsupportedOperationException on new architecture when app is killed. When reactContext is
|
|
1110
|
+
// null (e.g. during lifecycle transitions), events are cached regardless of jsHasListeners
|
|
1111
|
+
// since sending requires a valid ReactContext. This preserves the original behavior.
|
|
1091
1112
|
private fun emitOrCache(eventType: String, context: Context?, message: JSONObject?, actionId: String?, actionInputText: String?) {
|
|
1113
|
+
if (!pluginInitialized) {
|
|
1114
|
+
CacheManager.saveEvent(eventType, message, actionId, actionInputText)
|
|
1115
|
+
return
|
|
1116
|
+
}
|
|
1092
1117
|
val reactContext: ReactContext? = getReactContext(context)
|
|
1093
|
-
if (
|
|
1094
|
-
CacheManager.saveEvent(
|
|
1095
|
-
} else if (jsHasListeners
|
|
1118
|
+
if (reactContext == null) {
|
|
1119
|
+
CacheManager.saveEvent(eventType, message, actionId, actionInputText)
|
|
1120
|
+
} else if (jsHasListeners) {
|
|
1096
1121
|
ReactNativeEvent.send(eventType, reactContext, message, actionId, actionInputText)
|
|
1097
|
-
} else if (reactContext != null) {
|
|
1098
|
-
CacheManager.saveEvent(reactContext, eventType, message, actionId, actionInputText)
|
|
1099
|
-
} else if (context != null) {
|
|
1100
|
-
CacheManager.saveEvent(context, eventType, message, actionId, actionInputText)
|
|
1101
1122
|
} else {
|
|
1102
|
-
|
|
1123
|
+
CacheManager.saveEvent(eventType, message, actionId, actionInputText)
|
|
1103
1124
|
}
|
|
1104
1125
|
}
|
|
1105
1126
|
|
|
@@ -27,6 +27,7 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : NativeMobil
|
|
|
27
27
|
|
|
28
28
|
init {
|
|
29
29
|
reactApplicationContext.addLifecycleEventListener(this)
|
|
30
|
+
CacheManager.cleanupLegacyCache(reactApplicationContext)
|
|
30
31
|
ReactNativeMobileMessagingService.pluginInitialized = true
|
|
31
32
|
service.registerBroadcastReceiver()
|
|
32
33
|
}
|
|
@@ -159,6 +160,12 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : NativeMobil
|
|
|
159
160
|
service.showDialogForError(errorCode, successCallback, errorCallback)
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
// Cleanup
|
|
164
|
+
override fun cleanup(successCallback: Callback, errorCallback: Callback) {
|
|
165
|
+
RNMMLogger.d(TAG, "Cleanup...")
|
|
166
|
+
service.cleanup(successCallback, errorCallback)
|
|
167
|
+
}
|
|
168
|
+
|
|
162
169
|
// JWT
|
|
163
170
|
override fun setUserDataJwt(jwt: String?, successCallback: Callback, errorCallback: Callback) {
|
|
164
171
|
RNMMLogger.d(TAG, "SetUserDataJwt...")
|
|
@@ -199,6 +206,7 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : NativeMobil
|
|
|
199
206
|
override fun onHostDestroy() {
|
|
200
207
|
ReactNativeMobileMessagingService.pluginInitialized = false
|
|
201
208
|
service.unregisterBroadcastReceiver()
|
|
209
|
+
CacheManager.clearCache()
|
|
202
210
|
reactApplicationContext.removeLifecycleEventListener(this)
|
|
203
211
|
ReactNativeMobileMessagingService.unregisterService(reactApplicationContext)
|
|
204
212
|
}
|
|
@@ -28,6 +28,7 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : ReactContex
|
|
|
28
28
|
|
|
29
29
|
init {
|
|
30
30
|
reactApplicationContext.addLifecycleEventListener(this)
|
|
31
|
+
CacheManager.cleanupLegacyCache(reactApplicationContext)
|
|
31
32
|
ReactNativeMobileMessagingService.pluginInitialized = true
|
|
32
33
|
service.registerBroadcastReceiver()
|
|
33
34
|
}
|
|
@@ -186,6 +187,13 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : ReactContex
|
|
|
186
187
|
service.submitEventImmediately(eventData, successCallback, errorCallback)
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
// Cleanup
|
|
191
|
+
@ReactMethod
|
|
192
|
+
fun cleanup(successCallback: Callback, errorCallback: Callback) {
|
|
193
|
+
RNMMLogger.d(TAG, "Cleanup...")
|
|
194
|
+
service.cleanup(successCallback, errorCallback)
|
|
195
|
+
}
|
|
196
|
+
|
|
189
197
|
// JWT
|
|
190
198
|
@ReactMethod
|
|
191
199
|
fun setUserDataJwt(jwt: String?, successCallback: Callback, errorCallback: Callback) {
|
|
@@ -230,6 +238,7 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : ReactContex
|
|
|
230
238
|
|
|
231
239
|
override fun onHostDestroy() {
|
|
232
240
|
service.unregisterBroadcastReceiver()
|
|
241
|
+
CacheManager.clearCache()
|
|
233
242
|
reactApplicationContext.removeLifecycleEventListener(this)
|
|
234
243
|
ReactNativeMobileMessagingService.pluginInitialized = false
|
|
235
244
|
ReactNativeMobileMessagingService.unregisterService(reactApplicationContext)
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
|
|
3
3
|
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
-
mmVersion = "15.
|
|
4
|
+
mmVersion = "15.5.0"
|
|
5
5
|
|
|
6
6
|
Pod::Spec.new do |s|
|
|
7
7
|
s.name = "infobip-mobile-messaging-react-native-plugin"
|
|
@@ -20,7 +20,9 @@ Pod::Spec.new do |s|
|
|
|
20
20
|
s.requires_arc = true
|
|
21
21
|
|
|
22
22
|
s.dependency "React-Core"
|
|
23
|
-
|
|
23
|
+
if ENV['RCT_USE_PREBUILT_RNCORE'] == '1'
|
|
24
|
+
s.dependency "React-Core-prebuilt"
|
|
25
|
+
end
|
|
24
26
|
|
|
25
27
|
s.dependency "MobileMessaging/Core", mmVersion
|
|
26
28
|
s.dependency "MobileMessaging/InAppChat", mmVersion
|
|
@@ -98,60 +98,109 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
|
|
|
98
98
|
|
|
99
99
|
@objc(init:onSuccess:onError:)
|
|
100
100
|
func start(config: NSDictionary, onSuccess: @escaping RCTResponseSenderBlock, onError: @escaping RCTResponseSenderBlock) {
|
|
101
|
-
guard
|
|
101
|
+
guard var userConfigDict = config as? [String : AnyObject],
|
|
102
|
+
let applicationCode = userConfigDict.removeValue(forKey: RNMobileMessagingConfiguration.Keys.applicationCode) as? String,
|
|
103
|
+
let configuration = RNMobileMessagingConfiguration(rawConfig: userConfigDict)
|
|
104
|
+
else {
|
|
102
105
|
onError([NSError(type: .InvalidArguments).reactNativeObject])
|
|
103
106
|
return
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
let successCallback: RCTResponseSenderBlock = { [weak self] response in
|
|
107
|
-
RNMobileMessagingConfiguration.saveConfigToDefaults(rawConfig:
|
|
110
|
+
RNMobileMessagingConfiguration.saveConfigToDefaults(rawConfig: userConfigDict)
|
|
108
111
|
self?.isStarted = true
|
|
109
112
|
onSuccess(response)
|
|
110
113
|
}
|
|
111
|
-
|
|
114
|
+
|
|
112
115
|
let cachedConfigDict = RNMobileMessagingConfiguration.getRawConfigFromDefaults()
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
let keychainApplicationCode = MobileMessaging.getKeychainApplicationCode()
|
|
117
|
+
let shouldRestart = needsRestart(userConfigDict: userConfigDict, applicationCode: applicationCode)
|
|
118
|
+
let shouldStart = cachedConfigDict == nil || keychainApplicationCode == nil
|
|
119
|
+
|
|
120
|
+
if shouldRestart {
|
|
115
121
|
stop {
|
|
116
|
-
self.
|
|
122
|
+
self.startWithApplicationCode(
|
|
123
|
+
configuration: configuration,
|
|
124
|
+
applicationCode: applicationCode,
|
|
125
|
+
onSuccess: successCallback,
|
|
126
|
+
onError: onError
|
|
127
|
+
)
|
|
117
128
|
}
|
|
118
|
-
} else if
|
|
119
|
-
|
|
129
|
+
} else if shouldStart {
|
|
130
|
+
startWithApplicationCode(
|
|
131
|
+
configuration: configuration,
|
|
132
|
+
applicationCode: applicationCode,
|
|
133
|
+
onSuccess: successCallback,
|
|
134
|
+
onError: onError
|
|
135
|
+
)
|
|
120
136
|
} else {
|
|
121
137
|
successCallback(nil)
|
|
122
138
|
}
|
|
123
139
|
}
|
|
124
140
|
|
|
125
141
|
private func performEarlyStartIfPossible() {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
142
|
+
let cachedConfigDict = RNMobileMessagingConfiguration.getRawConfigFromDefaults()
|
|
143
|
+
let configuration = cachedConfigDict.flatMap(RNMobileMessagingConfiguration.init)
|
|
144
|
+
let applicationCode = MobileMessaging.getKeychainApplicationCode()
|
|
145
|
+
|
|
146
|
+
guard let configuration = configuration,
|
|
147
|
+
let applicationCode = applicationCode,
|
|
148
|
+
!self.isStarted,
|
|
149
|
+
!isEarlyStartPerformed else
|
|
130
150
|
{
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
isEarlyStartPerformed = true
|
|
155
|
+
startWithApplicationCode(configuration: configuration, applicationCode: applicationCode) { response in }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private func startWithApplicationCode(
|
|
159
|
+
configuration: RNMobileMessagingConfiguration,
|
|
160
|
+
applicationCode: String,
|
|
161
|
+
onSuccess: @escaping RCTResponseSenderBlock,
|
|
162
|
+
onError: RCTResponseSenderBlock? = nil
|
|
163
|
+
) {
|
|
164
|
+
guard let mobileMessaging = createMobileMessagingInstance(
|
|
165
|
+
configuration: configuration,
|
|
166
|
+
applicationCode: applicationCode
|
|
167
|
+
) else {
|
|
168
|
+
MMLogError("[RNMobileMessaging] Failed to initialize MobileMessaging instance, SDK can't start.")
|
|
169
|
+
onError?([NSError(type: .InitializationFailed).reactNativeObject])
|
|
170
|
+
return
|
|
134
171
|
}
|
|
172
|
+
|
|
173
|
+
applyConfigurationAndStart(
|
|
174
|
+
mobileMessaging,
|
|
175
|
+
configuration: configuration,
|
|
176
|
+
onSuccess: onSuccess
|
|
177
|
+
)
|
|
135
178
|
}
|
|
136
|
-
|
|
137
|
-
private func
|
|
179
|
+
|
|
180
|
+
private func createMobileMessagingInstance(configuration: RNMobileMessagingConfiguration, applicationCode: String) -> MobileMessaging? {
|
|
181
|
+
let backendBaseURL = configuration.backendBaseURL ?? MMConsts.APIValues.prodDynamicBaseURLString
|
|
182
|
+
return MobileMessaging.withApplicationCode(
|
|
183
|
+
applicationCode,
|
|
184
|
+
notificationType: configuration.notificationType,
|
|
185
|
+
backendBaseURL: backendBaseURL
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private func applyConfigurationAndStart(_ mobileMessaging: MobileMessaging, configuration: RNMobileMessagingConfiguration, onSuccess: @escaping RCTResponseSenderBlock) {
|
|
138
190
|
self.eventsManager?.startObserving()
|
|
139
191
|
MobileMessaging.privacySettings.systemInfoSendingDisabled = configuration.privacySettings[RNMobileMessagingConfiguration.Keys.systemInfoSendingDisabled].unwrap(orDefault: false)
|
|
140
192
|
MobileMessaging.privacySettings.carrierInfoSendingDisabled = configuration.privacySettings[RNMobileMessagingConfiguration.Keys.carrierInfoSendingDisabled].unwrap(orDefault: false)
|
|
141
193
|
MobileMessaging.privacySettings.userDataPersistingDisabled = configuration.privacySettings[RNMobileMessagingConfiguration.Keys.userDataPersistingDisabled].unwrap(orDefault: false)
|
|
142
194
|
|
|
143
|
-
var mobileMessaging =
|
|
144
|
-
configuration.appCode,
|
|
145
|
-
notificationType: configuration.notificationType,
|
|
146
|
-
backendBaseURL: configuration.backendBaseURL ?? MMConsts.APIValues.prodDynamicBaseURLString)
|
|
195
|
+
var mobileMessaging = mobileMessaging
|
|
147
196
|
|
|
148
197
|
if let storageAdapter = messageStorageAdapter, configuration.messageStorageEnabled {
|
|
149
|
-
mobileMessaging = mobileMessaging
|
|
198
|
+
mobileMessaging = mobileMessaging.withMessageStorage(storageAdapter)
|
|
150
199
|
} else if configuration.defaultMessageStorage {
|
|
151
|
-
mobileMessaging = mobileMessaging
|
|
200
|
+
mobileMessaging = mobileMessaging.withDefaultMessageStorage()
|
|
152
201
|
}
|
|
153
202
|
if let categories = configuration.categories {
|
|
154
|
-
mobileMessaging = mobileMessaging
|
|
203
|
+
mobileMessaging = mobileMessaging.withInteractiveNotificationCategories(Set(categories))
|
|
155
204
|
}
|
|
156
205
|
MobileMessaging.userAgent.pluginVersion = "reactNative \(configuration.reactNativePluginVersion)"
|
|
157
206
|
if configuration.logging {
|
|
@@ -159,22 +208,22 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
|
|
|
159
208
|
}
|
|
160
209
|
|
|
161
210
|
if configuration.inAppChatEnabled {
|
|
162
|
-
mobileMessaging = mobileMessaging
|
|
211
|
+
mobileMessaging = mobileMessaging.withInAppChat()
|
|
163
212
|
}
|
|
164
213
|
|
|
165
214
|
if configuration.fullFeaturedInAppsEnabled {
|
|
166
|
-
mobileMessaging = mobileMessaging
|
|
215
|
+
mobileMessaging = mobileMessaging.withFullFeaturedInApps()
|
|
167
216
|
}
|
|
168
217
|
|
|
169
218
|
if let webViewSettings = configuration.webViewSettings {
|
|
170
|
-
mobileMessaging
|
|
219
|
+
mobileMessaging.webViewSettings.configureWith(rawConfig: webViewSettings)
|
|
171
220
|
}
|
|
172
221
|
|
|
173
222
|
if let jwt = configuration.userDataJwt, !jwt.isEmpty {
|
|
174
|
-
|
|
223
|
+
mobileMessaging = mobileMessaging.withJwtSupplier(VariableJwtSupplier(jwt: jwt))
|
|
175
224
|
}
|
|
176
225
|
|
|
177
|
-
mobileMessaging
|
|
226
|
+
mobileMessaging.start({
|
|
178
227
|
onSuccess(nil)
|
|
179
228
|
})
|
|
180
229
|
}
|
|
@@ -183,6 +232,13 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
|
|
|
183
232
|
self.eventsManager?.stop(stopObservations: stopObservations)
|
|
184
233
|
MobileMessaging.stop(false, completion: completion)
|
|
185
234
|
}
|
|
235
|
+
|
|
236
|
+
private func needsRestart(userConfigDict: [String: AnyObject], applicationCode: String) -> Bool {
|
|
237
|
+
let configurationChanged = RNMobileMessagingConfiguration.didConfigurationChange(userConfigDict: userConfigDict)
|
|
238
|
+
let applicationCodeChanged = MobileMessaging.didApplicationCodeChange(applicationCode: applicationCode)
|
|
239
|
+
|
|
240
|
+
return configurationChanged || applicationCodeChanged
|
|
241
|
+
}
|
|
186
242
|
|
|
187
243
|
/*User Profile Management*/
|
|
188
244
|
|
|
@@ -328,7 +384,8 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
|
|
|
328
384
|
let ua = uaDict == nil ? nil : MMUserAttributes(dictRepresentation: uaDict!)
|
|
329
385
|
let keepAsLead = (context["keepAsLead"] as? Bool) ?? false
|
|
330
386
|
let forceDepersonalize = context["forceDepersonalize"] as? Bool ?? false
|
|
331
|
-
|
|
387
|
+
let setDeviceAsPrimary = (context["setDeviceAsPrimary"] as? Bool) ?? false
|
|
388
|
+
MobileMessaging.personalize(forceDepersonalize: forceDepersonalize, keepAsLead: keepAsLead, setDeviceAsPrimary: setDeviceAsPrimary, userIdentity: ui, userAttributes: ua) { (error) in
|
|
332
389
|
if let error = error {
|
|
333
390
|
onError([error.reactNativeObject])
|
|
334
391
|
} else {
|
|
@@ -340,12 +397,15 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
|
|
|
340
397
|
@objc(depersonalize:onError:)
|
|
341
398
|
func depersonalize(onSuccess: @escaping RCTResponseSenderBlock, onError: @escaping RCTResponseSenderBlock) {
|
|
342
399
|
MobileMessaging.depersonalize(completion: { (status, error) in
|
|
343
|
-
if
|
|
344
|
-
onSuccess(["pending"])
|
|
345
|
-
} else if let error = error {
|
|
400
|
+
if let error = error {
|
|
346
401
|
onError([error.reactNativeObject])
|
|
347
402
|
} else {
|
|
348
|
-
|
|
403
|
+
MobileMessaging.jwtSupplier = nil
|
|
404
|
+
if (status == MMSuccessPending.pending) {
|
|
405
|
+
onSuccess(["pending"])
|
|
406
|
+
} else {
|
|
407
|
+
onSuccess(["success"])
|
|
408
|
+
}
|
|
349
409
|
}
|
|
350
410
|
})
|
|
351
411
|
}
|
|
@@ -472,9 +532,22 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
|
|
|
472
532
|
onError([NSError(type: .NotSupported).reactNativeObject])
|
|
473
533
|
}
|
|
474
534
|
|
|
535
|
+
@objc(cleanup:onError:)
|
|
536
|
+
func cleanup(onSuccess: @escaping RCTResponseSenderBlock, onError: @escaping RCTResponseSenderBlock) {
|
|
537
|
+
MobileMessaging.jwtSupplier = nil
|
|
538
|
+
RNMobileMessagingConfiguration.saveConfigToDefaults(rawConfig: [:])
|
|
539
|
+
MobileMessaging.cleanUpAndStop(false, completion: {
|
|
540
|
+
onSuccess([])
|
|
541
|
+
})
|
|
542
|
+
}
|
|
543
|
+
|
|
475
544
|
@objc(setUserDataJwt:onSuccess:onError:)
|
|
476
545
|
func setUserDataJwt(jwt: String?, onSuccess: @escaping RCTResponseSenderBlock, onError: @escaping RCTResponseSenderBlock) {
|
|
477
|
-
|
|
546
|
+
if let jwt = jwt, !jwt.isEmpty {
|
|
547
|
+
MobileMessaging.jwtSupplier = VariableJwtSupplier(jwt: jwt)
|
|
548
|
+
} else {
|
|
549
|
+
MobileMessaging.jwtSupplier = nil
|
|
550
|
+
}
|
|
478
551
|
onSuccess(nil)
|
|
479
552
|
}
|
|
480
553
|
|
|
@@ -54,6 +54,9 @@ RCT_EXTERN_METHOD(fetchInboxMessages:(NSString *)token externalUserId:(NSString
|
|
|
54
54
|
RCT_EXTERN_METHOD(fetchInboxMessagesWithoutToken:(NSString *)externalUserId inboxFilterOptions:(NSDictionary *)inboxFilterOptions onSuccess:(RCTResponseSenderBlock)successCallback onError:(RCTResponseSenderBlock)errorCallback)
|
|
55
55
|
RCT_EXTERN_METHOD(setInboxMessagesSeen:(NSString *)externalUserId messages:(NSArray *)messages onSuccess:(RCTResponseSenderBlock)successCallback onError:(RCTResponseSenderBlock)errorCallback)
|
|
56
56
|
|
|
57
|
+
/*Cleanup*/
|
|
58
|
+
RCT_EXTERN_METHOD(cleanup:(RCTResponseSenderBlock)onSuccess onError:(RCTResponseSenderBlock)onError)
|
|
59
|
+
|
|
57
60
|
/*JWT*/
|
|
58
61
|
RCT_EXTERN_METHOD(setUserDataJwt:(NSString *)jwt onSuccess:(RCTResponseSenderBlock)successCallback onError:(RCTResponseSenderBlock)errorCallback)
|
|
59
62
|
@end
|
|
@@ -11,6 +11,7 @@ import MobileMessaging
|
|
|
11
11
|
|
|
12
12
|
class RNMobileMessagingConfiguration {
|
|
13
13
|
static let userDefaultsConfigKey = "com.mobile-messaging.reactNativePluginConfiguration"
|
|
14
|
+
static let ignoreKeysWhenComparing: [String] = [Keys.reactNativePluginVersion]
|
|
14
15
|
|
|
15
16
|
struct Keys {
|
|
16
17
|
static let privacySettings = "privacySettings"
|
|
@@ -34,7 +35,6 @@ class RNMobileMessagingConfiguration {
|
|
|
34
35
|
static let backendBaseURL = "backendBaseURL"
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
let appCode: String
|
|
38
38
|
let webRTCUI: [String: AnyObject]?
|
|
39
39
|
let messageStorageEnabled: Bool
|
|
40
40
|
let defaultMessageStorage: Bool
|
|
@@ -50,13 +50,11 @@ class RNMobileMessagingConfiguration {
|
|
|
50
50
|
let backendBaseURL: String?
|
|
51
51
|
|
|
52
52
|
init?(rawConfig: [String: AnyObject]) {
|
|
53
|
-
guard let
|
|
54
|
-
let ios = rawConfig["ios"] as? [String: AnyObject] else
|
|
53
|
+
guard let ios = rawConfig["ios"] as? [String: AnyObject] else
|
|
55
54
|
{
|
|
56
55
|
return nil
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
self.appCode = appCode
|
|
60
58
|
self.webRTCUI = rawConfig[RNMobileMessagingConfiguration.Keys.webRTCUI] as? [String: AnyObject]
|
|
61
59
|
self.logging = rawConfig[RNMobileMessagingConfiguration.Keys.logging].unwrap(orDefault: false)
|
|
62
60
|
self.defaultMessageStorage = rawConfig[RNMobileMessagingConfiguration.Keys.defaultMessageStorage].unwrap(orDefault: false)
|
|
@@ -70,7 +68,6 @@ class RNMobileMessagingConfiguration {
|
|
|
70
68
|
ps[RNMobileMessagingConfiguration.Keys.userDataPersistingDisabled] = rawPrivacySettings[RNMobileMessagingConfiguration.Keys.userDataPersistingDisabled].unwrap(orDefault: false)
|
|
71
69
|
ps[RNMobileMessagingConfiguration.Keys.carrierInfoSendingDisabled] = rawPrivacySettings[RNMobileMessagingConfiguration.Keys.carrierInfoSendingDisabled].unwrap(orDefault: false)
|
|
72
70
|
ps[RNMobileMessagingConfiguration.Keys.systemInfoSendingDisabled] = rawPrivacySettings[RNMobileMessagingConfiguration.Keys.systemInfoSendingDisabled].unwrap(orDefault: false)
|
|
73
|
-
ps[RNMobileMessagingConfiguration.Keys.applicationCodePersistingDisabled] = rawPrivacySettings[RNMobileMessagingConfiguration.Keys.applicationCodePersistingDisabled].unwrap(orDefault: false)
|
|
74
71
|
|
|
75
72
|
privacySettings = ps
|
|
76
73
|
} else {
|
|
@@ -108,17 +105,7 @@ class RNMobileMessagingConfiguration {
|
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
static func saveConfigToDefaults(rawConfig: [String: AnyObject]) {
|
|
111
|
-
|
|
112
|
-
if value is NSString || value is NSNumber || value is NSArray || value is NSDictionary || value is NSDate || value is NSData {
|
|
113
|
-
return value
|
|
114
|
-
}
|
|
115
|
-
return nil
|
|
116
|
-
}
|
|
117
|
-
if serializableConfig[RNMobileMessagingConfiguration.Keys.messageStorage] != nil {
|
|
118
|
-
// Temporal fix so the archiving doesn't fail (messageStorage doesn't contain codable objects).
|
|
119
|
-
// Below we overwrite it with a dummy value so messageStorageEnabled keeps working.
|
|
120
|
-
serializableConfig[RNMobileMessagingConfiguration.Keys.messageStorage] = NSString(string: "message_storage")
|
|
121
|
-
}
|
|
108
|
+
let serializableConfig = serializedConfig(from: rawConfig)
|
|
122
109
|
|
|
123
110
|
do {
|
|
124
111
|
let data: Data = try NSKeyedArchiver.archivedData(withRootObject: serializableConfig, requiringSecureCoding: false)
|
|
@@ -135,10 +122,71 @@ class RNMobileMessagingConfiguration {
|
|
|
135
122
|
}
|
|
136
123
|
|
|
137
124
|
do {
|
|
138
|
-
|
|
125
|
+
guard let rawConfig = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [String: AnyObject] else {
|
|
126
|
+
return nil
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let normalizedConfig = serializedConfig(from: rawConfig)
|
|
130
|
+
if (normalizedConfig as NSDictionary) != (rawConfig as NSDictionary) {
|
|
131
|
+
saveConfigToDefaults(rawConfig: normalizedConfig)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return normalizedConfig
|
|
139
135
|
} catch {
|
|
140
136
|
MMLogError("[MobileMessaging] Failed to unarchive config from UserDefaults: \(error)")
|
|
141
137
|
return nil
|
|
142
138
|
}
|
|
143
139
|
}
|
|
140
|
+
|
|
141
|
+
static func didConfigurationChange(userConfigDict: [String: AnyObject]) -> Bool {
|
|
142
|
+
var serializedUserConfig = serializedConfig(from: userConfigDict)
|
|
143
|
+
guard let cachedConfigDict = getRawConfigFromDefaults() else {
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
var serializedCachedConfig = serializedConfig(from: cachedConfigDict)
|
|
147
|
+
|
|
148
|
+
removeIgnoredKeys(from: &serializedUserConfig)
|
|
149
|
+
removeIgnoredKeys(from: &serializedCachedConfig)
|
|
150
|
+
|
|
151
|
+
return (serializedUserConfig as NSDictionary) != (serializedCachedConfig as NSDictionary)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private static func serializedConfig(from rawConfig: [String: AnyObject]) -> [String: AnyObject] {
|
|
155
|
+
var rawConfig = rawConfig
|
|
156
|
+
rawConfig.removeValue(forKey: RNMobileMessagingConfiguration.Keys.applicationCode)
|
|
157
|
+
rawConfig.removeValue(forKey: RNMobileMessagingConfiguration.Keys.userDataJwt)
|
|
158
|
+
sanitizePrivacySettings(in: &rawConfig)
|
|
159
|
+
|
|
160
|
+
var serializableConfig = rawConfig.compactMapValues { value -> AnyObject? in
|
|
161
|
+
if value is NSString || value is NSNumber || value is NSArray || value is NSDictionary || value is NSDate || value is NSData {
|
|
162
|
+
return value
|
|
163
|
+
}
|
|
164
|
+
return nil
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if rawConfig[RNMobileMessagingConfiguration.Keys.messageStorage] != nil {
|
|
168
|
+
// Preserve the fact that custom message storage was configured without persisting the JS callbacks.
|
|
169
|
+
serializableConfig[RNMobileMessagingConfiguration.Keys.messageStorage] = NSString(string: "message_storage")
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return serializableConfig
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private static func removeIgnoredKeys(from config: inout [String: AnyObject]) {
|
|
176
|
+
ignoreKeysWhenComparing.forEach { config.removeValue(forKey: $0) }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private static func sanitizePrivacySettings(in rawConfig: inout [String: AnyObject]) {
|
|
180
|
+
guard var rawPrivacySettings = rawConfig[RNMobileMessagingConfiguration.Keys.privacySettings] as? [String: Any] else {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
rawPrivacySettings.removeValue(forKey: RNMobileMessagingConfiguration.Keys.applicationCodePersistingDisabled)
|
|
185
|
+
|
|
186
|
+
if rawPrivacySettings.isEmpty {
|
|
187
|
+
rawConfig.removeValue(forKey: RNMobileMessagingConfiguration.Keys.privacySettings)
|
|
188
|
+
} else {
|
|
189
|
+
rawConfig[RNMobileMessagingConfiguration.Keys.privacySettings] = rawPrivacySettings as NSDictionary
|
|
190
|
+
}
|
|
191
|
+
}
|
|
144
192
|
}
|
|
@@ -16,6 +16,7 @@ public enum RNMobileMessagingErrorType: Error {
|
|
|
16
16
|
case DefaultStorageNotInitialized
|
|
17
17
|
case NotSupported
|
|
18
18
|
case NoData
|
|
19
|
+
case InitializationFailed
|
|
19
20
|
|
|
20
21
|
fileprivate var errorCode: Int {
|
|
21
22
|
switch self {
|
|
@@ -29,6 +30,8 @@ public enum RNMobileMessagingErrorType: Error {
|
|
|
29
30
|
return 3
|
|
30
31
|
case .NoData:
|
|
31
32
|
return 4
|
|
33
|
+
case .InitializationFailed:
|
|
34
|
+
return 5
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
}
|
|
@@ -47,6 +50,8 @@ public enum RNMobileMessagingErrorType: Error {
|
|
|
47
50
|
errorDescription = NSLocalizedString("Functionality is not supported.", comment: "")
|
|
48
51
|
case .NoData:
|
|
49
52
|
errorDescription = NSLocalizedString("No data retrieved.", comment: "")
|
|
53
|
+
case .InitializationFailed:
|
|
54
|
+
errorDescription = NSLocalizedString("Could not initialize MobileMessaging SDK.", comment: "")
|
|
50
55
|
}
|
|
51
56
|
return errorDescription
|
|
52
57
|
}
|
|
@@ -39,6 +39,12 @@ extension MMInbox {
|
|
|
39
39
|
var result = [String: Any]()
|
|
40
40
|
result["countTotal"] = countTotal
|
|
41
41
|
result["countUnread"] = countUnread
|
|
42
|
+
if let countTotalFiltered = countTotalFiltered {
|
|
43
|
+
result["countTotalFiltered"] = countTotalFiltered
|
|
44
|
+
}
|
|
45
|
+
if let countUnreadFiltered = countUnreadFiltered {
|
|
46
|
+
result["countUnreadFiltered"] = countUnreadFiltered
|
|
47
|
+
}
|
|
42
48
|
result["messages"] = messages.map({ return $0.dictionaryRepresentation })
|
|
43
49
|
return result
|
|
44
50
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infobip-mobile-messaging-react-native-plugin",
|
|
3
3
|
"title": "Infobip Mobile Messaging React Native Plugin",
|
|
4
|
-
"version": "14.
|
|
4
|
+
"version": "14.9.1",
|
|
5
5
|
"description": "Infobip Mobile Messaging React Native Plugin",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"scripts": {
|
package/src/index.d.ts
CHANGED
|
@@ -64,7 +64,6 @@ declare namespace MobileMessagingReactNative {
|
|
|
64
64
|
notificationSound?: string;
|
|
65
65
|
};
|
|
66
66
|
privacySettings?: {
|
|
67
|
-
applicationCodePersistingDisabled?: boolean;
|
|
68
67
|
userDataPersistingDisabled?: boolean;
|
|
69
68
|
carrierInfoSendingDisabled?: boolean;
|
|
70
69
|
systemInfoSendingDisabled?: boolean
|
|
@@ -130,13 +129,16 @@ declare namespace MobileMessagingReactNative {
|
|
|
130
129
|
export interface MMInbox {
|
|
131
130
|
countTotal: number;
|
|
132
131
|
countUnread: number;
|
|
133
|
-
|
|
132
|
+
countTotalFiltered?: number;
|
|
133
|
+
countUnreadFiltered?: number;
|
|
134
|
+
messages?: Message[];
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
export interface MMInboxFilterOptions {
|
|
137
138
|
fromDateTime?: string;
|
|
138
139
|
toDateTime?: string;
|
|
139
140
|
topic?: string;
|
|
141
|
+
topics?: string[];
|
|
140
142
|
limit?: number;
|
|
141
143
|
}
|
|
142
144
|
|
|
@@ -145,10 +147,12 @@ declare namespace MobileMessagingReactNative {
|
|
|
145
147
|
userAttributes?: Record<string, string | number | boolean | object[]>;
|
|
146
148
|
forceDepersonalize?: boolean;
|
|
147
149
|
keepAsLead?: boolean;
|
|
150
|
+
setDeviceAsPrimary?: boolean;
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
export interface Message {
|
|
151
154
|
messageId: string;
|
|
155
|
+
topic?: string;
|
|
152
156
|
title?: string;
|
|
153
157
|
body?: string;
|
|
154
158
|
sound?: string;
|
|
@@ -720,6 +724,16 @@ declare namespace MobileMessagingReactNative {
|
|
|
720
724
|
*/
|
|
721
725
|
setChatDomain(domain?: string): void;
|
|
722
726
|
|
|
727
|
+
/**
|
|
728
|
+
* Cleans up the SDK, removing all data and stopping all services.
|
|
729
|
+
* After cleanup, you should call init() again with a new configuration to restart the SDK.
|
|
730
|
+
* JWT supplier is also cleared during cleanup.
|
|
731
|
+
*
|
|
732
|
+
* @param onSuccess will be called on success
|
|
733
|
+
* @param onError will be called on error
|
|
734
|
+
*/
|
|
735
|
+
cleanup(onSuccess: () => void, onError: (error: MobileMessagingError) => void): void;
|
|
736
|
+
|
|
723
737
|
/**
|
|
724
738
|
* Set JSON Web Token for user data operations and personalization.
|
|
725
739
|
* @param jwt JWT token in predefined format
|
package/src/index.js
CHANGED
|
@@ -166,7 +166,6 @@ class MobileMessaging {
|
|
|
166
166
|
* notificationSound: <String>
|
|
167
167
|
* }
|
|
168
168
|
* privacySettings: {
|
|
169
|
-
* applicationCodePersistingDisabled: <Boolean>,
|
|
170
169
|
* userDataPersistingDisabled: <Boolean>,
|
|
171
170
|
* carrierInfoSendingDisabled: <Boolean>,
|
|
172
171
|
* systemInfoSendingDisabled: <Boolean>
|
|
@@ -285,7 +284,14 @@ class MobileMessaging {
|
|
|
285
284
|
* @name fetchInboxMessages
|
|
286
285
|
* @param token access token (JWT in a strictly predefined format) required for current user to have access to the Inbox messages
|
|
287
286
|
* @param externalUserId External User ID is meant to be an ID of a user in an external (non-Infobip) service
|
|
288
|
-
* @param filterOptions filtering options applied to messages list in response.
|
|
287
|
+
* @param filterOptions filtering options applied to messages list in response.
|
|
288
|
+
* {
|
|
289
|
+
* fromDateTime: <String; filter messages received after this datetime in ISO8601 format with timezone, e.g. "2024-03-11T12:00:00+01:00">,
|
|
290
|
+
* toDateTime: <String; filter messages received before this datetime in ISO8601 format with timezone, e.g. "2024-03-20T12:00:00+01:00">,
|
|
291
|
+
* topic: <String; filter messages by a single topic. Mutually exclusive with 'topics'>,
|
|
292
|
+
* topics: <Array<String>; filter messages by multiple topics, e.g. ["topic1", "topic2"]. Mutually exclusive with 'topic'>,
|
|
293
|
+
* limit: <Number; maximum number of messages to return. Default is 20 for standard/single-topic fetches. When using 'topics', no default limit is applied unless explicitly provided, and the limit applies to the total returned messages, not per topic>,
|
|
294
|
+
* }
|
|
289
295
|
* @param onSuccess will be called on success
|
|
290
296
|
* @param {Function} onError will be called on error
|
|
291
297
|
*/
|
|
@@ -298,7 +304,14 @@ class MobileMessaging {
|
|
|
298
304
|
*
|
|
299
305
|
* @name fetchInboxMessagesWithoutToken
|
|
300
306
|
* @param externalUserId External User ID is meant to be an ID of a user in an external (non-Infobip) service
|
|
301
|
-
* @param filterOptions filtering options applied to messages list in response.
|
|
307
|
+
* @param filterOptions filtering options applied to messages list in response.
|
|
308
|
+
* {
|
|
309
|
+
* fromDateTime: <String; filter messages received after this datetime in ISO8601 format with timezone, e.g. "2024-03-11T12:00:00+01:00">,
|
|
310
|
+
* toDateTime: <String; filter messages received before this datetime in ISO8601 format with timezone, e.g. "2024-03-20T12:00:00+01:00">,
|
|
311
|
+
* topic: <String; filter messages by a single topic. Mutually exclusive with 'topics'>,
|
|
312
|
+
* topics: <Array<String>; filter messages by multiple topics, e.g. ["topic1", "topic2"]. Mutually exclusive with 'topic'>,
|
|
313
|
+
* limit: <Number; maximum number of messages to return. Default is 20 for standard/single-topic fetches. When using 'topics', no default limit is applied unless explicitly provided, and the limit applies to the total returned messages, not per topic>,
|
|
314
|
+
* }
|
|
302
315
|
* @param onSuccess will be called on success
|
|
303
316
|
* @param {Function} onError will be called on error
|
|
304
317
|
*/
|
|
@@ -873,6 +886,19 @@ class MobileMessaging {
|
|
|
873
886
|
RNMMChat.setChatDomain(domain);
|
|
874
887
|
}
|
|
875
888
|
|
|
889
|
+
/**
|
|
890
|
+
* Cleans up the SDK, removing all data and stopping all services.
|
|
891
|
+
* After cleanup, you should call init() again with a new configuration to restart the SDK.
|
|
892
|
+
* JWT supplier is also cleared during cleanup.
|
|
893
|
+
*
|
|
894
|
+
* @name cleanup
|
|
895
|
+
* @param {Function} onSuccess will be called on success
|
|
896
|
+
* @param {Function} onError will be called on error
|
|
897
|
+
*/
|
|
898
|
+
cleanup(onSuccess = function() {}, onError = function() {}) {
|
|
899
|
+
ReactNativeMobileMessaging.cleanup(onSuccess, onError);
|
|
900
|
+
}
|
|
901
|
+
|
|
876
902
|
/**
|
|
877
903
|
* Set JSON Web Token for user data operations and personalization.
|
|
878
904
|
* @param {String} jwt JWT token in predefined format
|
|
@@ -52,6 +52,9 @@ export interface Spec extends TurboModule {
|
|
|
52
52
|
submitEvent(eventData: Object, onError: (error: Object) => void): void;
|
|
53
53
|
submitEventImmediately(eventData: Object, onSuccess: () => void, onError: (error: Object) => void): void;
|
|
54
54
|
|
|
55
|
+
// Cleanup
|
|
56
|
+
cleanup(onSuccess: () => void, onError: (error: Object) => void): void;
|
|
57
|
+
|
|
55
58
|
// JWT
|
|
56
59
|
setUserDataJwt(jwt: string, onSuccess: () => void, onError: (error: Object) => void): void;
|
|
57
60
|
|
|
Binary file
|