Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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 @@ -651,6 +651,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 Down Expand Up @@ -678,6 +683,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);
}
}
}

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());
}
Comment on lines +686 to +718
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
}
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
10 changes: 10 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 @@ -239,6 +244,11 @@ 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
10 changes: 10 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 @@ -243,6 +248,11 @@ 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
97 changes: 75 additions & 22 deletions example/src/components/Embedded/Embedded.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import { Text, TouchableOpacity, View } 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 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 @@ -27,28 +39,69 @@ 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]);

return (
<View 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 embedded 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 embedded session</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={endEmbeddedSession}>
<Text style={styles.buttonText}>End embedded session</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={getEmbeddedMessages}>
<Text style={styles.buttonText}>Get embedded messages</Text>
</TouchableOpacity>
</View>
<View style={styles.hr} />
<ScrollView>
<View style={styles.embeddedSection}>
{embeddedMessages.map((message) => (
<View key={message.metadata.messageId}>
<Text>Embedded message</Text>
<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>
</View>
);
};
Expand Down
31 changes: 31 additions & 0 deletions src/api/NativeRNIterableAPI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

// NOTE: No types can be imported because of the way new arch works, so we have
// to re-define the types here.
interface EmbeddedMessage {
metadata: {
messageId: string;
placementId: number;
campaignId?: number | null;
isProof?: boolean;
};
elements: {
buttons?:
| {
id: string;
title?: string | null;
action: { type: string; data?: string } | null;
}[]
| null;
body?: string | null;
mediaUrl?: string | null;
mediaUrlCaption?: string | null;
defaultAction?: { type: string; data?: string } | null;
text?: { id: string; text?: string | null; label?: string | null }[] | null;
title?: string | null;
} | null;
payload?: { [key: string]: string | number | boolean | null } | null;
}

export interface Spec extends TurboModule {
// Initialization
initializeWithApiKey(
Expand Down Expand Up @@ -119,9 +146,13 @@ export interface Spec extends TurboModule {
pauseAuthRetries(pauseRetry: boolean): void;

// Embedded Messaging
syncEmbeddedMessages(): void;
startEmbeddedSession(): void;
endEmbeddedSession(): void;
getEmbeddedPlacementIds(): Promise<number[]>;
getEmbeddedMessages(
placementIds: number[] | null
): Promise<EmbeddedMessage[]>;

// Wake app -- android only
wakeApp(): void;
Expand Down
21 changes: 21 additions & 0 deletions src/core/classes/IterableApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { IterableAttributionInfo } from './IterableAttributionInfo';
import type { IterableCommerceItem } from './IterableCommerceItem';
import { IterableConfig } from './IterableConfig';
import { IterableLogger } from './IterableLogger';
import type { IterableEmbeddedMessage } from '../../embedded/types/IterableEmbeddedMessage';

/**
* Contains functions that directly interact with the native layer.
Expand Down Expand Up @@ -510,6 +511,14 @@ export class IterableApi {
// ======================= EMBEDDED ===================== //
// ====================================================== //

/**
* Syncs embedded local cache with the server.
*/
static syncEmbeddedMessages() {
IterableLogger.log('syncEmbeddedMessages');
return RNIterableAPI.syncEmbeddedMessages();
}

/**
* Starts an embedded session.
*/
Expand All @@ -534,6 +543,18 @@ export class IterableApi {
return RNIterableAPI.getEmbeddedPlacementIds();
}

/**
* Get the embedded messages.
*
* @returns A Promise that resolves to an array of embedded messages.
*/
static getEmbeddedMessages(
placementIds: number[] | null
): Promise<IterableEmbeddedMessage[]> {
IterableLogger.log('getEmbeddedMessages: ', placementIds);
return RNIterableAPI.getEmbeddedMessages(placementIds);
}

// ---- End EMBEDDED ---- //

// ====================================================== //
Expand Down
36 changes: 36 additions & 0 deletions src/embedded/classes/IterableEmbeddedManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IterableApi } from '../../core/classes/IterableApi';
import type { IterableEmbeddedMessage } from '../types/IterableEmbeddedMessage';

/**
* Manages embedded messages from Iterable.
Expand All @@ -20,6 +21,29 @@ export class IterableEmbeddedManager {
*/
isEnabled = false;

/**
* Syncs embedded local cache with the server.
*
* When your app first launches, and each time it comes to the foreground,
* Iterable's iOS SDK automatically refresh a local, on-device cache of
* embedded messages for the signed-in user. These are the messages the
* signed-in user is eligible to see.
*
* At key points during your app's lifecycle, you may want to manually refresh
* your app's local cache of embedded messages. For example, as users navigate
* around, on pull-to-refresh, etc.
*
* However, do not poll for new embedded messages at a regular interval.
*
* @example
* ```typescript
* IterableEmbeddedManager.syncMessages();
* ```
*/
syncMessages() {
return IterableApi.syncEmbeddedMessages();
}

/**
* Retrieves a list of placement IDs for the embedded manager.
*
Expand All @@ -29,6 +53,18 @@ export class IterableEmbeddedManager {
return IterableApi.getEmbeddedPlacementIds();
}

/**
* Retrieves a list of embedded messages the user is eligible to see.
*
* @param placementIds - The placement IDs to retrieve messages for.
* @returns A Promise that resolves to an array of embedded messages.
*/
getMessages(
placementIds: number[] | null
): Promise<IterableEmbeddedMessage[]> {
return IterableApi.getEmbeddedMessages(placementIds);
}

/**
* Starts a session.
*
Expand Down
1 change: 1 addition & 0 deletions src/embedded/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './classes';
export * from './types';
Loading