Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
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 @@ -643,6 +643,11 @@ public void onInboxUpdated() {
// ---------------------------------------------------------------------------------------
// 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();
Expand All @@ -653,6 +658,16 @@ public void endEmbeddedSession() {
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().endSession();
}

public void startEmbeddedImpression(String messageId, int placementId) {
IterableLogger.d(TAG, "startEmbeddedImpression");
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().startImpression(messageId, placementId);
}

public void pauseEmbeddedImpression(String messageId) {
IterableLogger.d(TAG, "pauseEmbeddedImpression");
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().pauseImpression(messageId);
}

public void getEmbeddedPlacementIds(Promise promise) {
IterableLogger.d(TAG, "getEmbeddedPlacementIds");
try {
Expand All @@ -670,6 +685,41 @@ public void getEmbeddedPlacementIds(Promise promise) {
}
}

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);
}
Comment on lines 702 to 709
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The implementation for handling null/empty placementIds is unclear. Using 0 as a default placement ID may not be the correct behavior. Consider either:

  1. Getting messages from all registered placements by iterating through them
  2. Throwing an error if no placement IDs are provided
  3. Documenting that 0 is a valid default placeholder in the comment

The current comment mentions "This might need to be adjusted based on the actual SDK behavior" which suggests this is uncertain logic that should be resolved.

Copilot uses AI. Check for mistakes.
}
}

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());
}
}

// ---------------------------------------------------------------------------------------
// endregion
}
Expand Down
11 changes: 11 additions & 0 deletions android/src/main/java/com/iterable/reactnative/Serialization.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,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
20 changes: 20 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,11 @@ public void pauseAuthRetries(boolean pauseRetry) {
moduleImpl.pauseAuthRetries(pauseRetry);
}

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

@Override
public void startEmbeddedSession() {
moduleImpl.startEmbeddedSession();
Expand All @@ -234,11 +239,26 @@ public void endEmbeddedSession() {
moduleImpl.endEmbeddedSession();
}

@Override
public void startEmbeddedImpression(String messageId, double placementId) {
moduleImpl.startEmbeddedImpression(messageId, (int) placementId);
}

@Override
public void pauseEmbeddedImpression(String messageId) {
moduleImpl.pauseEmbeddedImpression(messageId);
}

@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
20 changes: 20 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,11 @@ public void pauseAuthRetries(boolean pauseRetry) {
moduleImpl.pauseAuthRetries(pauseRetry);
}

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

@ReactMethod
public void startEmbeddedSession() {
moduleImpl.startEmbeddedSession();
Expand All @@ -238,11 +243,26 @@ public void endEmbeddedSession() {
moduleImpl.endEmbeddedSession();
}

@ReactMethod
public void startEmbeddedImpression(String messageId, double placementId) {
moduleImpl.startEmbeddedImpression(messageId, (int) placementId);
}

@ReactMethod
public void pauseEmbeddedImpression(String messageId) {
moduleImpl.pauseEmbeddedImpression(messageId);
}

@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
12 changes: 11 additions & 1 deletion example/src/components/Embedded/Embedded.styles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { StyleSheet } from 'react-native';
import { button, buttonText, container, hr } from '../../constants';
import { button, buttonText, container, hr, link } from '../../constants';

const styles = StyleSheet.create({
button,
Expand All @@ -11,7 +11,17 @@ const styles = StyleSheet.create({
gap: 16,
paddingHorizontal: 16,
},
embeddedTitle: {
fontSize: 16,
fontWeight: 'bold',
lineHeight: 20,
},
embeddedTitleContainer: {
display: 'flex',
flexDirection: 'row',
},
hr,
link,
text: { textAlign: 'center' },
utilitySection: {
paddingHorizontal: 16,
Expand Down
131 changes: 109 additions & 22 deletions example/src/components/Embedded/Embedded.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { Text, TouchableOpacity } from 'react-native';
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { useCallback, useState } from 'react';
import { Iterable } from '@iterable/react-native-sdk';
import {
Iterable,
type IterableEmbeddedMessage,
} from '@iterable/react-native-sdk';
import { SafeAreaView } from 'react-native-safe-area-context';

import styles from './Embedded.styles';

export const Embedded = () => {
const [placementIds, setPlacementIds] = useState<number[]>([]);
const [embeddedMessages, setEmbeddedMessages] = useState<
IterableEmbeddedMessage[]
>([]);

const syncEmbeddedMessages = useCallback(() => {
Iterable.embeddedManager.syncMessages();
}, []);

const getPlacementIds = useCallback(() => {
Iterable.embeddedManager.getPlacementIds().then((ids: unknown) => {
return Iterable.embeddedManager.getPlacementIds().then((ids: unknown) => {
console.log(ids);
setPlacementIds(ids as number[]);
return ids;
});
}, []);

Expand All @@ -28,28 +40,103 @@ export const Embedded = () => {
Iterable.embeddedManager.endSession();
}, []);

const getEmbeddedMessages = useCallback(() => {
getPlacementIds()
.then((ids: number[]) => Iterable.embeddedManager.getMessages(ids))
.then((messages: IterableEmbeddedMessage[]) => {
setEmbeddedMessages(messages);
console.log(messages);
});
}, [getPlacementIds]);

const startEmbeddedImpression = useCallback(
(message: IterableEmbeddedMessage) => {
console.log(`startEmbeddedImpression`, message);
Iterable.embeddedManager.startImpression(
message.metadata.messageId,
// TODO: check if this should be changed to a number, as per the type
Number(message.metadata.placementId)
Comment on lines +57 to +58
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

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

The TODO comment and Number() conversion are unnecessary. The placementId field is already typed as number in IterableEmbeddedMessageMetadata, so it can be passed directly to startImpression() which expects a number.

Suggested change
// TODO: check if this should be changed to a number, as per the type
Number(message.metadata.placementId)
message.metadata.placementId

Copilot uses AI. Check for mistakes.
);
},
[]
);

const pauseEmbeddedImpression = useCallback(
(message: IterableEmbeddedMessage) => {
console.log(`pauseEmbeddedImpression:`, message);
Iterable.embeddedManager.pauseImpression(message.metadata.messageId);
},
[]
);

return (
<SafeAreaView style={styles.container}>
<Text style={styles.text}>EMBEDDED</Text>
<Text style={styles.text}>
Does embedded class exist? {Iterable.embeddedManager ? 'Yes' : 'No'}
</Text>
<Text style={styles.text}>
Is embedded manager enabled?{' '}
{Iterable.embeddedManager.isEnabled ? 'Yes' : 'No'}
</Text>
<Text style={styles.text}>
Placement ids: [{placementIds.join(', ')}]
</Text>
<TouchableOpacity style={styles.button} onPress={getPlacementIds}>
<Text style={styles.buttonText}>Get placement ids</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={startEmbeddedSession}>
<Text style={styles.buttonText}>Start embedded session</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={endEmbeddedSession}>
<Text style={styles.buttonText}>End embedded session</Text>
</TouchableOpacity>
<View style={styles.utilitySection}>
<Text style={styles.text}>
Does embedded class exist? {Iterable.embeddedManager ? 'Yes' : 'No'}
</Text>
<Text style={styles.text}>
Is embedded manager enabled?{' '}
{Iterable.embeddedManager.isEnabled ? 'Yes' : 'No'}
</Text>
<Text style={styles.text}>
Placement ids: [{placementIds.join(', ')}]
</Text>
<TouchableOpacity style={styles.button} onPress={syncEmbeddedMessages}>
<Text style={styles.buttonText}>Sync messages</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={getPlacementIds}>
<Text style={styles.buttonText}>Get placement ids</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={startEmbeddedSession}>
<Text style={styles.buttonText}>Start session</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={endEmbeddedSession}>
<Text style={styles.buttonText}>End session</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={getEmbeddedMessages}>
<Text style={styles.buttonText}>Get messages</Text>
</TouchableOpacity>
</View>
<View style={styles.hr} />
<ScrollView>
<View style={styles.embeddedSection}>
{embeddedMessages.map((message) => (
<View key={message.metadata.messageId}>
<View style={styles.embeddedTitleContainer}>
<Text style={styles.embeddedTitle}>Embedded message | </Text>
<TouchableOpacity
onPress={() => startEmbeddedImpression(message)}
>
<Text style={styles.link}>Start impression</Text>
</TouchableOpacity>
<Text style={styles.embeddedTitle}> | </Text>
<TouchableOpacity
onPress={() => pauseEmbeddedImpression(message)}
>
<Text style={styles.link}>Pause impression</Text>
</TouchableOpacity>
</View>

<Text>metadata.messageId: {message.metadata.messageId}</Text>
<Text>metadata.placementId: {message.metadata.placementId}</Text>
<Text>elements.title: {message.elements?.title}</Text>
<Text>elements.body: {message.elements?.body}</Text>
{(message.elements?.buttons ?? []).map((button, buttonIndex) => (
<View key={`${button.id}-${buttonIndex}`}>
<Text>Button {buttonIndex + 1}</Text>
<Text>button.id: {button.id}</Text>
<Text>button.title: {button.title}</Text>
<Text>button.action?.data: {button.action?.data}</Text>
<Text>button.action?.type: {button.action?.type}</Text>
</View>
))}
<Text>payload: {JSON.stringify(message.payload)}</Text>
</View>
))}
</View>
</ScrollView>
</SafeAreaView>
);
};
Expand Down
5 changes: 5 additions & 0 deletions example/src/constants/styles/typography.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,8 @@ export const requiredStar: TextStyle = {
...label,
color: colors.textDestructive,
};

export const link: TextStyle = {
color: colors.textInteractive,
textDecorationLine: 'underline',
};
8 changes: 8 additions & 0 deletions src/__mocks__/MockRNIterableAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ export class MockRNIterableAPI {
.fn()
.mockResolvedValue([1, 2, 3] as number[]);

static syncEmbeddedMessages = jest.fn();

static getEmbeddedMessages = jest.fn().mockResolvedValue([]);

static startEmbeddedImpression = jest.fn();

static pauseEmbeddedImpression = jest.fn();

// set messages function is to set the messages static property
// this is for testing purposes only
static setMessages(messages: IterableInAppMessage[]): void {
Expand Down
Loading