diff --git a/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java b/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java index 1dedc7fbc..9c352093f 100644 --- a/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java +++ b/android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java @@ -642,6 +642,17 @@ public void onInboxUpdated() { // --------------------------------------------------------------------------------------- // region Embedded messaging + + 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 { diff --git a/android/src/newarch/java/com/RNIterableAPIModule.java b/android/src/newarch/java/com/RNIterableAPIModule.java index 50e814f0b..4c67af4e5 100644 --- a/android/src/newarch/java/com/RNIterableAPIModule.java +++ b/android/src/newarch/java/com/RNIterableAPIModule.java @@ -224,6 +224,16 @@ public void pauseAuthRetries(boolean pauseRetry) { moduleImpl.pauseAuthRetries(pauseRetry); } + @Override + public void startEmbeddedSession() { + moduleImpl.startEmbeddedSession(); + } + + @Override + public void endEmbeddedSession() { + moduleImpl.endEmbeddedSession(); + } + @Override public void getEmbeddedPlacementIds(Promise promise) { moduleImpl.getEmbeddedPlacementIds(promise); diff --git a/android/src/oldarch/java/com/RNIterableAPIModule.java b/android/src/oldarch/java/com/RNIterableAPIModule.java index 4555fd71b..868f4051d 100644 --- a/android/src/oldarch/java/com/RNIterableAPIModule.java +++ b/android/src/oldarch/java/com/RNIterableAPIModule.java @@ -228,6 +228,16 @@ public void pauseAuthRetries(boolean pauseRetry) { moduleImpl.pauseAuthRetries(pauseRetry); } + @ReactMethod + public void startEmbeddedSession() { + moduleImpl.startEmbeddedSession(); + } + + @ReactMethod + public void endEmbeddedSession() { + moduleImpl.endEmbeddedSession(); + } + @ReactMethod public void getEmbeddedPlacementIds(Promise promise) { moduleImpl.getEmbeddedPlacementIds(promise); diff --git a/example/src/components/Embedded/Embedded.tsx b/example/src/components/Embedded/Embedded.tsx index 1fa87ae5c..cf46ed730 100644 --- a/example/src/components/Embedded/Embedded.tsx +++ b/example/src/components/Embedded/Embedded.tsx @@ -14,6 +14,20 @@ export const Embedded = () => { }); }, []); + const startEmbeddedSession = useCallback(() => { + console.log( + 'startEmbeddedSession --> check android/ios logs to check if it worked' + ); + Iterable.embeddedManager.startSession(); + }, []); + + const endEmbeddedSession = useCallback(() => { + console.log( + 'endEmbeddedSession --> check android/ios logs to check if it worked' + ); + Iterable.embeddedManager.endSession(); + }, []); + return ( EMBEDDED @@ -30,6 +44,12 @@ export const Embedded = () => { Get placement ids + + Start embedded session + + + End embedded session + ); }; diff --git a/example/src/hooks/useIterableApp.tsx b/example/src/hooks/useIterableApp.tsx index 53de3d126..2a392dd0d 100644 --- a/example/src/hooks/useIterableApp.tsx +++ b/example/src/hooks/useIterableApp.tsx @@ -123,7 +123,7 @@ export const IterableAppProvider: FunctionComponent< return jwtToken; }, [userId]); - const login = useCallback(() => { + const login = useCallback(async () => { const id = userId ?? process.env.ITBL_ID; if (!id) return Promise.reject('No User ID or Email set'); @@ -132,12 +132,18 @@ export const IterableAppProvider: FunctionComponent< const fn = getIsEmail(id) ? Iterable.setEmail : Iterable.setUserId; - fn(id); + let token; + + if (process.env.ITBL_IS_JWT_ENABLED === 'true' && process.env.ITBL_JWT_SECRET) { + token = await getJwtToken(); + } + + fn(id, token); setIsLoggedIn(true); setLoginInProgress(false); return Promise.resolve(true); - }, [userId]); + }, [getJwtToken, userId]); const initialize = useCallback( (navigation: Navigation) => { diff --git a/src/__mocks__/MockRNIterableAPI.ts b/src/__mocks__/MockRNIterableAPI.ts index c7f325677..fc7ed502f 100644 --- a/src/__mocks__/MockRNIterableAPI.ts +++ b/src/__mocks__/MockRNIterableAPI.ts @@ -129,6 +129,14 @@ export class MockRNIterableAPI { static getHtmlInAppContentForMessage = jest.fn(); + static startEmbeddedSession = jest.fn(); + + static endEmbeddedSession = jest.fn(); + + static getEmbeddedPlacementIds = jest + .fn() + .mockResolvedValue([1, 2, 3] as number[]); + // set messages function is to set the messages static property // this is for testing purposes only static setMessages(messages: IterableInAppMessage[]): void { diff --git a/src/api/NativeRNIterableAPI.ts b/src/api/NativeRNIterableAPI.ts index 9dd2af2b7..931be6f29 100644 --- a/src/api/NativeRNIterableAPI.ts +++ b/src/api/NativeRNIterableAPI.ts @@ -119,6 +119,8 @@ export interface Spec extends TurboModule { pauseAuthRetries(pauseRetry: boolean): void; // Embedded Messaging + startEmbeddedSession(): void; + endEmbeddedSession(): void; getEmbeddedPlacementIds(): Promise; // Wake app -- android only diff --git a/src/core/classes/IterableApi.ts b/src/core/classes/IterableApi.ts index 9825f42ae..84c8f3a7a 100644 --- a/src/core/classes/IterableApi.ts +++ b/src/core/classes/IterableApi.ts @@ -510,6 +510,22 @@ export class IterableApi { // ======================= EMBEDDED ===================== // // ====================================================== // + /** + * Starts an embedded session. + */ + static startEmbeddedSession() { + IterableLogger.log('startEmbeddedSession'); + return RNIterableAPI.startEmbeddedSession(); + } + + /** + * Ends an embedded session. + */ + static endEmbeddedSession() { + IterableLogger.log('endEmbeddedSession'); + return RNIterableAPI.endEmbeddedSession(); + } + /** * Get the embedded placement IDs. */ diff --git a/src/embedded/classes/IterableEmbeddedManager.test.ts b/src/embedded/classes/IterableEmbeddedManager.test.ts index 96a09ddf4..c05e38533 100644 --- a/src/embedded/classes/IterableEmbeddedManager.test.ts +++ b/src/embedded/classes/IterableEmbeddedManager.test.ts @@ -1,10 +1,18 @@ +import { MockRNIterableAPI } from '../../__mocks__/MockRNIterableAPI'; import { IterableEmbeddedManager } from './IterableEmbeddedManager'; +// Mock the RNIterableAPI module +jest.mock('../../api', () => ({ + __esModule: true, + default: MockRNIterableAPI, +})); + describe('IterableEmbeddedManager', () => { let embeddedManager: IterableEmbeddedManager; beforeEach(() => { embeddedManager = new IterableEmbeddedManager(); + jest.clearAllMocks(); }); describe('isEnabled', () => { @@ -55,5 +63,40 @@ describe('IterableEmbeddedManager', () => { expect(embeddedManager.isEnabled).toBe(false); }); }); + + describe('getPlacementIds', () => { + it('should call IterableApi.getEmbeddedPlacementIds', async () => { + // WHEN getPlacementIds is called + const result = await embeddedManager.getPlacementIds(); + + // THEN IterableApi.getEmbeddedPlacementIds is called + expect(MockRNIterableAPI.getEmbeddedPlacementIds).toHaveBeenCalledTimes( + 1 + ); + + // AND the result is returned + expect(result).toEqual([1, 2, 3]); + }); + }); + + describe('startSession', () => { + it('should call IterableApi.startEmbeddedSession', () => { + // WHEN startSession is called + embeddedManager.startSession(); + + // THEN IterableApi.startEmbeddedSession is called + expect(MockRNIterableAPI.startEmbeddedSession).toHaveBeenCalledTimes(1); + }); + }); + + describe('endSession', () => { + it('should call IterableApi.endEmbeddedSession', () => { + // WHEN endSession is called + embeddedManager.endSession(); + + // THEN IterableApi.endEmbeddedSession is called + expect(MockRNIterableAPI.endEmbeddedSession).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/src/embedded/classes/IterableEmbeddedManager.ts b/src/embedded/classes/IterableEmbeddedManager.ts index 879ed6199..d36d11885 100644 --- a/src/embedded/classes/IterableEmbeddedManager.ts +++ b/src/embedded/classes/IterableEmbeddedManager.ts @@ -14,6 +14,9 @@ import { IterableApi } from '../../core/classes/IterableApi'; export class IterableEmbeddedManager { /** * Whether the embedded manager is enabled. + * + * This is set through the `enableEmbeddedMessaging` flag in the + * `IterableConfig` class. */ private _isEnabled = false; @@ -41,4 +44,41 @@ export class IterableEmbeddedManager { getPlacementIds() { return IterableApi.getEmbeddedPlacementIds(); } + + /** + * Starts a session. + * + * As session is a period of time when a user is on a screen or page that can + * display embedded messages. + * + * When a user comes to a screen or page in your app where embedded messages + * are displayed (in one or more placements), a session should be started. + * + * @example + * ```typescript + * Iterable.embeddedManager.startSession(); + * ``` + */ + startSession() { + return IterableApi.startEmbeddedSession(); + } + + /** + * Ends a session. + * + * When a user leaves a screen in your app where embedded messages are + * displayed, the session should be ended. This causes the SDK to send + * session and impression data back to the server. + * + * A session is tracked when it is ended, so you should be able to find + * tracking data after this method is called. + * + * @example + * ```typescript + * Iterable.embeddedManager.endSession(); + * ``` + */ + endSession() { + return IterableApi.endEmbeddedSession(); + } }