Skip to content

Commit 9044b76

Browse files
committed
android: store dispatched events when catalyst instance is not ready
1 parent 570d692 commit 9044b76

File tree

3 files changed

+201
-69
lines changed

3 files changed

+201
-69
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.batch.batch_rn;
2+
3+
import androidx.annotation.NonNull;
4+
5+
6+
import com.facebook.react.bridge.WritableMap;
7+
8+
/**
9+
* Simple class to help storing batch event
10+
*/
11+
public class RNBatchEvent {
12+
13+
@NonNull
14+
private final String name;
15+
16+
@NonNull
17+
private final WritableMap params;
18+
19+
20+
public RNBatchEvent(@NonNull String name, @NonNull WritableMap params) {
21+
this.name = name;
22+
this.params = params;
23+
}
24+
25+
@NonNull
26+
public String getName() {
27+
return name;
28+
}
29+
30+
@NonNull
31+
public WritableMap getParams() {
32+
return params;
33+
}
34+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package com.batch.batch_rn;
2+
3+
import android.util.Log;
4+
5+
import androidx.annotation.NonNull;
6+
import androidx.annotation.Nullable;
7+
8+
import com.batch.android.Batch;
9+
import com.batch.android.BatchEventDispatcher;
10+
import com.batch.android.BatchPushPayload;
11+
import com.facebook.react.bridge.Arguments;
12+
import com.facebook.react.bridge.LifecycleEventListener;
13+
import com.facebook.react.bridge.ReactApplicationContext;
14+
import com.facebook.react.bridge.WritableMap;
15+
import com.facebook.react.modules.core.DeviceEventManagerModule;
16+
17+
import java.util.LinkedList;
18+
19+
/**
20+
* Bridge class to handle events dispatched by the Batch native SDK
21+
*/
22+
public class RNBatchEventDispatcher implements BatchEventDispatcher {
23+
24+
/**
25+
* React Context
26+
*/
27+
@Nullable
28+
private ReactApplicationContext reactContext;
29+
30+
/**
31+
* Event Queue
32+
*
33+
* We need to queue events because Batch SDK is started before
34+
* we have react context catalyst instance ready.
35+
*/
36+
@NonNull
37+
private final LinkedList<RNBatchEvent> events = new LinkedList<>();
38+
39+
/**
40+
* Send event to the JS bridge
41+
* @param event dispatched event
42+
*/
43+
private void sendEvent(@NonNull RNBatchEvent event) {
44+
if (reactContext == null || !reactContext.hasActiveCatalystInstance()) {
45+
return;
46+
}
47+
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
48+
.emit(event.getName(), event.getParams());
49+
}
50+
51+
/**
52+
* Batch event dispatcher callback
53+
* @param type event type
54+
* @param payload event payload
55+
*/
56+
@Override
57+
public void dispatchEvent(@NonNull Batch.EventDispatcher.Type type,
58+
@NonNull Batch.EventDispatcher.Payload payload) {
59+
String eventName = this.mapBatchEventDispatcherTypeToRNEvent(type);
60+
if (eventName != null) {
61+
WritableMap params = Arguments.createMap();
62+
params.putBoolean("isPositiveAction", payload.isPositiveAction());
63+
params.putString("deeplink", payload.getDeeplink());
64+
params.putString("trackingId", payload.getTrackingId());
65+
params.putString("webViewAnalyticsIdentifier", payload.getWebViewAnalyticsID());
66+
67+
BatchPushPayload pushPayload = payload.getPushPayload();
68+
if (pushPayload != null) {
69+
params.putMap("pushPayload", Arguments.fromBundle(pushPayload.getPushBundle()));
70+
}
71+
72+
RNBatchEvent event = new RNBatchEvent(eventName, params);
73+
synchronized (events) {
74+
events.add(event);
75+
}
76+
77+
if (reactContext == null || !reactContext.hasActiveCatalystInstance()) {
78+
Log.d(RNBatchModule.LOGGER_TAG,
79+
"React context is null or has no active catalyst instance. Queuing event.");
80+
return;
81+
}
82+
dequeueEvents();
83+
}
84+
}
85+
86+
/**
87+
* Dequeue the stored events
88+
*/
89+
public void dequeueEvents() {
90+
if (events.isEmpty()) {
91+
return;
92+
}
93+
synchronized(events) {
94+
sendEvent(events.pop());
95+
}
96+
dequeueEvents();
97+
}
98+
99+
/**
100+
* Set the react context instance
101+
*
102+
* Note: We are using a LifecycleEventListener to be notified when the react context
103+
* has a catalyst instance ready
104+
* @param reactContext context
105+
*/
106+
public void setReactContext(final ReactApplicationContext reactContext) {
107+
this.reactContext = reactContext;
108+
this.reactContext.addLifecycleEventListener(new LifecycleEventListener() {
109+
@Override
110+
public void onHostResume() {
111+
// No we should have a catalyst instance ready
112+
if (reactContext.hasActiveCatalystInstance()) {
113+
dequeueEvents();
114+
reactContext.removeLifecycleEventListener(this);
115+
}
116+
}
117+
118+
@Override
119+
public void onHostPause() {
120+
// do noting
121+
}
122+
123+
@Override
124+
public void onHostDestroy() {
125+
// do noting
126+
}
127+
});
128+
}
129+
130+
/**
131+
* Helper method to map native batch event with react-native event
132+
* @param type event type
133+
* @return the event emitted through the JS bridge
134+
*/
135+
private @Nullable
136+
String mapBatchEventDispatcherTypeToRNEvent(@NonNull Batch.EventDispatcher.Type type) {
137+
switch (type) {
138+
case MESSAGING_SHOW:
139+
return "messaging_show";
140+
case MESSAGING_CLICK:
141+
return "messaging_click";
142+
case MESSAGING_CLOSE:
143+
return "messaging_close";
144+
case MESSAGING_AUTO_CLOSE:
145+
return "messaging_auto_close";
146+
case MESSAGING_CLOSE_ERROR:
147+
return "messaging_close_error";
148+
case MESSAGING_WEBVIEW_CLICK:
149+
return "messaging_webview_click";
150+
case NOTIFICATION_OPEN:
151+
return "notification_open";
152+
case NOTIFICATION_DISMISS:
153+
return "notification_dismiss";
154+
case NOTIFICATION_DISPLAY:
155+
return "notification_display";
156+
default:
157+
return null;
158+
}
159+
}
160+
}

android/src/main/java/com/batch/batch_rn/RNBatchModule.java

Lines changed: 7 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@
66
import android.content.res.Resources;
77
import android.graphics.Typeface;
88
import android.location.Location;
9-
import android.util.Log;
109

1110
import androidx.annotation.NonNull;
1211
import androidx.annotation.Nullable;
1312

1413
import com.batch.android.Batch;
1514
import com.batch.android.BatchActivityLifecycleHelper;
1615
import com.batch.android.BatchAttributesFetchListener;
17-
import com.batch.android.BatchEventDispatcher;
18-
import com.batch.android.BatchPushPayload;
1916
import com.batch.android.BatchTagCollectionsFetchListener;
2017
import com.batch.android.BatchUserAttribute;
2118
import com.batch.android.PushNotificationType;
@@ -25,10 +22,8 @@
2522
import com.batch.android.BatchUserDataEditor;
2623
import com.batch.android.Config;
2724
import com.batch.android.json.JSONObject;
28-
import com.facebook.react.bridge.Arguments;
2925
import com.facebook.react.bridge.Promise;
3026
import com.facebook.react.bridge.ReactApplicationContext;
31-
import com.facebook.react.bridge.ReactContext;
3227
import com.facebook.react.bridge.ReactContextBaseJavaModule;
3328
import com.facebook.react.bridge.ReactMethod;
3429
import com.facebook.react.bridge.ReadableArray;
@@ -38,7 +33,6 @@
3833
import com.facebook.react.bridge.WritableMap;
3934
import com.facebook.react.bridge.WritableNativeArray;
4035
import com.facebook.react.bridge.WritableNativeMap;
41-
import com.facebook.react.modules.core.DeviceEventManagerModule;
4236

4337
import java.net.URI;
4438
import java.util.Date;
@@ -49,18 +43,22 @@
4943
import java.util.Set;
5044
import java.util.UUID;
5145

52-
public class RNBatchModule extends ReactContextBaseJavaModule implements BatchEventDispatcher {
46+
public class RNBatchModule extends ReactContextBaseJavaModule {
5347

5448
private static final String NAME = "RNBatch";
5549
private static final String PLUGIN_VERSION_ENVIRONMENT_VARIABLE = "batch.plugin.version";
5650
private static final String PLUGIN_VERSION = "ReactNative/8.1.0";
5751

52+
public static final String LOGGER_TAG = "RNBatchBridge";
53+
5854
private static final String BATCH_BRIDGE_ERROR_CODE = "BATCH_BRIDGE_ERROR";
5955

6056
private final ReactApplicationContext reactContext;
6157

6258
private final Map<String, BatchInboxFetcher> batchInboxFetcherMap;
6359

60+
private static final RNBatchEventDispatcher eventDispatcher = new RNBatchEventDispatcher();
61+
6462
static {
6563
System.setProperty("batch.plugin.version", PLUGIN_VERSION);
6664
}
@@ -94,7 +92,7 @@ public static void initialize(Application application) {
9492
String packageName = application.getPackageName();
9593
String batchAPIKey = resources.getString(resources.getIdentifier("BATCH_API_KEY", "string", packageName));
9694
Batch.setConfig(new Config(batchAPIKey));
97-
95+
Batch.EventDispatcher.addDispatcher(eventDispatcher);
9896
try {
9997
boolean doNotDisturbEnabled = resources.getBoolean(resources.getIdentifier("BATCH_DO_NOT_DISTURB_INITIAL_STATE", "bool", packageName));
10098
Batch.Messaging.setDoNotDisturbEnabled(doNotDisturbEnabled);
@@ -112,7 +110,7 @@ public RNBatchModule(ReactApplicationContext reactContext) {
112110
super(reactContext);
113111
this.reactContext = reactContext;
114112
this.batchInboxFetcherMap = new HashMap<>();
115-
Batch.EventDispatcher.addDispatcher(this);
113+
eventDispatcher.setReactContext(reactContext);
116114
}
117115

118116
public void start() {
@@ -145,20 +143,6 @@ public void optOutAndWipeData(Promise promise) {
145143
promise.resolve(null);
146144
}
147145

148-
// EVENT EventDispatcher
149-
150-
private void sendEvent(ReactContext reactContext,
151-
String eventName,
152-
@Nullable WritableMap params) {
153-
154-
if (!reactContext.hasActiveCatalystInstance()) {
155-
Log.d(NAME, "React context has no active catalyst instance. Aborting send event.");
156-
return;
157-
}
158-
reactContext
159-
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
160-
.emit(eventName, params);
161-
}
162146

163147
@ReactMethod
164148
public void addListener(String eventName) {
@@ -170,52 +154,6 @@ public void removeListeners(double count) {
170154
// iOS only
171155
}
172156

173-
@Override
174-
public void dispatchEvent(@NonNull Batch.EventDispatcher.Type type,
175-
@NonNull Batch.EventDispatcher.Payload payload) {
176-
String eventName = this.mapBatchEventDispatcherTypeToRNEvent(type);
177-
if (eventName != null) {
178-
WritableMap params = Arguments.createMap();
179-
params.putBoolean("isPositiveAction", payload.isPositiveAction());
180-
params.putString("deeplink", payload.getDeeplink());
181-
params.putString("trackingId", payload.getTrackingId());
182-
params.putString("webViewAnalyticsIdentifier", payload.getWebViewAnalyticsID());
183-
184-
BatchPushPayload pushPayload = payload.getPushPayload();
185-
if (pushPayload != null) {
186-
params.putMap("pushPayload", Arguments.fromBundle(pushPayload.getPushBundle()));
187-
}
188-
189-
sendEvent(reactContext, eventName, params);
190-
}
191-
}
192-
193-
private @Nullable
194-
String mapBatchEventDispatcherTypeToRNEvent(@NonNull Batch.EventDispatcher.Type type) {
195-
switch (type) {
196-
case MESSAGING_SHOW:
197-
return "messaging_show";
198-
case MESSAGING_CLICK:
199-
return "messaging_click";
200-
case MESSAGING_CLOSE:
201-
return "messaging_close";
202-
case MESSAGING_AUTO_CLOSE:
203-
return "messaging_auto_close";
204-
case MESSAGING_CLOSE_ERROR:
205-
return "messaging_close_error";
206-
case MESSAGING_WEBVIEW_CLICK:
207-
return "messaging_webview_click";
208-
case NOTIFICATION_OPEN:
209-
return "notification_open";
210-
case NOTIFICATION_DISMISS:
211-
return "notification_dismiss";
212-
case NOTIFICATION_DISPLAY:
213-
return "notification_display";
214-
default:
215-
return null;
216-
}
217-
}
218-
219157
// PUSH MODULE
220158

221159
@ReactMethod

0 commit comments

Comments
 (0)