Skip to content

Commit 52c7a7b

Browse files
authored
Merge pull request #751 from Iterable/loren/embedded/master
[MOB-7052] Embedded messaging feature
2 parents 574d624 + 7fce509 commit 52c7a7b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2718
-96
lines changed

android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java

Lines changed: 126 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.facebook.react.bridge.ReadableArray;
1616
import com.facebook.react.bridge.ReadableMap;
1717
import com.facebook.react.bridge.UiThreadUtil;
18+
import com.facebook.react.bridge.WritableArray;
1819
import com.facebook.react.bridge.WritableMap;
1920
import com.facebook.react.modules.core.DeviceEventManagerModule;
2021

@@ -23,11 +24,13 @@
2324
import com.iterable.iterableapi.IterableAction;
2425
import com.iterable.iterableapi.IterableActionContext;
2526
import com.iterable.iterableapi.IterableApi;
27+
import com.iterable.iterableapi.IterableAttributionInfo;
2628
import com.iterable.iterableapi.IterableAuthHandler;
2729
import com.iterable.iterableapi.IterableAuthManager;
2830
import com.iterable.iterableapi.IterableConfig;
2931
import com.iterable.iterableapi.IterableCustomActionHandler;
30-
import com.iterable.iterableapi.IterableAttributionInfo;
32+
import com.iterable.iterableapi.IterableEmbeddedMessage;
33+
import com.iterable.iterableapi.IterableEmbeddedUpdateHandler;
3134
import com.iterable.iterableapi.IterableHelper;
3235
import com.iterable.iterableapi.IterableInAppCloseAction;
3336
import com.iterable.iterableapi.IterableInAppHandler;
@@ -46,10 +49,11 @@
4649
import java.util.Map;
4750
import java.util.HashMap;
4851
import java.util.List;
52+
import java.util.ArrayList;
4953
import java.util.concurrent.CountDownLatch;
5054
import java.util.concurrent.TimeUnit;
5155

52-
public class RNIterableAPIModuleImpl implements IterableUrlHandler, IterableCustomActionHandler, IterableInAppHandler, IterableAuthHandler, IterableInAppManager.Listener {
56+
public class RNIterableAPIModuleImpl implements IterableUrlHandler, IterableCustomActionHandler, IterableInAppHandler, IterableAuthHandler, IterableInAppManager.Listener, IterableEmbeddedUpdateHandler {
5357
public static final String NAME = "RNIterableAPI";
5458

5559
private static String TAG = "RNIterableAPIModule";
@@ -89,6 +93,9 @@ public void initializeWithApiKey(String apiKey, ReadableMap configReadableMap, S
8993
configBuilder.setAuthHandler(this);
9094
}
9195

96+
// Check if embedded messaging is enabled before building config
97+
boolean enableEmbeddedMessaging = configReadableMap.hasKey("enableEmbeddedMessaging") && configReadableMap.getBoolean("enableEmbeddedMessaging");
98+
9299
IterableConfig config = configBuilder.build();
93100
IterableApi.initialize(reactContext, apiKey, config);
94101

@@ -122,6 +129,12 @@ public void initializeWithApiKey(String apiKey, ReadableMap configReadableMap, S
122129
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);
123130

124131
IterableApi.getInstance().getInAppManager().addListener(this);
132+
IterableApi.getInstance().getEmbeddedManager().syncMessages();
133+
134+
// Add embedded update listener if embedded messaging is enabled
135+
if (enableEmbeddedMessaging) {
136+
IterableApi.getInstance().getEmbeddedManager().addUpdateListener(this);
137+
}
125138

126139
// MOB-10421: Figure out what the error cases are and handle them appropriately
127140
// This is just here to match the TS types and let the JS thread know when we are done initializing
@@ -152,6 +165,9 @@ public void initialize2WithApiKey(String apiKey, ReadableMap configReadableMap,
152165
// override in the Android SDK. Check with @Ayyanchira and @evantk91 to
153166
// see what the best approach is.
154167

168+
// Check if embedded messaging is enabled before building config
169+
boolean enableEmbeddedMessaging = configReadableMap.hasKey("enableEmbeddedMessaging") && configReadableMap.getBoolean("enableEmbeddedMessaging");
170+
155171
IterableConfig config = configBuilder.build();
156172
IterableApi.initialize(reactContext, apiKey, config);
157173

@@ -185,6 +201,12 @@ public void initialize2WithApiKey(String apiKey, ReadableMap configReadableMap,
185201
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);
186202

187203
IterableApi.getInstance().getInAppManager().addListener(this);
204+
IterableApi.getInstance().getEmbeddedManager().syncMessages();
205+
206+
// Add embedded update listener if embedded messaging is enabled
207+
if (enableEmbeddedMessaging) {
208+
IterableApi.getInstance().getEmbeddedManager().addUpdateListener(this);
209+
}
188210

189211
// MOB-10421: Figure out what the error cases are and handle them appropriately
190212
// This is just here to match the TS types and let the JS thread know when we are done initializing
@@ -683,14 +705,111 @@ public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
683705
public void onInboxUpdated() {
684706
sendEvent(EventName.receivedIterableInboxChanged.name(), null);
685707
}
708+
709+
@Override
710+
public void onMessagesUpdated() {
711+
IterableLogger.d(TAG, "onMessagesUpdated");
712+
sendEvent(EventName.handleEmbeddedMessageUpdateCalled.name(), null);
713+
}
714+
715+
@Override
716+
public void onEmbeddedMessagingDisabled() {
717+
IterableLogger.d(TAG, "onEmbeddedMessagingDisabled");
718+
sendEvent(EventName.handleEmbeddedMessagingDisabledCalled.name(), null);
719+
}
720+
// ---------------------------------------------------------------------------------------
721+
// endregion
722+
723+
// ---------------------------------------------------------------------------------------
724+
// region Embedded messaging
725+
726+
public void syncEmbeddedMessages() {
727+
IterableLogger.d(TAG, "syncEmbeddedMessages");
728+
IterableApi.getInstance().getEmbeddedManager().syncMessages();
729+
}
730+
731+
public void startEmbeddedSession() {
732+
IterableLogger.d(TAG, "startEmbeddedSession");
733+
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().startSession();
734+
}
735+
736+
public void endEmbeddedSession() {
737+
IterableLogger.d(TAG, "endEmbeddedSession");
738+
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().endSession();
739+
}
740+
741+
public void startEmbeddedImpression(String messageId, int placementId) {
742+
IterableLogger.d(TAG, "startEmbeddedImpression");
743+
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().startImpression(messageId, placementId);
744+
}
745+
746+
public void pauseEmbeddedImpression(String messageId) {
747+
IterableLogger.d(TAG, "pauseEmbeddedImpression");
748+
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().pauseImpression(messageId);
749+
}
750+
751+
public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
752+
IterableLogger.d(TAG, "getEmbeddedMessages for placements: " + placementIds);
753+
754+
try {
755+
List<IterableEmbeddedMessage> allMessages = new ArrayList<>();
756+
757+
if (placementIds == null || placementIds.size() == 0) {
758+
// If no placement IDs provided, get messages from all placements
759+
// Get all available placement IDs and fetch messages for each
760+
List<Long> allPlacementIds = IterableApi.getInstance().getEmbeddedManager().getPlacementIds();
761+
IterableLogger.d(TAG, "Getting messages for all placement IDs: " + allPlacementIds);
762+
763+
for (Long placementId : allPlacementIds) {
764+
List<IterableEmbeddedMessage> messages = IterableApi.getInstance().getEmbeddedManager().getMessages(placementId);
765+
if (messages != null) {
766+
allMessages.addAll(messages);
767+
}
768+
}
769+
} else {
770+
// Convert ReadableArray to individual placement IDs and get messages for each
771+
for (int i = 0; i < placementIds.size(); i++) {
772+
long placementId = placementIds.getInt(i);
773+
List<IterableEmbeddedMessage> messages = IterableApi.getInstance().getEmbeddedManager().getMessages(placementId);
774+
if (messages != null) {
775+
allMessages.addAll(messages);
776+
}
777+
}
778+
}
779+
780+
JSONArray embeddedMessageJsonArray = Serialization.serializeEmbeddedMessages(allMessages);
781+
IterableLogger.d(TAG, "Messages for placements: " + embeddedMessageJsonArray);
782+
783+
promise.resolve(Serialization.convertJsonToArray(embeddedMessageJsonArray));
784+
} catch (JSONException e) {
785+
IterableLogger.e(TAG, e.getLocalizedMessage());
786+
promise.reject("", "Failed to fetch messages with error " + e.getLocalizedMessage());
787+
}
788+
}
789+
790+
public void trackEmbeddedClick(ReadableMap messageMap, String buttonId, String clickedUrl) {
791+
IterableLogger.d(TAG, "trackEmbeddedClick: buttonId: " + buttonId + " clickedUrl: " + clickedUrl);
792+
IterableEmbeddedMessage message = Serialization.embeddedMessageFromReadableMap(messageMap);
793+
if (message != null) {
794+
IterableApi.getInstance().trackEmbeddedClick(message, buttonId, clickedUrl);
795+
} else {
796+
IterableLogger.e(TAG, "Failed to convert message map to IterableEmbeddedMessage");
797+
}
798+
}
799+
800+
// ---------------------------------------------------------------------------------------
801+
// endregion
686802
}
687803

688804
enum EventName {
689-
handleUrlCalled,
690-
handleCustomActionCalled,
691-
handleInAppCalled,
692805
handleAuthCalled,
693-
receivedIterableInboxChanged,
806+
handleAuthFailureCalled,
694807
handleAuthSuccessCalled,
695-
handleAuthFailureCalled
808+
handleCustomActionCalled,
809+
handleEmbeddedMessageUpdateCalled,
810+
handleEmbeddedMessagingDisabledCalled,
811+
handleInAppCalled,
812+
handleUrlCalled,
813+
receivedIterableEmbeddedMessagesChanged,
814+
receivedIterableInboxChanged
696815
}

android/src/main/java/com/iterable/reactnative/Serialization.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@
1616
import com.iterable.iterableapi.IterableActionContext;
1717
import com.iterable.iterableapi.IterableConfig;
1818
import com.iterable.iterableapi.IterableDataRegion;
19+
import com.iterable.iterableapi.IterableEmbeddedMessage;
1920
import com.iterable.iterableapi.IterableInAppCloseAction;
2021
import com.iterable.iterableapi.IterableInAppDeleteActionType;
2122
import com.iterable.iterableapi.IterableInAppHandler;
2223
import com.iterable.iterableapi.IterableInAppLocation;
2324
import com.iterable.iterableapi.IterableInAppMessage;
2425
import com.iterable.iterableapi.IterableInboxSession;
2526
import com.iterable.iterableapi.IterableLogger;
26-
import com.iterable.iterableapi.RNIterableInternal;
2727
import com.iterable.iterableapi.RetryPolicy;
28+
import com.iterable.iterableapi.RNIterableInternal;
2829

2930
import org.json.JSONArray;
3031
import org.json.JSONException;
@@ -137,6 +138,33 @@ static JSONArray serializeInAppMessages(List<IterableInAppMessage> inAppMessages
137138
return inAppMessagesJson;
138139
}
139140

141+
static JSONArray serializeEmbeddedMessages(List<IterableEmbeddedMessage> embeddedMessages) {
142+
JSONArray embeddedMessagesJson = new JSONArray();
143+
if (embeddedMessages != null) {
144+
for (IterableEmbeddedMessage message : embeddedMessages) {
145+
JSONObject messageJson = IterableEmbeddedMessage.Companion.toJSONObject(message);
146+
embeddedMessagesJson.put(messageJson);
147+
}
148+
}
149+
return embeddedMessagesJson;
150+
}
151+
152+
/**
153+
* Converts a ReadableMap to an IterableEmbeddedMessage.
154+
*
155+
* This is needed as in new arch you can only pass in basic types, which
156+
* then need to be converted in the native layer.
157+
*/
158+
static IterableEmbeddedMessage embeddedMessageFromReadableMap(ReadableMap messageMap) {
159+
try {
160+
JSONObject messageJson = convertMapToJson(messageMap);
161+
return IterableEmbeddedMessage.Companion.fromJSONObject(messageJson);
162+
} catch (JSONException e) {
163+
IterableLogger.e(TAG, "Failed to convert ReadableMap to IterableEmbeddedMessage: " + e.getLocalizedMessage());
164+
return null;
165+
}
166+
}
167+
140168
static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableContextMap) {
141169
try {
142170
JSONObject iterableContextJSON = convertMapToJson(iterableContextMap);
@@ -218,6 +246,10 @@ static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableConte
218246
configBuilder.setDataRegion(iterableDataRegion);
219247
}
220248

249+
if (iterableContextJSON.has("enableEmbeddedMessaging")) {
250+
configBuilder.setEnableEmbeddedMessaging(iterableContextJSON.optBoolean("enableEmbeddedMessaging"));
251+
}
252+
221253
if (iterableContextJSON.has("retryPolicy")) {
222254
JSONObject retryPolicyJson = iterableContextJSON.getJSONObject("retryPolicy");
223255
int maxRetry = retryPolicyJson.getInt("maxRetry");

android/src/newarch/java/com/RNIterableAPIModule.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,41 @@ public void pauseAuthRetries(boolean pauseRetry) {
224224
moduleImpl.pauseAuthRetries(pauseRetry);
225225
}
226226

227+
@Override
228+
public void syncEmbeddedMessages() {
229+
moduleImpl.syncEmbeddedMessages();
230+
}
231+
232+
@Override
233+
public void startEmbeddedSession() {
234+
moduleImpl.startEmbeddedSession();
235+
}
236+
237+
@Override
238+
public void endEmbeddedSession() {
239+
moduleImpl.endEmbeddedSession();
240+
}
241+
242+
@Override
243+
public void startEmbeddedImpression(String messageId, double placementId) {
244+
moduleImpl.startEmbeddedImpression(messageId, (int) placementId);
245+
}
246+
247+
@Override
248+
public void pauseEmbeddedImpression(String messageId) {
249+
moduleImpl.pauseEmbeddedImpression(messageId);
250+
}
251+
252+
@Override
253+
public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
254+
moduleImpl.getEmbeddedMessages(placementIds, promise);
255+
}
256+
257+
@Override
258+
public void trackEmbeddedClick(ReadableMap message, String buttonId, String clickedUrl) {
259+
moduleImpl.trackEmbeddedClick(message, buttonId, clickedUrl);
260+
}
261+
227262
public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
228263
moduleImpl.sendEvent(eventName, eventData);
229264
}

android/src/oldarch/java/com/RNIterableAPIModule.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,40 @@ public void pauseAuthRetries(boolean pauseRetry) {
228228
moduleImpl.pauseAuthRetries(pauseRetry);
229229
}
230230

231+
@ReactMethod
232+
public void syncEmbeddedMessages() {
233+
moduleImpl.syncEmbeddedMessages();
234+
}
235+
236+
@ReactMethod
237+
public void startEmbeddedSession() {
238+
moduleImpl.startEmbeddedSession();
239+
}
240+
241+
@ReactMethod
242+
public void endEmbeddedSession() {
243+
moduleImpl.endEmbeddedSession();
244+
}
245+
246+
@ReactMethod
247+
public void startEmbeddedImpression(String messageId, double placementId) {
248+
moduleImpl.startEmbeddedImpression(messageId, (int) placementId);
249+
}
250+
251+
@ReactMethod
252+
public void pauseEmbeddedImpression(String messageId) {
253+
moduleImpl.pauseEmbeddedImpression(messageId);
254+
}
255+
256+
@ReactMethod
257+
public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
258+
moduleImpl.getEmbeddedMessages(placementIds, promise);
259+
}
260+
261+
@ReactMethod
262+
public void trackEmbeddedClick(ReadableMap message, String buttonId, String clickedUrl) {
263+
moduleImpl.trackEmbeddedClick(message, buttonId, clickedUrl);
264+
}
231265

232266
public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
233267
moduleImpl.sendEvent(eventName, eventData);

example/android/app/src/main/java/iterable/reactnativesdk/example/MainActivity.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.facebook.react.ReactActivity
66
import com.facebook.react.ReactActivityDelegate
77
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
88
import com.facebook.react.defaults.DefaultReactActivityDelegate
9+
import com.iterable.iterableapi.IterableApi
910

1011
class MainActivity : ReactActivity() {
1112

@@ -27,6 +28,8 @@ class MainActivity : ReactActivity() {
2728
* This being in Kotlin **may** cause issues with react-native-screens
2829
*/
2930
override fun onCreate(savedInstanceState: Bundle?) {
31+
IterableApi.setContext(this)
32+
// Call super.onCreate with null to prevent savedInstanceState restoration issues
3033
super.onCreate(null)
3134
}
32-
}
35+
}

0 commit comments

Comments
 (0)