Skip to content

Commit 1c82b83

Browse files
authored
Merge pull request #746 from Iterable/loren/embedded/MOB-12264-android-sync-and-get-messages
[MOB-12264] Add sync messages and get messages to android
2 parents c578308 + 256d8d1 commit 1c82b83

18 files changed

+418
-23
lines changed

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,11 @@ public void onInboxUpdated() {
694694
// ---------------------------------------------------------------------------------------
695695
// region Embedded messaging
696696

697+
public void syncEmbeddedMessages() {
698+
IterableLogger.d(TAG, "syncEmbeddedMessages");
699+
IterableApi.getInstance().getEmbeddedManager().syncMessages();
700+
}
701+
697702
public void startEmbeddedSession() {
698703
IterableLogger.d(TAG, "startEmbeddedSession");
699704
IterableApi.getInstance().getEmbeddedManager().getEmbeddedSessionManager().startSession();
@@ -721,6 +726,41 @@ public void getEmbeddedPlacementIds(Promise promise) {
721726
}
722727
}
723728

729+
public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
730+
IterableLogger.d(TAG, "getEmbeddedMessages for placements: " + placementIds);
731+
732+
try {
733+
List<IterableEmbeddedMessage> allMessages = new ArrayList<>();
734+
735+
if (placementIds == null || placementIds.size() == 0) {
736+
// If no placement IDs provided, we need to get messages for all possible placements
737+
// Since the Android SDK requires a placement ID, we'll use 0 as a default
738+
// This might need to be adjusted based on the actual SDK behavior
739+
List<IterableEmbeddedMessage> messages = IterableApi.getInstance().getEmbeddedManager().getMessages(0L);
740+
if (messages != null) {
741+
allMessages.addAll(messages);
742+
}
743+
} else {
744+
// Convert ReadableArray to individual placement IDs and get messages for each
745+
for (int i = 0; i < placementIds.size(); i++) {
746+
long placementId = placementIds.getInt(i);
747+
List<IterableEmbeddedMessage> messages = IterableApi.getInstance().getEmbeddedManager().getMessages(placementId);
748+
if (messages != null) {
749+
allMessages.addAll(messages);
750+
}
751+
}
752+
}
753+
754+
JSONArray embeddedMessageJsonArray = Serialization.serializeEmbeddedMessages(allMessages);
755+
IterableLogger.d(TAG, "Messages for placements: " + embeddedMessageJsonArray);
756+
757+
promise.resolve(Serialization.convertJsonToArray(embeddedMessageJsonArray));
758+
} catch (JSONException e) {
759+
IterableLogger.e(TAG, e.getLocalizedMessage());
760+
promise.reject("", "Failed to fetch messages with error " + e.getLocalizedMessage());
761+
}
762+
}
763+
724764
// ---------------------------------------------------------------------------------------
725765
// endregion
726766
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,17 @@ static JSONArray serializeInAppMessages(List<IterableInAppMessage> inAppMessages
138138
return inAppMessagesJson;
139139
}
140140

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+
141152
static IterableConfig.Builder getConfigFromReadableMap(ReadableMap iterableContextMap) {
142153
try {
143154
JSONObject iterableContextJSON = convertMapToJson(iterableContextMap);

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

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

227+
@Override
228+
public void syncEmbeddedMessages() {
229+
moduleImpl.syncEmbeddedMessages();
230+
}
231+
227232
@Override
228233
public void startEmbeddedSession() {
229234
moduleImpl.startEmbeddedSession();
@@ -239,6 +244,11 @@ public void getEmbeddedPlacementIds(Promise promise) {
239244
moduleImpl.getEmbeddedPlacementIds(promise);
240245
}
241246

247+
@Override
248+
public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
249+
moduleImpl.getEmbeddedMessages(placementIds, promise);
250+
}
251+
242252
public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
243253
moduleImpl.sendEvent(eventName, eventData);
244254
}

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

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

231+
@ReactMethod
232+
public void syncEmbeddedMessages() {
233+
moduleImpl.syncEmbeddedMessages();
234+
}
235+
231236
@ReactMethod
232237
public void startEmbeddedSession() {
233238
moduleImpl.startEmbeddedSession();
@@ -243,6 +248,11 @@ public void getEmbeddedPlacementIds(Promise promise) {
243248
moduleImpl.getEmbeddedPlacementIds(promise);
244249
}
245250

251+
@ReactMethod
252+
public void getEmbeddedMessages(@Nullable ReadableArray placementIds, Promise promise) {
253+
moduleImpl.getEmbeddedMessages(placementIds, promise);
254+
}
255+
246256
public void sendEvent(@NonNull String eventName, @Nullable Object eventData) {
247257
moduleImpl.sendEvent(eventName, eventData);
248258
}

example/src/components/Embedded/Embedded.tsx

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
1-
import { Text, TouchableOpacity } from 'react-native';
1+
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
22
import { useCallback, useState } from 'react';
3-
import { Iterable } from '@iterable/react-native-sdk';
3+
import {
4+
Iterable,
5+
type IterableEmbeddedMessage,
6+
} from '@iterable/react-native-sdk';
47
import { SafeAreaView } from 'react-native-safe-area-context';
58

69
import styles from './Embedded.styles';
710

811
export const Embedded = () => {
912
const [placementIds, setPlacementIds] = useState<number[]>([]);
13+
const [embeddedMessages, setEmbeddedMessages] = useState<
14+
IterableEmbeddedMessage[]
15+
>([]);
16+
17+
const syncEmbeddedMessages = useCallback(() => {
18+
Iterable.embeddedManager.syncMessages();
19+
}, []);
20+
1021
const getPlacementIds = useCallback(() => {
11-
Iterable.embeddedManager.getPlacementIds().then((ids: unknown) => {
22+
return Iterable.embeddedManager.getPlacementIds().then((ids: unknown) => {
1223
console.log(ids);
1324
setPlacementIds(ids as number[]);
25+
return ids;
1426
});
1527
}, []);
1628

@@ -28,28 +40,69 @@ export const Embedded = () => {
2840
Iterable.embeddedManager.endSession();
2941
}, []);
3042

43+
const getEmbeddedMessages = useCallback(() => {
44+
getPlacementIds()
45+
.then((ids: number[]) => Iterable.embeddedManager.getMessages(ids))
46+
.then((messages: IterableEmbeddedMessage[]) => {
47+
setEmbeddedMessages(messages);
48+
console.log(messages);
49+
});
50+
}, [getPlacementIds]);
51+
3152
return (
3253
<SafeAreaView style={styles.container}>
3354
<Text style={styles.text}>EMBEDDED</Text>
34-
<Text style={styles.text}>
35-
Does embedded class exist? {Iterable.embeddedManager ? 'Yes' : 'No'}
36-
</Text>
37-
<Text style={styles.text}>
38-
Is embedded manager enabled?{' '}
39-
{Iterable.embeddedManager.isEnabled ? 'Yes' : 'No'}
40-
</Text>
41-
<Text style={styles.text}>
42-
Placement ids: [{placementIds.join(', ')}]
43-
</Text>
44-
<TouchableOpacity style={styles.button} onPress={getPlacementIds}>
45-
<Text style={styles.buttonText}>Get placement ids</Text>
46-
</TouchableOpacity>
47-
<TouchableOpacity style={styles.button} onPress={startEmbeddedSession}>
48-
<Text style={styles.buttonText}>Start embedded session</Text>
49-
</TouchableOpacity>
50-
<TouchableOpacity style={styles.button} onPress={endEmbeddedSession}>
51-
<Text style={styles.buttonText}>End embedded session</Text>
52-
</TouchableOpacity>
55+
<View style={styles.utilitySection}>
56+
<Text style={styles.text}>
57+
Does embedded class exist? {Iterable.embeddedManager ? 'Yes' : 'No'}
58+
</Text>
59+
<Text style={styles.text}>
60+
Is embedded manager enabled?{' '}
61+
{Iterable.embeddedManager.isEnabled ? 'Yes' : 'No'}
62+
</Text>
63+
<Text style={styles.text}>
64+
Placement ids: [{placementIds.join(', ')}]
65+
</Text>
66+
<TouchableOpacity style={styles.button} onPress={syncEmbeddedMessages}>
67+
<Text style={styles.buttonText}>Sync embedded messages</Text>
68+
</TouchableOpacity>
69+
<TouchableOpacity style={styles.button} onPress={getPlacementIds}>
70+
<Text style={styles.buttonText}>Get placement ids</Text>
71+
</TouchableOpacity>
72+
<TouchableOpacity style={styles.button} onPress={startEmbeddedSession}>
73+
<Text style={styles.buttonText}>Start embedded session</Text>
74+
</TouchableOpacity>
75+
<TouchableOpacity style={styles.button} onPress={endEmbeddedSession}>
76+
<Text style={styles.buttonText}>End embedded session</Text>
77+
</TouchableOpacity>
78+
<TouchableOpacity style={styles.button} onPress={getEmbeddedMessages}>
79+
<Text style={styles.buttonText}>Get embedded messages</Text>
80+
</TouchableOpacity>
81+
</View>
82+
<View style={styles.hr} />
83+
<ScrollView>
84+
<View style={styles.embeddedSection}>
85+
{embeddedMessages.map((message) => (
86+
<View key={message.metadata.messageId}>
87+
<Text>Embedded message</Text>
88+
<Text>metadata.messageId: {message.metadata.messageId}</Text>
89+
<Text>metadata.placementId: {message.metadata.placementId}</Text>
90+
<Text>elements.title: {message.elements?.title}</Text>
91+
<Text>elements.body: {message.elements?.body}</Text>
92+
{(message.elements?.buttons ?? []).map((button, buttonIndex) => (
93+
<View key={`${button.id}-${buttonIndex}`}>
94+
<Text>Button {buttonIndex + 1}</Text>
95+
<Text>button.id: {button.id}</Text>
96+
<Text>button.title: {button.title}</Text>
97+
<Text>button.action?.data: {button.action?.data}</Text>
98+
<Text>button.action?.type: {button.action?.type}</Text>
99+
</View>
100+
))}
101+
<Text>payload: {JSON.stringify(message.payload)}</Text>
102+
</View>
103+
))}
104+
</View>
105+
</ScrollView>
53106
</SafeAreaView>
54107
);
55108
};

src/__mocks__/MockRNIterableAPI.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,35 @@ export class MockRNIterableAPI {
149149
.fn()
150150
.mockResolvedValue([1, 2, 3] as number[]);
151151

152+
static syncEmbeddedMessages = jest.fn().mockResolvedValue(undefined);
153+
154+
static getEmbeddedMessages = jest.fn().mockResolvedValue([
155+
{
156+
metadata: {
157+
messageId: 'msg-1',
158+
campaignId: 123,
159+
placementId: 1,
160+
},
161+
elements: {
162+
title: 'Test Message 1',
163+
body: 'Test body 1',
164+
},
165+
payload: { customKey: 'customValue' },
166+
},
167+
{
168+
metadata: {
169+
messageId: 'msg-2',
170+
campaignId: 456,
171+
placementId: 2,
172+
},
173+
elements: {
174+
title: 'Test Message 2',
175+
body: 'Test body 2',
176+
},
177+
payload: null,
178+
},
179+
]);
180+
152181
// set messages function is to set the messages static property
153182
// this is for testing purposes only
154183
static setMessages(messages: IterableInAppMessage[]): void {

src/api/NativeRNIterableAPI.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
import type { TurboModule } from 'react-native';
22
import { TurboModuleRegistry } from 'react-native';
33

4+
// NOTE: No types can be imported because of the way new arch works, so we have
5+
// to re-define the types here.
6+
interface EmbeddedMessage {
7+
metadata: {
8+
messageId: string;
9+
placementId: number;
10+
campaignId?: number | null;
11+
isProof?: boolean;
12+
};
13+
elements: {
14+
buttons?:
15+
| {
16+
id: string;
17+
title?: string | null;
18+
action: { type: string; data?: string } | null;
19+
}[]
20+
| null;
21+
body?: string | null;
22+
mediaUrl?: string | null;
23+
mediaUrlCaption?: string | null;
24+
defaultAction?: { type: string; data?: string } | null;
25+
text?: { id: string; text?: string | null; label?: string | null }[] | null;
26+
title?: string | null;
27+
} | null;
28+
payload?: { [key: string]: string | number | boolean | null } | null;
29+
}
30+
431
export interface Spec extends TurboModule {
532
// Initialization
633
initializeWithApiKey(
@@ -119,9 +146,13 @@ export interface Spec extends TurboModule {
119146
pauseAuthRetries(pauseRetry: boolean): void;
120147

121148
// Embedded Messaging
149+
syncEmbeddedMessages(): void;
122150
startEmbeddedSession(): void;
123151
endEmbeddedSession(): void;
124152
getEmbeddedPlacementIds(): Promise<number[]>;
153+
getEmbeddedMessages(
154+
placementIds: number[] | null
155+
): Promise<EmbeddedMessage[]>;
125156

126157
// Wake app -- android only
127158
wakeApp(): void;

src/core/classes/IterableApi.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { IterableAttributionInfo } from './IterableAttributionInfo';
1212
import type { IterableCommerceItem } from './IterableCommerceItem';
1313
import { IterableConfig } from './IterableConfig';
1414
import { IterableLogger } from './IterableLogger';
15+
import type { IterableEmbeddedMessage } from '../../embedded/types/IterableEmbeddedMessage';
1516

1617
/**
1718
* Contains functions that directly interact with the native layer.
@@ -510,6 +511,14 @@ export class IterableApi {
510511
// ======================= EMBEDDED ===================== //
511512
// ====================================================== //
512513

514+
/**
515+
* Syncs embedded local cache with the server.
516+
*/
517+
static syncEmbeddedMessages() {
518+
IterableLogger.log('syncEmbeddedMessages');
519+
return RNIterableAPI.syncEmbeddedMessages();
520+
}
521+
513522
/**
514523
* Starts an embedded session.
515524
*/
@@ -534,6 +543,18 @@ export class IterableApi {
534543
return RNIterableAPI.getEmbeddedPlacementIds();
535544
}
536545

546+
/**
547+
* Get the embedded messages.
548+
*
549+
* @returns A Promise that resolves to an array of embedded messages.
550+
*/
551+
static getEmbeddedMessages(
552+
placementIds: number[] | null
553+
): Promise<IterableEmbeddedMessage[]> {
554+
IterableLogger.log('getEmbeddedMessages: ', placementIds);
555+
return RNIterableAPI.getEmbeddedMessages(placementIds);
556+
}
557+
537558
// ---- End EMBEDDED ---- //
538559

539560
// ====================================================== //

0 commit comments

Comments
 (0)