react-native-insider 8.0.0-nh → 8.0.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/RNInsider.podspec +9 -2
- package/android/build.gradle +16 -1
- package/android/src/main/java/com/useinsider/react/RNInsiderModule.java +20 -0
- package/android/src/test/java/com/useinsider/react/RNInsiderModuleTest.java +169 -0
- package/index.d.ts +1 -0
- package/index.js +13 -0
- package/ios/RNInsider/RNInsider.m +4 -0
- package/ios/RNInsiderTests/RNInsiderTriggerPushProcessTests.m +91 -0
- package/package.json +1 -1
- package/src/InsiderAppCard.d.ts +2 -2
- package/src/InsiderAppCard.js +11 -11
- package/src/InsiderAppCards.d.ts +2 -2
package/RNInsider.podspec
CHANGED
|
@@ -9,12 +9,19 @@ Pod::Spec.new do |s|
|
|
|
9
9
|
s.authors = package_json['author']
|
|
10
10
|
s.license = 'MIT'
|
|
11
11
|
s.platform = :ios, '12.0'
|
|
12
|
-
s.source = {:http => 'https://mobilesdk.useinsider.com/iOS/15.0.
|
|
12
|
+
s.source = {:http => 'https://mobilesdk.useinsider.com/iOS/15.0.1/InsiderMobileIOSFramework.zip'}
|
|
13
13
|
s.source_files = 'ios/RNInsider/*.{h,m}'
|
|
14
14
|
s.requires_arc = true
|
|
15
15
|
s.static_framework = true
|
|
16
16
|
s.dependency 'React'
|
|
17
|
-
s.dependency 'InsiderMobile', '15.0.
|
|
17
|
+
s.dependency 'InsiderMobile', '15.0.1'
|
|
18
18
|
s.dependency 'InsiderGeofence', '1.2.4'
|
|
19
19
|
s.dependency 'InsiderHybrid', '1.7.6'
|
|
20
|
+
|
|
21
|
+
s.test_spec 'Tests' do |test_spec|
|
|
22
|
+
test_spec.source_files = 'ios/RNInsiderTests/**/*.{h,m}'
|
|
23
|
+
test_spec.frameworks = 'XCTest'
|
|
24
|
+
test_spec.requires_app_host = false
|
|
25
|
+
test_spec.dependency 'OCMock', '~> 3.9'
|
|
26
|
+
end
|
|
20
27
|
end
|
package/android/build.gradle
CHANGED
|
@@ -2,6 +2,7 @@ buildscript {
|
|
|
2
2
|
repositories {
|
|
3
3
|
google()
|
|
4
4
|
mavenCentral()
|
|
5
|
+
maven { url "https://developer.huawei.com/repo/"}
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
dependencies {
|
|
@@ -29,18 +30,24 @@ android {
|
|
|
29
30
|
if (major >= 8) {
|
|
30
31
|
namespace "com.useinsider.react"
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
testOptions {
|
|
35
|
+
unitTests.includeAndroidResources = true
|
|
36
|
+
unitTests.returnDefaultValues = true
|
|
37
|
+
}
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
repositories {
|
|
35
41
|
google()
|
|
36
42
|
mavenCentral()
|
|
43
|
+
maven { url "https://developer.huawei.com/repo/"}
|
|
37
44
|
maven { url "https://mobilesdk.useinsider.com/android" }
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
|
|
41
48
|
dependencies {
|
|
42
49
|
implementation "com.facebook.react:react-native:${getVersionFromPartner('reactNativeVersion', '+')}"
|
|
43
|
-
implementation 'com.useinsider:insider:16.0.1
|
|
50
|
+
implementation 'com.useinsider:insider:16.0.1'
|
|
44
51
|
implementation 'com.useinsider:insiderhybrid:1.3.4'
|
|
45
52
|
|
|
46
53
|
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
|
|
@@ -51,4 +58,12 @@ dependencies {
|
|
|
51
58
|
implementation 'com.google.firebase:firebase-messaging:24.0.0'
|
|
52
59
|
implementation 'com.google.android.gms:play-services-location:21.3.0'
|
|
53
60
|
implementation 'com.google.android.play:review:2.0.1'
|
|
61
|
+
|
|
62
|
+
implementation 'com.huawei.hms:push:6.13.0.300'
|
|
63
|
+
implementation 'com.huawei.hms:ads-identifier:3.4.62.300'
|
|
64
|
+
implementation 'com.huawei.hms:location:6.16.0.302'
|
|
65
|
+
|
|
66
|
+
testImplementation 'junit:junit:4.13.2'
|
|
67
|
+
testImplementation 'org.mockito:mockito-core:5.12.0'
|
|
68
|
+
testImplementation 'org.mockito:mockito-inline:5.2.0'
|
|
54
69
|
}
|
|
@@ -895,6 +895,10 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
|
|
|
895
895
|
}
|
|
896
896
|
String provider = Insider.Instance.getCurrentProvider(reactContext);
|
|
897
897
|
switch (provider) {
|
|
898
|
+
case "huawei":
|
|
899
|
+
com.huawei.hms.push.RemoteMessage hmsRemoteMessage = new com.huawei.hms.push.RemoteMessage.Builder("insider").setData(remoteMessageStringMap).build();
|
|
900
|
+
Insider.Instance.handleHMSNotification(reactContext, hmsRemoteMessage);
|
|
901
|
+
break;
|
|
898
902
|
case "other":
|
|
899
903
|
case "google":
|
|
900
904
|
RemoteMessage fcmRemoteMessage = new RemoteMessage.Builder("insider").setData(remoteMessageStringMap).build();
|
|
@@ -909,6 +913,22 @@ public class RNInsiderModule extends ReactContextBaseJavaModule {
|
|
|
909
913
|
}
|
|
910
914
|
}
|
|
911
915
|
|
|
916
|
+
@ReactMethod
|
|
917
|
+
public void triggerPushProcessWithNotificationData(ReadableMap remoteMessageData) {
|
|
918
|
+
try {
|
|
919
|
+
if (remoteMessageData == null)
|
|
920
|
+
return;
|
|
921
|
+
Map<String, Object> convertedMap = RNUtils.convertReadableMapToMap(remoteMessageData);
|
|
922
|
+
Map<String, String> remoteMessageStringMap = new HashMap<>();
|
|
923
|
+
for (String key : convertedMap.keySet()) {
|
|
924
|
+
remoteMessageStringMap.put(key, String.valueOf(convertedMap.get(key)));
|
|
925
|
+
}
|
|
926
|
+
Insider.Instance.triggerPushProcessWithNotificationData(reactContext, remoteMessageStringMap);
|
|
927
|
+
} catch (Exception e) {
|
|
928
|
+
Insider.Instance.putException(e);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
912
932
|
// Deprecated
|
|
913
933
|
@ReactMethod
|
|
914
934
|
public void enableIDFACollection(final boolean enableIDFACollection) {}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
package com.useinsider.react;
|
|
2
|
+
|
|
3
|
+
import static org.junit.Assert.assertEquals;
|
|
4
|
+
import static org.junit.Assert.assertTrue;
|
|
5
|
+
import static org.mockito.ArgumentMatchers.any;
|
|
6
|
+
import static org.mockito.ArgumentMatchers.eq;
|
|
7
|
+
import static org.mockito.Mockito.never;
|
|
8
|
+
import static org.mockito.Mockito.times;
|
|
9
|
+
import static org.mockito.Mockito.verify;
|
|
10
|
+
import static org.mockito.Mockito.when;
|
|
11
|
+
|
|
12
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
13
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
14
|
+
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
|
15
|
+
import com.facebook.react.bridge.ReadableType;
|
|
16
|
+
import com.useinsider.insider.Insider;
|
|
17
|
+
|
|
18
|
+
import org.junit.After;
|
|
19
|
+
import org.junit.Before;
|
|
20
|
+
import org.junit.Test;
|
|
21
|
+
import org.mockito.ArgumentCaptor;
|
|
22
|
+
|
|
23
|
+
import java.lang.reflect.Field;
|
|
24
|
+
import java.util.HashMap;
|
|
25
|
+
import java.util.Map;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Unit tests for {@link RNInsiderModule#triggerPushProcessWithNotificationData(ReadableMap)}.
|
|
29
|
+
*
|
|
30
|
+
* MOB-26455: New bridge method that converts the JS notification payload to a
|
|
31
|
+
* String map and forwards it to {@code Insider.Instance.triggerPushProcessWithNotificationData}.
|
|
32
|
+
* The bridge must:
|
|
33
|
+
* - Treat a null payload as a no-op (early return, no native call).
|
|
34
|
+
* - Convert ReadableMap entries to String values via {@link String#valueOf(Object)}.
|
|
35
|
+
* - Pass the supplied {@link ReactApplicationContext} to the native SDK.
|
|
36
|
+
* - Route any thrown exception through {@code Insider.Instance.putException}
|
|
37
|
+
* instead of propagating it to the JS caller.
|
|
38
|
+
*/
|
|
39
|
+
public class RNInsiderModuleTest {
|
|
40
|
+
|
|
41
|
+
private RNInsiderModule module;
|
|
42
|
+
private ReactApplicationContext reactContext;
|
|
43
|
+
private Insider insiderMock;
|
|
44
|
+
private Insider originalInstance;
|
|
45
|
+
|
|
46
|
+
@Before
|
|
47
|
+
public void setUp() throws Exception {
|
|
48
|
+
reactContext = org.mockito.Mockito.mock(ReactApplicationContext.class);
|
|
49
|
+
insiderMock = org.mockito.Mockito.mock(Insider.class);
|
|
50
|
+
|
|
51
|
+
Field instanceField = Insider.class.getDeclaredField("Instance");
|
|
52
|
+
instanceField.setAccessible(true);
|
|
53
|
+
originalInstance = (Insider) instanceField.get(null);
|
|
54
|
+
instanceField.set(null, insiderMock);
|
|
55
|
+
|
|
56
|
+
module = new RNInsiderModule(reactContext);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@After
|
|
60
|
+
public void tearDown() throws Exception {
|
|
61
|
+
Field instanceField = Insider.class.getDeclaredField("Instance");
|
|
62
|
+
instanceField.setAccessible(true);
|
|
63
|
+
instanceField.set(null, originalInstance);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Test
|
|
67
|
+
public void triggerPushProcessWithNotificationData_nullPayload_doesNotInvokeNativeSdk() {
|
|
68
|
+
module.triggerPushProcessWithNotificationData(null);
|
|
69
|
+
|
|
70
|
+
verify(insiderMock, never())
|
|
71
|
+
.triggerPushProcessWithNotificationData(any(), any());
|
|
72
|
+
verify(insiderMock, never()).putException(any(Exception.class));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Test
|
|
76
|
+
public void triggerPushProcessWithNotificationData_emptyMap_forwardsEmptyStringMap() {
|
|
77
|
+
ReadableMap empty = readableMapOf(new HashMap<String, Object>());
|
|
78
|
+
|
|
79
|
+
module.triggerPushProcessWithNotificationData(empty);
|
|
80
|
+
|
|
81
|
+
@SuppressWarnings("unchecked")
|
|
82
|
+
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
|
|
83
|
+
verify(insiderMock, times(1))
|
|
84
|
+
.triggerPushProcessWithNotificationData(eq(reactContext), captor.capture());
|
|
85
|
+
assertTrue("Empty payload should produce empty String map", captor.getValue().isEmpty());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@Test
|
|
89
|
+
public void triggerPushProcessWithNotificationData_stringValues_passedThroughVerbatim() {
|
|
90
|
+
Map<String, Object> entries = new HashMap<>();
|
|
91
|
+
entries.put("title", "Welcome");
|
|
92
|
+
entries.put("body", "Hello there");
|
|
93
|
+
ReadableMap payload = readableMapOf(entries);
|
|
94
|
+
|
|
95
|
+
module.triggerPushProcessWithNotificationData(payload);
|
|
96
|
+
|
|
97
|
+
@SuppressWarnings("unchecked")
|
|
98
|
+
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
|
|
99
|
+
verify(insiderMock, times(1))
|
|
100
|
+
.triggerPushProcessWithNotificationData(eq(reactContext), captor.capture());
|
|
101
|
+
Map<String, String> forwarded = captor.getValue();
|
|
102
|
+
assertEquals("Welcome", forwarded.get("title"));
|
|
103
|
+
assertEquals("Hello there", forwarded.get("body"));
|
|
104
|
+
assertEquals(2, forwarded.size());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@Test
|
|
108
|
+
public void triggerPushProcessWithNotificationData_numericValues_stringifiedViaStringValueOf() {
|
|
109
|
+
Map<String, Object> entries = new HashMap<>();
|
|
110
|
+
entries.put("priority", 5.0);
|
|
111
|
+
entries.put("retry", true);
|
|
112
|
+
ReadableMap payload = readableMapOf(entries);
|
|
113
|
+
|
|
114
|
+
module.triggerPushProcessWithNotificationData(payload);
|
|
115
|
+
|
|
116
|
+
@SuppressWarnings("unchecked")
|
|
117
|
+
ArgumentCaptor<Map<String, String>> captor = ArgumentCaptor.forClass(Map.class);
|
|
118
|
+
verify(insiderMock, times(1))
|
|
119
|
+
.triggerPushProcessWithNotificationData(eq(reactContext), captor.capture());
|
|
120
|
+
Map<String, String> forwarded = captor.getValue();
|
|
121
|
+
assertEquals(String.valueOf(5.0), forwarded.get("priority"));
|
|
122
|
+
assertEquals("true", forwarded.get("retry"));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@Test
|
|
126
|
+
public void triggerPushProcessWithNotificationData_nativeSdkThrows_swallowedViaPutException() {
|
|
127
|
+
ReadableMap payload = readableMapOf(new HashMap<String, Object>());
|
|
128
|
+
RuntimeException boom = new RuntimeException("native failure");
|
|
129
|
+
org.mockito.Mockito.doThrow(boom)
|
|
130
|
+
.when(insiderMock)
|
|
131
|
+
.triggerPushProcessWithNotificationData(any(), any());
|
|
132
|
+
|
|
133
|
+
module.triggerPushProcessWithNotificationData(payload);
|
|
134
|
+
|
|
135
|
+
verify(insiderMock, times(1)).putException(boom);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Builds a {@link ReadableMap} stub backed by a plain Java map. The bridge
|
|
140
|
+
* only walks the iterator and asks for {@code getType}/{@code getString}/
|
|
141
|
+
* {@code getDouble}/{@code getBoolean}, so we mirror that minimal contract
|
|
142
|
+
* instead of pulling in {@code JavaOnlyMap} (which requires the React Native
|
|
143
|
+
* AAR at test time).
|
|
144
|
+
*/
|
|
145
|
+
private static ReadableMap readableMapOf(Map<String, Object> entries) {
|
|
146
|
+
ReadableMap map = org.mockito.Mockito.mock(ReadableMap.class);
|
|
147
|
+
ReadableMapKeySetIterator iterator = org.mockito.Mockito.mock(ReadableMapKeySetIterator.class);
|
|
148
|
+
Iterable<String> keys = new java.util.ArrayList<>(entries.keySet());
|
|
149
|
+
java.util.Iterator<String> keyIter = keys.iterator();
|
|
150
|
+
when(iterator.hasNextKey()).thenAnswer(invocation -> keyIter.hasNext());
|
|
151
|
+
when(iterator.nextKey()).thenAnswer(invocation -> keyIter.next());
|
|
152
|
+
when(map.keySetIterator()).thenReturn(iterator);
|
|
153
|
+
for (Map.Entry<String, Object> entry : entries.entrySet()) {
|
|
154
|
+
String key = entry.getKey();
|
|
155
|
+
Object value = entry.getValue();
|
|
156
|
+
if (value instanceof String) {
|
|
157
|
+
when(map.getType(key)).thenReturn(ReadableType.String);
|
|
158
|
+
when(map.getString(key)).thenReturn((String) value);
|
|
159
|
+
} else if (value instanceof Boolean) {
|
|
160
|
+
when(map.getType(key)).thenReturn(ReadableType.Boolean);
|
|
161
|
+
when(map.getBoolean(key)).thenReturn((Boolean) value);
|
|
162
|
+
} else if (value instanceof Number) {
|
|
163
|
+
when(map.getType(key)).thenReturn(ReadableType.Number);
|
|
164
|
+
when(map.getDouble(key)).thenReturn(((Number) value).doubleValue());
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return map;
|
|
168
|
+
}
|
|
169
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -202,6 +202,7 @@ declare module 'react-native-insider' {
|
|
|
202
202
|
static startTrackingGeofence(): void;
|
|
203
203
|
static setAllowsBackgroundLocationUpdates(allowsBackgroundLocationUpdates: boolean): void;
|
|
204
204
|
static handleNotification(notification: any): void;
|
|
205
|
+
static triggerPushProcessWithNotificationData(notification: any): void;
|
|
205
206
|
static enableIDFACollection(enableIDFACollection: boolean): void;
|
|
206
207
|
static removeInapp(): void;
|
|
207
208
|
static registerWithQuietPermission(enabled: boolean): void;
|
package/index.js
CHANGED
|
@@ -744,6 +744,19 @@ export default class RNInsider {
|
|
|
744
744
|
}
|
|
745
745
|
}
|
|
746
746
|
|
|
747
|
+
static triggerPushProcessWithNotificationData(notification) {
|
|
748
|
+
if (shouldNotProceed()) return;
|
|
749
|
+
if (checkParameters([{ type: 'object', value: notification }])) {
|
|
750
|
+
showParameterWarningLog("triggerPushProcessWithNotificationData", [{ type: 'object', value: notification }]);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
try {
|
|
754
|
+
Insider.triggerPushProcessWithNotificationData(notification);
|
|
755
|
+
} catch (error) {
|
|
756
|
+
Insider.putErrorLog(generateJSONErrorString(error));
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
747
760
|
static setHybridPushToken(token) {
|
|
748
761
|
if (shouldNotProceed()) return;
|
|
749
762
|
if (checkParameters([{ type: 'string', value: token }])) {
|
|
@@ -744,6 +744,10 @@ RCT_EXPORT_METHOD(handleNotification:(NSDictionary *)notification) {
|
|
|
744
744
|
}
|
|
745
745
|
}
|
|
746
746
|
|
|
747
|
+
RCT_EXPORT_METHOD(triggerPushProcessWithNotificationData:(NSDictionary *)notification) {
|
|
748
|
+
[self handleNotification:notification];
|
|
749
|
+
}
|
|
750
|
+
|
|
747
751
|
RCT_EXPORT_METHOD(enableIDFACollection:(BOOL)enableIDFACollection) {
|
|
748
752
|
@try {
|
|
749
753
|
[Insider enableIDFACollection:enableIDFACollection];
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Unit tests for `triggerPushProcessWithNotificationData:` on the iOS bridge.
|
|
2
|
+
//
|
|
3
|
+
// MOB-26455: The new method delegates straight to `handleNotification:`, which
|
|
4
|
+
// is responsible for stamping `aps = "insider"` and forwarding the payload to
|
|
5
|
+
// the native InsiderMobile entry points. These tests pin that contract so
|
|
6
|
+
// future refactors cannot silently drop the delegation or skip the stamp.
|
|
7
|
+
|
|
8
|
+
#import <XCTest/XCTest.h>
|
|
9
|
+
#import <OCMock/OCMock.h>
|
|
10
|
+
|
|
11
|
+
#import "RNInsider.h"
|
|
12
|
+
|
|
13
|
+
#if __has_include(<InsiderMobile/Insider.h>)
|
|
14
|
+
#import <InsiderMobile/Insider.h>
|
|
15
|
+
#elif __has_include("Insider.h")
|
|
16
|
+
#import "Insider.h"
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
@interface RNInsiderTriggerPushProcessTests : XCTestCase
|
|
20
|
+
@property (nonatomic, strong) RNInsider *bridge;
|
|
21
|
+
@property (nonatomic, strong) id insiderClassMock;
|
|
22
|
+
@end
|
|
23
|
+
|
|
24
|
+
@implementation RNInsiderTriggerPushProcessTests
|
|
25
|
+
|
|
26
|
+
- (void)setUp {
|
|
27
|
+
[super setUp];
|
|
28
|
+
self.bridge = [RNInsider new];
|
|
29
|
+
self.insiderClassMock = OCMClassMock([Insider class]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (void)tearDown {
|
|
33
|
+
[self.insiderClassMock stopMocking];
|
|
34
|
+
self.insiderClassMock = nil;
|
|
35
|
+
self.bridge = nil;
|
|
36
|
+
[super tearDown];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
- (void)testTriggerPushProcess_delegatesToHandleNotification {
|
|
40
|
+
id partialBridge = OCMPartialMock(self.bridge);
|
|
41
|
+
NSDictionary *payload = @{ @"title": @"Hi", @"body": @"There" };
|
|
42
|
+
|
|
43
|
+
OCMExpect([partialBridge handleNotification:payload]);
|
|
44
|
+
|
|
45
|
+
[self.bridge triggerPushProcessWithNotificationData:payload];
|
|
46
|
+
|
|
47
|
+
OCMVerifyAll(partialBridge);
|
|
48
|
+
[partialBridge stopMocking];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (void)testTriggerPushProcess_stampsApsKeyAndForwardsToInsider {
|
|
52
|
+
NSDictionary *payload = @{ @"title": @"Welcome" };
|
|
53
|
+
|
|
54
|
+
[self.bridge triggerPushProcessWithNotificationData:payload];
|
|
55
|
+
|
|
56
|
+
OCMVerify([self.insiderClassMock handlePushLogWithUserInfo:[OCMArg checkWithBlock:^BOOL(NSDictionary *arg) {
|
|
57
|
+
return [arg[@"aps"] isEqualToString:@"insider"] && [arg[@"title"] isEqualToString:@"Welcome"];
|
|
58
|
+
}]]);
|
|
59
|
+
OCMVerify([self.insiderClassMock trackInteractiveLogWithUserInfo:[OCMArg checkWithBlock:^BOOL(NSDictionary *arg) {
|
|
60
|
+
return [arg[@"aps"] isEqualToString:@"insider"] && [arg[@"title"] isEqualToString:@"Welcome"];
|
|
61
|
+
}]]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
- (void)testTriggerPushProcess_emptyDictionary_stillStampsApsKey {
|
|
65
|
+
[self.bridge triggerPushProcessWithNotificationData:@{}];
|
|
66
|
+
|
|
67
|
+
OCMVerify([self.insiderClassMock handlePushLogWithUserInfo:[OCMArg checkWithBlock:^BOOL(NSDictionary *arg) {
|
|
68
|
+
return [arg[@"aps"] isEqualToString:@"insider"];
|
|
69
|
+
}]]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
- (void)testTriggerPushProcess_doesNotMutateOriginalDictionary {
|
|
73
|
+
NSDictionary *payload = @{ @"title": @"Hi" };
|
|
74
|
+
|
|
75
|
+
[self.bridge triggerPushProcessWithNotificationData:payload];
|
|
76
|
+
|
|
77
|
+
XCTAssertNil(payload[@"aps"], @"Original payload must remain untouched");
|
|
78
|
+
XCTAssertEqualObjects(payload[@"title"], @"Hi");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
- (void)testTriggerPushProcess_swallowsNativeException {
|
|
82
|
+
OCMStub([self.insiderClassMock handlePushLogWithUserInfo:[OCMArg any]])
|
|
83
|
+
.andThrow([NSException exceptionWithName:@"BoomException" reason:@"native failure" userInfo:nil]);
|
|
84
|
+
|
|
85
|
+
XCTAssertNoThrow([self.bridge triggerPushProcessWithNotificationData:@{ @"title": @"Hi" }],
|
|
86
|
+
@"Bridge must catch native exceptions and route them through Insider sendError");
|
|
87
|
+
|
|
88
|
+
OCMVerify([self.insiderClassMock sendError:[OCMArg any] desc:[OCMArg any]]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@end
|
package/package.json
CHANGED
package/src/InsiderAppCard.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export class InsiderAppCardImage {
|
|
|
29
29
|
*/
|
|
30
30
|
export class InsiderAppCardButton {
|
|
31
31
|
/** Unique identifier for the button. */
|
|
32
|
-
readonly
|
|
32
|
+
readonly id: string;
|
|
33
33
|
|
|
34
34
|
/** Identifier of the app card this button belongs to. */
|
|
35
35
|
readonly appCardId: string;
|
|
@@ -122,7 +122,7 @@ export type InsiderAppCardType = "message" | "image"
|
|
|
122
122
|
*/
|
|
123
123
|
export class InsiderAppCard {
|
|
124
124
|
/** Unique identifier for the app card. */
|
|
125
|
-
readonly
|
|
125
|
+
readonly id: string;
|
|
126
126
|
|
|
127
127
|
/** Type of the app card (text or image). */
|
|
128
128
|
readonly type: InsiderAppCardType;
|
package/src/InsiderAppCard.js
CHANGED
|
@@ -33,21 +33,21 @@ class InsiderAppCardImage {
|
|
|
33
33
|
|
|
34
34
|
class InsiderAppCardButton {
|
|
35
35
|
#data;
|
|
36
|
-
#
|
|
36
|
+
#id;
|
|
37
37
|
#appCardId;
|
|
38
38
|
#text;
|
|
39
39
|
#action;
|
|
40
40
|
|
|
41
41
|
constructor(appCardId, data) {
|
|
42
42
|
this.#data = data;
|
|
43
|
-
this.#
|
|
43
|
+
this.#id = data.id;
|
|
44
44
|
this.#appCardId = appCardId;
|
|
45
45
|
this.#text = data.text;
|
|
46
46
|
this.#action = InsiderAppCardAction.fromData(data.action);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
get
|
|
50
|
-
return this.#
|
|
49
|
+
get id() {
|
|
50
|
+
return this.#id;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
get appCardId() {
|
|
@@ -164,7 +164,7 @@ class InsiderAppCardFeedbackAction extends InsiderAppCardAction {
|
|
|
164
164
|
|
|
165
165
|
class InsiderAppCard {
|
|
166
166
|
#data;
|
|
167
|
-
#
|
|
167
|
+
#id;
|
|
168
168
|
#type;
|
|
169
169
|
#isRead;
|
|
170
170
|
#images;
|
|
@@ -174,7 +174,7 @@ class InsiderAppCard {
|
|
|
174
174
|
|
|
175
175
|
constructor(data) {
|
|
176
176
|
this.#data = data;
|
|
177
|
-
this.#
|
|
177
|
+
this.#id = data.id;
|
|
178
178
|
this.#type = data.type;
|
|
179
179
|
this.#isRead = data.read;
|
|
180
180
|
this.#images = data.images ? data.images.map(x => new InsiderAppCardImage(x)) : null;
|
|
@@ -183,8 +183,8 @@ class InsiderAppCard {
|
|
|
183
183
|
this.#action = InsiderAppCardAction.fromData(data.action);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
get
|
|
187
|
-
return this.#
|
|
186
|
+
get id() {
|
|
187
|
+
return this.#id;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
get type() {
|
|
@@ -217,7 +217,7 @@ class InsiderAppCard {
|
|
|
217
217
|
|
|
218
218
|
markAsRead(completion) {
|
|
219
219
|
const promise = new Promise((resolve, reject) => {
|
|
220
|
-
InsiderAppCards.markAsRead([this.
|
|
220
|
+
InsiderAppCards.markAsRead([this.id], error => {
|
|
221
221
|
if (error) reject(error);
|
|
222
222
|
else {
|
|
223
223
|
this.#isRead = true;
|
|
@@ -231,7 +231,7 @@ class InsiderAppCard {
|
|
|
231
231
|
|
|
232
232
|
markAsUnread(completion) {
|
|
233
233
|
const promise = new Promise((resolve, reject) => {
|
|
234
|
-
InsiderAppCards.markAsUnread([this.
|
|
234
|
+
InsiderAppCards.markAsUnread([this.id], error => {
|
|
235
235
|
if (error) reject(error);
|
|
236
236
|
else {
|
|
237
237
|
this.#isRead = false;
|
|
@@ -245,7 +245,7 @@ class InsiderAppCard {
|
|
|
245
245
|
|
|
246
246
|
delete(completion) {
|
|
247
247
|
const promise = new Promise((resolve, reject) => {
|
|
248
|
-
InsiderAppCards.delete([this.
|
|
248
|
+
InsiderAppCards.delete([this.id], error => {
|
|
249
249
|
if (error) reject(error);
|
|
250
250
|
else resolve();
|
|
251
251
|
});
|
package/src/InsiderAppCards.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ interface InsiderAppCards {
|
|
|
42
42
|
* console.log('Failed to fetch campaigns:', error.message);
|
|
43
43
|
* } else {
|
|
44
44
|
* campaignResponse.appCards.forEach(appCard => {
|
|
45
|
-
* console.log('App Card Id:', appCard.
|
|
45
|
+
* console.log('App Card Id:', appCard.id, 'Read:', appCard.isRead);
|
|
46
46
|
* });
|
|
47
47
|
* }
|
|
48
48
|
* });
|
|
@@ -63,7 +63,7 @@ interface InsiderAppCards {
|
|
|
63
63
|
* try {
|
|
64
64
|
* const campaignResponse = await appCards.getCampaigns();
|
|
65
65
|
* campaignResponse.appCards.forEach(appCard => {
|
|
66
|
-
* console.log('App Card Id:', appCard.
|
|
66
|
+
* console.log('App Card Id:', appCard.id, 'Read:', appCard.isRead);
|
|
67
67
|
* });
|
|
68
68
|
* } catch (error) {
|
|
69
69
|
* console.log('Failed to fetch campaigns:', error.message);
|