diff --git a/CHANGELOG.md b/CHANGELOG.md index 7413f4c56..dfd24cca3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Updates +- Added optional placement IDs to `Iterable.embeddedManager.syncMessages()` so apps can sync embedded messages for specific placements only (SDK-751). When omitted, null, or empty, behavior matches the previous sync-all call. - Added `IterableConfig.onEmbeddedMessagingSyncSucceeded` and `IterableConfig.onEmbeddedMessagingSyncFailed` so apps can observe embedded messaging sync outcomes (SDK-749). Failure callbacks receive a nullable reason diff --git a/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java b/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java index a8dc06fdf..5f025dca2 100644 --- a/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java +++ b/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java @@ -786,9 +786,18 @@ public void onEmbeddedMessagingSyncFailed(@Nullable String reason) { // --------------------------------------------------------------------------------------- // region Embedded messaging - public void syncEmbeddedMessages() { - IterableLogger.d(TAG, "syncEmbeddedMessages"); - IterableApi.getInstance().getEmbeddedManager().syncMessages(); + public void syncEmbeddedMessages(@Nullable ReadableArray placementIds) { + IterableLogger.d(TAG, "syncEmbeddedMessages for placements: " + placementIds); + + if (placementIds == null || placementIds.size() == 0) { + IterableApi.getInstance().getEmbeddedManager().syncMessages(); + } else { + Long[] ids = new Long[placementIds.size()]; + for (int i = 0; i < placementIds.size(); i++) { + ids[i] = (long) placementIds.getInt(i); + } + IterableApi.getInstance().getEmbeddedManager().syncMessages(ids); + } } public void startEmbeddedSession() { diff --git a/android/src/newarch/java/com/RNIterableAPIModule.java b/android/src/newarch/java/com/RNIterableAPIModule.java index 6e851fb54..ab07951ce 100644 --- a/android/src/newarch/java/com/RNIterableAPIModule.java +++ b/android/src/newarch/java/com/RNIterableAPIModule.java @@ -240,8 +240,8 @@ public void pauseAuthRetries(boolean pauseRetry) { } @Override - public void syncEmbeddedMessages() { - moduleImpl.syncEmbeddedMessages(); + public void syncEmbeddedMessages(@Nullable ReadableArray placementIds) { + moduleImpl.syncEmbeddedMessages(placementIds); } @Override diff --git a/android/src/oldarch/java/com/RNIterableAPIModule.java b/android/src/oldarch/java/com/RNIterableAPIModule.java index 9142967d5..4ee68aeb2 100644 --- a/android/src/oldarch/java/com/RNIterableAPIModule.java +++ b/android/src/oldarch/java/com/RNIterableAPIModule.java @@ -244,8 +244,8 @@ public void pauseAuthRetries(boolean pauseRetry) { } @ReactMethod - public void syncEmbeddedMessages() { - moduleImpl.syncEmbeddedMessages(); + public void syncEmbeddedMessages(@Nullable ReadableArray placementIds) { + moduleImpl.syncEmbeddedMessages(placementIds); } @ReactMethod diff --git a/ios/RNIterableAPI/RNIterableAPI.mm b/ios/RNIterableAPI/RNIterableAPI.mm index 825091661..ae7e47ba3 100644 --- a/ios/RNIterableAPI/RNIterableAPI.mm +++ b/ios/RNIterableAPI/RNIterableAPI.mm @@ -304,8 +304,8 @@ - (void)endEmbeddedSession { [_swiftAPI endEmbeddedSession]; } -- (void)syncEmbeddedMessages { - [_swiftAPI syncEmbeddedMessages]; +- (void)syncEmbeddedMessages:(NSArray *_Nullable)placementIds { + [_swiftAPI syncEmbeddedMessages:placementIds]; } - (void)getEmbeddedMessages:(NSArray *_Nullable)placementIds @@ -597,8 +597,8 @@ - (void)wakeApp { [_swiftAPI endEmbeddedSession]; } -RCT_EXPORT_METHOD(syncEmbeddedMessages) { - [_swiftAPI syncEmbeddedMessages]; +RCT_EXPORT_METHOD(syncEmbeddedMessages : (NSArray *_Nullable)placementIds) { + [_swiftAPI syncEmbeddedMessages:placementIds]; } RCT_EXPORT_METHOD(getEmbeddedMessages : (NSArray *_Nullable)placementIds resolve : (RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject) { diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index f7e19daa4..05009ad37 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -537,10 +537,15 @@ import React EmbeddedSessionManager.shared.endSession() } - @objc(syncEmbeddedMessages) - public func syncEmbeddedMessages() { + @objc(syncEmbeddedMessages:) + public func syncEmbeddedMessages(placementIds: [NSNumber]?) { ITBInfo() - IterableAPI.embeddedManager.syncMessages { } + if let placementIds = placementIds, !placementIds.isEmpty { + let ids = placementIds.map { $0.intValue } + IterableAPI.embeddedManager.syncMessages(placementIds: ids) { } + } else { + IterableAPI.embeddedManager.syncMessages { } + } } @objc(getEmbeddedMessages:resolver:rejecter:) diff --git a/src/api/NativeRNIterableAPI.ts b/src/api/NativeRNIterableAPI.ts index d3ab874b0..8ef525f2a 100644 --- a/src/api/NativeRNIterableAPI.ts +++ b/src/api/NativeRNIterableAPI.ts @@ -184,7 +184,7 @@ export interface Spec extends TurboModule { pauseAuthRetries(pauseRetry: boolean): void; // Embedded Messaging - syncEmbeddedMessages(): void; + syncEmbeddedMessages(placementIds: Array | null): void; startEmbeddedSession(): void; endEmbeddedSession(): void; startEmbeddedImpression(messageId: string, placementId: number): void; diff --git a/src/core/classes/IterableApi.test.ts b/src/core/classes/IterableApi.test.ts index 77b1b68b5..d77083712 100644 --- a/src/core/classes/IterableApi.test.ts +++ b/src/core/classes/IterableApi.test.ts @@ -1299,13 +1299,42 @@ describe('IterableApi', () => { }); describe('syncEmbeddedMessages', () => { - it('should call RNIterableAPI.syncEmbeddedMessages', () => { + it('should call RNIterableAPI.syncEmbeddedMessages with null when omitted', () => { // GIVEN no parameters // WHEN syncEmbeddedMessages is called IterableApi.syncEmbeddedMessages(); - // THEN RNIterableAPI.syncEmbeddedMessages is called - expect(MockRNIterableAPI.syncEmbeddedMessages).toBeCalled(); + // THEN RNIterableAPI.syncEmbeddedMessages is called with null (sync all) + expect(MockRNIterableAPI.syncEmbeddedMessages).toBeCalledWith(null); + }); + + it('should call RNIterableAPI.syncEmbeddedMessages with null when passed null', () => { + // WHEN syncEmbeddedMessages is called with null + IterableApi.syncEmbeddedMessages(null); + + // THEN RNIterableAPI.syncEmbeddedMessages is called with null (sync all) + expect(MockRNIterableAPI.syncEmbeddedMessages).toBeCalledWith(null); + }); + + it('should call RNIterableAPI.syncEmbeddedMessages with placement IDs', () => { + // GIVEN placement IDs + const placementIds = [1, 2]; + + // WHEN syncEmbeddedMessages is called + IterableApi.syncEmbeddedMessages(placementIds); + + // THEN RNIterableAPI.syncEmbeddedMessages is called with placement IDs + expect(MockRNIterableAPI.syncEmbeddedMessages).toBeCalledWith( + placementIds + ); + }); + + it('should call RNIterableAPI.syncEmbeddedMessages with empty array for sync all', () => { + // WHEN syncEmbeddedMessages is called with an empty array + IterableApi.syncEmbeddedMessages([]); + + // THEN the empty array is forwarded (native syncs all placements) + expect(MockRNIterableAPI.syncEmbeddedMessages).toBeCalledWith([]); }); }); diff --git a/src/core/classes/IterableApi.ts b/src/core/classes/IterableApi.ts index 9e01b6793..c0793b7d7 100644 --- a/src/core/classes/IterableApi.ts +++ b/src/core/classes/IterableApi.ts @@ -551,9 +551,11 @@ export class IterableApi { /** * Syncs embedded local cache with the server. */ - static syncEmbeddedMessages() { - IterableLogger.log('syncEmbeddedMessages'); - return RNIterableAPI.syncEmbeddedMessages(); + static syncEmbeddedMessages(placementIds?: number[] | null) { + IterableLogger.log('syncEmbeddedMessages: ', placementIds); + return RNIterableAPI.syncEmbeddedMessages( + placementIds === undefined ? null : placementIds + ); } /** diff --git a/src/embedded/classes/IterableEmbeddedManager.test.ts b/src/embedded/classes/IterableEmbeddedManager.test.ts index c460572f0..80a910237 100644 --- a/src/embedded/classes/IterableEmbeddedManager.test.ts +++ b/src/embedded/classes/IterableEmbeddedManager.test.ts @@ -97,16 +97,49 @@ describe('IterableEmbeddedManager', () => { }); describe('syncMessages', () => { - it('should call IterableApi.syncEmbeddedMessages', async () => { + it('should call IterableApi.syncEmbeddedMessages with null when omitted', async () => { // WHEN syncMessages is called const result = await embeddedManager.syncMessages(); - // THEN IterableApi.syncEmbeddedMessages is called + // THEN IterableApi.syncEmbeddedMessages is called with null expect(MockRNIterableAPI.syncEmbeddedMessages).toHaveBeenCalledTimes(1); + expect(MockRNIterableAPI.syncEmbeddedMessages).toHaveBeenCalledWith(null); // AND the result is returned expect(result).toBeUndefined(); }); + + it('should call IterableApi.syncEmbeddedMessages with placement IDs', () => { + // GIVEN placement IDs + const placementIds = [1, 2]; + + // WHEN syncMessages is called + embeddedManager.syncMessages(placementIds); + + // THEN IterableApi.syncEmbeddedMessages is called with placement IDs + expect(MockRNIterableAPI.syncEmbeddedMessages).toHaveBeenCalledTimes(1); + expect(MockRNIterableAPI.syncEmbeddedMessages).toHaveBeenCalledWith( + placementIds + ); + }); + + it('should call IterableApi.syncEmbeddedMessages with null placement IDs', () => { + // WHEN syncMessages is called with null + embeddedManager.syncMessages(null); + + // THEN IterableApi.syncEmbeddedMessages is called with null + expect(MockRNIterableAPI.syncEmbeddedMessages).toHaveBeenCalledTimes(1); + expect(MockRNIterableAPI.syncEmbeddedMessages).toHaveBeenCalledWith(null); + }); + + it('should call IterableApi.syncEmbeddedMessages with empty placement IDs', () => { + // WHEN syncMessages is called with an empty array + embeddedManager.syncMessages([]); + + // THEN the empty array is forwarded (native syncs all placements) + expect(MockRNIterableAPI.syncEmbeddedMessages).toHaveBeenCalledTimes(1); + expect(MockRNIterableAPI.syncEmbeddedMessages).toHaveBeenCalledWith([]); + }); }); describe('getMessages', () => { diff --git a/src/embedded/classes/IterableEmbeddedManager.ts b/src/embedded/classes/IterableEmbeddedManager.ts index 8d53af49c..1359f46b3 100644 --- a/src/embedded/classes/IterableEmbeddedManager.ts +++ b/src/embedded/classes/IterableEmbeddedManager.ts @@ -71,13 +71,17 @@ export class IterableEmbeddedManager { * * However, do not poll for new embedded messages at a regular interval. * + * @param placementIds - Optional placement IDs to sync. When omitted, null, or + * empty, syncs all placements. + * * @example * ```typescript * IterableEmbeddedManager.syncMessages(); + * IterableEmbeddedManager.syncMessages([1, 2, 3]); * ``` */ - syncMessages() { - return IterableApi.syncEmbeddedMessages(); + syncMessages(placementIds?: number[] | null) { + return IterableApi.syncEmbeddedMessages(placementIds); } /**