Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
1ac7370
docs: add Embedded component to example app
lposen Oct 10, 2025
15e1894
feat: add IterableEmbeddedManager class and update exports for embedd…
lposen Oct 10, 2025
f81b52d
Merge branch 'jwt/MOB-12298-new-improve-logger' into loren/embedded/M…
lposen Oct 10, 2025
952eebe
Merge branch 'loren/embedded/MOB-12260-create-embedded-tab-in-example…
lposen Oct 11, 2025
2a62913
feat: add embeddedManager property to Iterable class for managing emb…
lposen Oct 11, 2025
789ae83
refactor: remove unused methods and improve documentation in Iterable…
lposen Oct 11, 2025
9940f46
docs: enhance documentation
lposen Oct 11, 2025
e7d4ecb
feat: add embeddedMessagingEnabled property to IterableConfig
lposen Oct 11, 2025
12ee8fd
feat: enable embedded messaging configuration in RNIterableAPIModuleImpl
lposen Oct 11, 2025
93402fd
feat: update IterableConfig with prop that matches android name
lposen Oct 11, 2025
b916fb3
feat: implement getEmbeddedPlacementIds
lposen Oct 11, 2025
f456e20
feat: add Embedded tab to Main component in example app
lposen Oct 11, 2025
4441759
Merge branch 'loren/embedded/MOB-12260-create-embedded-tab-in-example…
lposen Oct 11, 2025
8201c4d
feat: display embedded manager status in Embedded component
lposen Oct 11, 2025
3f5fa41
Merge branch 'loren/embedded/MOB-12261-add-embedded-manager-class' in…
lposen Oct 11, 2025
b0e17f9
feat: add functionality to retrieve and display placement IDs in Embe…
lposen Oct 11, 2025
d42dd73
feat: implement methods to retrieve and sync embedded messages in RNI…
lposen Oct 11, 2025
67278b7
refactor: add sync and get messages
lposen Oct 11, 2025
06a94fc
refactor: remove unused methodss
lposen Oct 11, 2025
80ec4fc
refactor: remove commented-out error handling in Embedded component
lposen Oct 11, 2025
1bee2d6
feat: add session management methods to IterableEmbeddedManager
lposen Oct 13, 2025
0ba0b7f
docs: enhance session management method documentation in IterableEmbe…
lposen Oct 13, 2025
24d5a70
feat: add startEmbeddedSession method
lposen Oct 13, 2025
be8998a
feat: add endEmbeddedSession method and update session management in …
lposen Oct 13, 2025
cbb547f
docs: update IterableEmbeddedManager documentation to clarify enableE…
lposen Oct 13, 2025
760a9e3
feat: add syncEmbeddedMessages method
lposen Oct 13, 2025
1986caa
feat: implement getEmbeddedMessages method and add example on embedded
lposen Oct 13, 2025
94e46a6
feat: enhance Embedded component with ScrollView and button details d…
lposen Oct 13, 2025
e13756e
feat: implement setEnabled method for managing embedded manager state
lposen Oct 14, 2025
0e68107
Merge branch 'jwt/master' into loren/embedded/master
lposen Oct 15, 2025
28173ed
Merge branch 'loren/embedded/master' into loren/embedded/MOB-12260-cr…
lposen Nov 19, 2025
f0ff3f8
Merge branch 'jwt/master' into loren/embedded/master
lposen Nov 19, 2025
0be3376
Merge branch 'loren/embedded/master' into loren/embedded/MOB-12260-cr…
lposen Nov 19, 2025
79926df
Merge branch 'loren/embedded/MOB-12260-create-embedded-tab-in-example…
lposen Nov 19, 2025
ae09cb0
Merge remote-tracking branch 'origin/loren/embedded/MOB-12261-add-emb…
lposen Nov 19, 2025
c115493
Apply suggestions from code review
lposen Nov 19, 2025
25ec86d
feat: add chat icon to route configuration in App.constants.ts
lposen Nov 19, 2025
5206902
Merge branch 'loren/embedded/MOB-12260-create-embedded-tab-in-example…
lposen Nov 19, 2025
2b4da8a
refactor: wrap components in SafeAreaView for improved layout consist…
lposen Nov 19, 2025
2a5ac95
Merge branch 'loren/embedded/MOB-12261-add-embedded-manager-class' in…
lposen Nov 19, 2025
11f8227
feat: refactor IterableEmbeddedManager to use private variable
lposen Nov 19, 2025
935cf22
test: enhance Iterable configuration tests with additional assertions
lposen Nov 19, 2025
047f94b
fix: update embeddedMessagingEnabled configuration to use nullish coa…
lposen Nov 19, 2025
92c8c9e
test: add unit tests for IterableEmbeddedManager and enhance Iterable…
lposen Nov 19, 2025
59da91a
Merge branch 'loren/embedded/MOB-12261-add-embedded-manager-class' in…
lposen Nov 19, 2025
eb0dff5
Merge branch 'loren/embedded/MOB-12263-android-add-ability-to-sync-em…
lposen Nov 19, 2025
efbda57
feat: update login function to support JWT token retrieval and enhanc…
lposen Nov 19, 2025
67e116b
fix: update example usage of startSession and endSession methods in I…
lposen Nov 19, 2025
bab2b35
test: add unit tests for new embedded session methods in IterableEmbe…
lposen Nov 19, 2025
bb0c200
Merge branch 'loren/embedded/MOB-12265-start-end-session' into loren/…
lposen Nov 19, 2025
0984e9e
test: add unit tests for syncEmbeddedMessages and getEmbeddedMessages…
lposen Nov 19, 2025
62e248f
refactor: remove embedded messaging configuration from RNIterableAPIM…
lposen Nov 19, 2025
074d64f
Merge branch 'master' into loren/embedded/master
lposen Dec 11, 2025
733ebd3
Merge branch 'loren/embedded/master' into loren/embedded/MOB-12260-cr…
lposen Dec 11, 2025
d365761
Merge branch 'loren/embedded/MOB-12260-create-embedded-tab-in-example…
lposen Dec 11, 2025
d4c89e8
Merge branch 'loren/embedded/MOB-12261-add-embedded-manager-class' in…
lposen Dec 11, 2025
fd42bac
Merge branch 'loren/embedded/MOB-12263-android-add-ability-to-sync-em…
lposen Dec 11, 2025
1014a49
Merge branch 'loren/embedded/MOB-12265-start-end-session' into loren/…
lposen Dec 11, 2025
f5746ac
Merge pull request #741 from Iterable/loren/embedded/MOB-12260-create…
lposen Dec 15, 2025
b3ce46c
Merge branch 'loren/embedded/master' into loren/embedded/MOB-12261-ad…
lposen Dec 15, 2025
2c7a839
Merge pull request #742 from Iterable/loren/embedded/MOB-12261-add-em…
lposen Dec 15, 2025
e0e83b2
Merge pull request #744 from Iterable/loren/embedded/MOB-12263-androi…
lposen Dec 15, 2025
433a089
Merge branch 'loren/embedded/master' into loren/embedded/MOB-12265-st…
lposen Dec 15, 2025
834f2ab
Merge branch 'loren/embedded/MOB-12265-start-end-session' into loren/…
lposen Dec 15, 2025
c578308
Merge pull request #745 from Iterable/loren/embedded/MOB-12265-start-…
lposen Dec 18, 2025
256d8d1
Merge branch 'loren/embedded/master' into loren/embedded/MOB-12264-an…
lposen Dec 18, 2025
1c82b83
Merge pull request #746 from Iterable/loren/embedded/MOB-12264-androi…
lposen Dec 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

Expand All @@ -23,11 +24,12 @@
import com.iterable.iterableapi.IterableAction;
import com.iterable.iterableapi.IterableActionContext;
import com.iterable.iterableapi.IterableApi;
import com.iterable.iterableapi.IterableAttributionInfo;
import com.iterable.iterableapi.IterableAuthHandler;
import com.iterable.iterableapi.IterableAuthManager;
import com.iterable.iterableapi.IterableConfig;
import com.iterable.iterableapi.IterableCustomActionHandler;
import com.iterable.iterableapi.IterableAttributionInfo;
import com.iterable.iterableapi.IterableEmbeddedMessage;
import com.iterable.iterableapi.IterableHelper;
import com.iterable.iterableapi.IterableInAppCloseAction;
import com.iterable.iterableapi.IterableInAppHandler;
Expand All @@ -46,6 +48,7 @@
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -122,6 +125,7 @@ public void initializeWithApiKey(String apiKey, ReadableMap configReadableMap, S
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);

IterableApi.getInstance().getInAppManager().addListener(this);
IterableApi.getInstance().getEmbeddedManager().syncMessages();

// MOB-10421: Figure out what the error cases are and handle them appropriately
// This is just here to match the TS types and let the JS thread know when we are done initializing
Expand Down Expand Up @@ -185,6 +189,7 @@ public void initialize2WithApiKey(String apiKey, ReadableMap configReadableMap,
IterableApi.getInstance().setDeviceAttribute("reactNativeSDKVersion", version);

IterableApi.getInstance().getInAppManager().addListener(this);
IterableApi.getInstance().getEmbeddedManager().syncMessages();

// MOB-10421: Figure out what the error cases are and handle them appropriately
// This is just here to match the TS types and let the JS thread know when we are done initializing
Expand Down Expand Up @@ -683,14 +688,90 @@ public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
public void onInboxUpdated() {
sendEvent(EventName.receivedIterableInboxChanged.name(), null);
}
// ---------------------------------------------------------------------------------------
// endregion

// ---------------------------------------------------------------------------------------
// region Embedded messaging

public void syncEmbeddedMessages() {
IterableLogger.d(TAG, "syncEmbeddedMessages");
IterableApi.getInstance().getEmbeddedManager().syncMessages();
}

public void startEmbeddedSession() {
IterableLogger.d(TAG, "startEmbeddedSession");
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().startSession();
}

public void endEmbeddedSession() {
IterableLogger.d(TAG, "endEmbeddedSession");
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().endSession();
}

public void getEmbeddedPlacementIds(Promise promise) {
IterableLogger.d(TAG, "getEmbeddedPlacementIds");
try {
List<Long> placementIds = IterableApi.getInstance().getEmbeddedManager().getPlacementIds();
WritableArray writableArray = Arguments.createArray();
if (placementIds != null) {
for (Long placementId : placementIds) {
writableArray.pushDouble(placementId.doubleValue());
}
}
promise.resolve(writableArray);
} catch (Exception e) {
IterableLogger.e(TAG, "Error getting placement IDs: " + e.getLocalizedMessage());
promise.reject("", "Failed to get placement IDs: " + e.getLocalizedMessage());
}
}

public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
IterableLogger.d(TAG, "getEmbeddedMessages for placements: " + placementIds);

try {
List<IterableEmbeddedMessage> allMessages = new ArrayList<>();

if (placementIds == null || placementIds.size() == 0) {
// If no placement IDs provided, we need to get messages for all possible placements
// Since the Android SDK requires a placement ID, we'll use 0 as a default
// This might need to be adjusted based on the actual SDK behavior
List<IterableEmbeddedMessage> messages = IterableApi.getInstance().getEmbeddedManager().getMessages(0L);
if (messages != null) {
allMessages.addAll(messages);
}
} else {
// Convert ReadableArray to individual placement IDs and get messages for each
for (int i = 0; i < placementIds.size(); i++) {
long placementId = placementIds.getInt(i);
List<IterableEmbeddedMessage> messages = IterableApi.getInstance().getEmbeddedManager().getMessages(placementId);
if (messages != null) {
allMessages.addAll(messages);
}
}
}

JSONArray embeddedMessageJsonArray = Serialization.serializeEmbeddedMessages(allMessages);
IterableLogger.d(TAG, "Messages for placements: " + embeddedMessageJsonArray);

promise.resolve(Serialization.convertJsonToArray(embeddedMessageJsonArray));
} catch (JSONException e) {
IterableLogger.e(TAG, e.getLocalizedMessage());
promise.reject("", "Failed to fetch messages with error " + e.getLocalizedMessage());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function with high complexity (count = 11): getEmbeddedMessages [qlty:function-complexity]

}

// ---------------------------------------------------------------------------------------
// endregion
}

enum EventName {
handleUrlCalled,
handleCustomActionCalled,
handleInAppCalled,
handleAuthCalled,
receivedIterableInboxChanged,
handleAuthFailureCalled,
handleAuthSuccessCalled,
handleAuthFailureCalled
handleCustomActionCalled,
handleInAppCalled,
handleUrlCalled,
receivedIterableEmbeddedMessagesChanged,
receivedIterableInboxChanged
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@
import com.iterable.iterableapi.IterableActionContext;
import com.iterable.iterableapi.IterableConfig;
import com.iterable.iterableapi.IterableDataRegion;
import com.iterable.iterableapi.IterableEmbeddedMessage;
import com.iterable.iterableapi.IterableInAppCloseAction;
import com.iterable.iterableapi.IterableInAppDeleteActionType;
import com.iterable.iterableapi.IterableInAppHandler;
import com.iterable.iterableapi.IterableInAppLocation;
import com.iterable.iterableapi.IterableInAppMessage;
import com.iterable.iterableapi.IterableInboxSession;
import com.iterable.iterableapi.IterableLogger;
import com.iterable.iterableapi.RNIterableInternal;
import com.iterable.iterableapi.RetryPolicy;
import com.iterable.iterableapi.RNIterableInternal;

import org.json.JSONArray;
import org.json.JSONException;
Expand Down Expand Up @@ -137,6 +138,17 @@ static JSONArray serializeInAppMessages(List<IterableInAppMessage> inAppMessages
return inAppMessagesJson;
}

static JSONArray serializeEmbeddedMessages(List<IterableEmbeddedMessage> embeddedMessages) {
JSONArray embeddedMessagesJson = new JSONArray();
if (embeddedMessages != null) {
for (IterableEmbeddedMessage message : embeddedMessages) {
JSONObject messageJson = IterableEmbeddedMessage.Companion.toJSONObject(message);
embeddedMessagesJson.put(messageJson);
}
}
return embeddedMessagesJson;
}

static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableContextMap) {
try {
JSONObject iterableContextJSON = convertMapToJson(iterableContextMap);
Expand Down Expand Up @@ -218,6 +230,10 @@ static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableConte
configBuilder.setDataRegion(iterableDataRegion);
}

if (iterableContextJSON.has("enableEmbeddedMessaging")) {
configBuilder.setEnableEmbeddedMessaging(iterableContextJSON.optBoolean("enableEmbeddedMessaging"));
}

if (iterableContextJSON.has("retryPolicy")) {
JSONObject retryPolicyJson = iterableContextJSON.getJSONObject("retryPolicy");
int maxRetry = retryPolicyJson.getInt("maxRetry");
Expand Down
25 changes: 25 additions & 0 deletions android/src/newarch/java/com/RNIterableAPIModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,31 @@ public void pauseAuthRetries(boolean pauseRetry) {
moduleImpl.pauseAuthRetries(pauseRetry);
}

@Override
public void syncEmbeddedMessages() {
moduleImpl.syncEmbeddedMessages();
}

@Override
public void startEmbeddedSession() {
moduleImpl.startEmbeddedSession();
}

@Override
public void endEmbeddedSession() {
moduleImpl.endEmbeddedSession();
}

@Override
public void getEmbeddedPlacementIds(Promise promise) {
moduleImpl.getEmbeddedPlacementIds(promise);
}

@Override
public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
moduleImpl.getEmbeddedMessages(placementIds, promise);
}

public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
moduleImpl.sendEvent(eventName, eventData);
}
Expand Down
24 changes: 24 additions & 0 deletions android/src/oldarch/java/com/RNIterableAPIModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,30 @@ public void pauseAuthRetries(boolean pauseRetry) {
moduleImpl.pauseAuthRetries(pauseRetry);
}

@ReactMethod
public void syncEmbeddedMessages() {
moduleImpl.syncEmbeddedMessages();
}

@ReactMethod
public void startEmbeddedSession() {
moduleImpl.startEmbeddedSession();
}

@ReactMethod
public void endEmbeddedSession() {
moduleImpl.endEmbeddedSession();
}

@ReactMethod
public void getEmbeddedPlacementIds(Promise promise) {
moduleImpl.getEmbeddedPlacementIds(promise);
}

@ReactMethod
public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
moduleImpl.getEmbeddedMessages(placementIds, promise);
}

public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
moduleImpl.sendEvent(eventName, eventData);
Expand Down
3 changes: 3 additions & 0 deletions example/src/components/App/App.constants.ts

Large diffs are not rendered by default.

45 changes: 44 additions & 1 deletion example/src/components/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,59 @@
/* eslint-disable react-native/split-platform-components */
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useEffect } from 'react';
import { PermissionsAndroid, Platform } from 'react-native';

import { Route } from '../../constants/routes';
import { useIterableApp } from '../../hooks/useIterableApp';
import type { RootStackParamList } from '../../types';
import { Login } from '../Login';
import { Main } from './Main';
import type { RootStackParamList } from '../../types';

const Stack = createNativeStackNavigator<RootStackParamList>();

const requestNotificationPermission = async () => {
if (Platform.OS === 'android') {
const apiLevel = Platform.Version; // Get the Android API level

if (apiLevel >= 33) {
// Check if Android 13 or higher
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
{
title: 'Notification Permission',
message:
'This app needs access to your notifications for push, in-app messages, embedded messages and more.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('Notification permission granted');
} else {
console.log('Notification permission denied');
}
} catch (err) {
console.warn(err);
}
} else {
// For Android versions below 13, notification permission is generally not required
// or is automatically granted upon app installation.
console.log(
'Notification permission not required for this Android version.'
);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function with high complexity (count = 15): requestNotificationPermission [qlty:function-complexity]

};

export const App = () => {
const { isLoggedIn } = useIterableApp();

useEffect(() => {
requestNotificationPermission();
}, []);

return (
<Stack.Navigator>
{isLoggedIn ? (
Expand Down
8 changes: 8 additions & 0 deletions example/src/components/App/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { User } from '../User';
import { Inbox } from '../Inbox';
import { useIterableApp } from '../../hooks';
import { Commerce } from '../Commerce';
import { Embedded } from '../Embedded';

const Tab = createBottomTabNavigator<MainScreenParamList>();

Expand Down Expand Up @@ -44,6 +45,13 @@ export const Main = () => {
},
})}
/>
<Tab.Screen
name={Route.Embedded}
component={Embedded}
listeners={() => ({
tabPress: () => setIsInboxTab(false),
})}
/>
<Tab.Screen
name={Route.Commerce}
component={Commerce}
Expand Down
Loading
Loading