From 948d6857f1fcc6f6df7de2a33b327a7446b80dfa Mon Sep 17 00:00:00 2001 From: Franco Zalamena Date: Tue, 7 Apr 2026 16:08:41 +0100 Subject: [PATCH] feat: add getUnreadInboxMessagesCount and getReadInboxMessagesCount methods Expose the native SDK's existing getUnreadInboxMessagesCount via the TypeScript API, and add a computed getReadInboxMessagesCount method to enable badge count use cases for in-app inbox messages. Fixes #495 Co-Authored-By: Claude Opus 4.6 --- src/__mocks__/MockRNIterableAPI.ts | 10 ++ src/core/classes/IterableApi.test.ts | 164 ++++++++++++++++++++++ src/core/classes/IterableApi.ts | 29 ++++ src/inApp/classes/IterableInAppManager.ts | 33 +++++ 4 files changed, 236 insertions(+) diff --git a/src/__mocks__/MockRNIterableAPI.ts b/src/__mocks__/MockRNIterableAPI.ts index 708ab56ad..0c4658b4e 100644 --- a/src/__mocks__/MockRNIterableAPI.ts +++ b/src/__mocks__/MockRNIterableAPI.ts @@ -108,6 +108,16 @@ export class MockRNIterableAPI { }); } + + static async getUnreadInboxMessagesCount(): Promise { + return await new Promise((resolve) => { + const inboxMessages = + MockRNIterableAPI.messages?.filter((msg) => msg.saveToInbox) || []; + const unreadCount = inboxMessages.filter((msg) => !msg.read).length; + resolve(unreadCount); + }); + } + static setAutoDisplayPaused = jest.fn(); static showMessage = jest.fn( diff --git a/src/core/classes/IterableApi.test.ts b/src/core/classes/IterableApi.test.ts index 77a229c58..a1fd08cb9 100644 --- a/src/core/classes/IterableApi.test.ts +++ b/src/core/classes/IterableApi.test.ts @@ -922,6 +922,170 @@ describe('IterableApi', () => { }); }); + + describe('getUnreadInboxMessagesCount', () => { + it('should return the count of unread inbox messages', async () => { + // GIVEN mock messages with mixed read states + const mockMessages = [ + new IterableInAppMessage( + 'msg1', + 123, + new IterableInAppTrigger(IterableInAppTriggerType.immediate), + new Date(), + new Date(), + true, // saveToInbox + undefined, + undefined, + false, // unread + 0 + ), + new IterableInAppMessage( + 'msg2', + 456, + new IterableInAppTrigger(IterableInAppTriggerType.event), + new Date(), + new Date(), + true, // saveToInbox + undefined, + undefined, + true, // read + 0 + ), + new IterableInAppMessage( + 'msg3', + 789, + new IterableInAppTrigger(IterableInAppTriggerType.immediate), + new Date(), + new Date(), + true, // saveToInbox + undefined, + undefined, + false, // unread + 0 + ), + ]; + MockRNIterableAPI.messages = mockMessages; + + // WHEN getUnreadInboxMessagesCount is called + const result = await IterableApi.getUnreadInboxMessagesCount(); + + // THEN the unread count is returned + expect(result).toBe(2); + }); + + it('should return 0 when all inbox messages are read', async () => { + // GIVEN mock messages that are all read + const mockMessages = [ + new IterableInAppMessage( + 'msg1', + 123, + new IterableInAppTrigger(IterableInAppTriggerType.immediate), + new Date(), + new Date(), + true, // saveToInbox + undefined, + undefined, + true, // read + 0 + ), + ]; + MockRNIterableAPI.messages = mockMessages; + + // WHEN getUnreadInboxMessagesCount is called + const result = await IterableApi.getUnreadInboxMessagesCount(); + + // THEN the unread count is 0 + expect(result).toBe(0); + }); + }); + + describe('getReadInboxMessagesCount', () => { + it('should return the count of read inbox messages', async () => { + // GIVEN mock messages with mixed read states + const mockMessages = [ + new IterableInAppMessage( + 'msg1', + 123, + new IterableInAppTrigger(IterableInAppTriggerType.immediate), + new Date(), + new Date(), + true, // saveToInbox + undefined, + undefined, + false, // unread + 0 + ), + new IterableInAppMessage( + 'msg2', + 456, + new IterableInAppTrigger(IterableInAppTriggerType.event), + new Date(), + new Date(), + true, // saveToInbox + undefined, + undefined, + true, // read + 0 + ), + new IterableInAppMessage( + 'msg3', + 789, + new IterableInAppTrigger(IterableInAppTriggerType.immediate), + new Date(), + new Date(), + true, // saveToInbox + undefined, + undefined, + true, // read + 0 + ), + ]; + MockRNIterableAPI.messages = mockMessages; + + // WHEN getReadInboxMessagesCount is called + const result = await IterableApi.getReadInboxMessagesCount(); + + // THEN the read count is returned (total inbox 3 - unread 1 = 2) + expect(result).toBe(2); + }); + + it('should return 0 when no inbox messages are read', async () => { + // GIVEN mock messages that are all unread + const mockMessages = [ + new IterableInAppMessage( + 'msg1', + 123, + new IterableInAppTrigger(IterableInAppTriggerType.immediate), + new Date(), + new Date(), + true, // saveToInbox + undefined, + undefined, + false, // unread + 0 + ), + ]; + MockRNIterableAPI.messages = mockMessages; + + // WHEN getReadInboxMessagesCount is called + const result = await IterableApi.getReadInboxMessagesCount(); + + // THEN the read count is 0 + expect(result).toBe(0); + }); + + it('should return 0 when there are no inbox messages', async () => { + // GIVEN no messages + MockRNIterableAPI.messages = []; + + // WHEN getReadInboxMessagesCount is called + const result = await IterableApi.getReadInboxMessagesCount(); + + // THEN the read count is 0 + expect(result).toBe(0); + }); + }); + // ====================================================== // // ======================= MOSC ======================= // // ====================================================== // diff --git a/src/core/classes/IterableApi.ts b/src/core/classes/IterableApi.ts index 88820c9f3..a12f9ba06 100644 --- a/src/core/classes/IterableApi.ts +++ b/src/core/classes/IterableApi.ts @@ -505,6 +505,35 @@ export class IterableApi { return RNIterableAPI.updateVisibleRows(visibleRows); } + + /** + * Retrieve the count of unread inbox messages for the current user. + * + * This uses the native SDK's built-in method for an accurate count. + * + * @returns A Promise that resolves to the number of unread inbox messages. + */ + static getUnreadInboxMessagesCount(): Promise { + IterableLogger.log('getUnreadInboxMessagesCount'); + return RNIterableAPI.getUnreadInboxMessagesCount(); + } + + /** + * Retrieve the count of read inbox messages for the current user. + * + * This is computed by subtracting the unread count from the total inbox message count. + * + * @returns A Promise that resolves to the number of read inbox messages. + */ + static async getReadInboxMessagesCount(): Promise { + IterableLogger.log('getReadInboxMessagesCount'); + const [inboxMessages, unreadCount] = await Promise.all([ + IterableApi.getInboxMessages(), + IterableApi.getUnreadInboxMessagesCount(), + ]); + return inboxMessages.length - unreadCount; + } + // ---- End IN-APP ---- // // ====================================================== // diff --git a/src/inApp/classes/IterableInAppManager.ts b/src/inApp/classes/IterableInAppManager.ts index ae34f80d1..d3dfeaff4 100644 --- a/src/inApp/classes/IterableInAppManager.ts +++ b/src/inApp/classes/IterableInAppManager.ts @@ -158,6 +158,39 @@ export class IterableInAppManager { return IterableApi.getHtmlInAppContentForMessage(message.messageId); } + + /** + * Retrieve the count of unread in-app messages designated for the mobile inbox. + * + * @example + * ```typescript + * Iterable.inAppManager.getUnreadInboxMessagesCount().then(count => { + * console.log('Unread count:', count); + * }); + * ``` + * + * @returns A Promise that resolves to the number of unread inbox messages. + */ + getUnreadInboxMessagesCount(): Promise { + return IterableApi.getUnreadInboxMessagesCount(); + } + + /** + * Retrieve the count of read in-app messages designated for the mobile inbox. + * + * @example + * ```typescript + * Iterable.inAppManager.getReadInboxMessagesCount().then(count => { + * console.log('Read count:', count); + * }); + * ``` + * + * @returns A Promise that resolves to the number of read inbox messages. + */ + getReadInboxMessagesCount(): Promise { + return IterableApi.getReadInboxMessagesCount(); + } + /** * Pause or unpause the automatic display of incoming in-app messages *