From 44cf1a3094eaf6b78662515347733be279316842 Mon Sep 17 00:00:00 2001 From: Franco Zalamena Date: Tue, 7 Apr 2026 16:13:17 +0100 Subject: [PATCH] Expose push notification custom payload data to JavaScript Add pushPayload field to IterableActionContext and auto-populate it when URL or custom action handlers are triggered from push notifications. This allows JS handlers to access custom metadata from push campaigns via context.pushPayload. Fixes #135. Co-Authored-By: Claude Opus 4.6 --- src/core/classes/Iterable.ts | 58 ++++++++++++++++++++--- src/core/classes/IterableActionContext.ts | 22 +++++++++ 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/core/classes/Iterable.ts b/src/core/classes/Iterable.ts index 983fab49b..c4cf6831b 100644 --- a/src/core/classes/Iterable.ts +++ b/src/core/classes/Iterable.ts @@ -9,6 +9,7 @@ import { IterableInAppMessage } from '../../inApp/classes/IterableInAppMessage'; import { IterableInAppCloseSource } from '../../inApp/enums/IterableInAppCloseSource'; import { IterableInAppDeleteSource } from '../../inApp/enums/IterableInAppDeleteSource'; import { IterableInAppLocation } from '../../inApp/enums/IterableInAppLocation'; +import { IterableActionSource } from '../enums/IterableActionSource'; import { IterableAuthResponseResult } from '../enums/IterableAuthResponseResult'; import { IterableEventName } from '../enums/IterableEventName'; import type { IterableAuthFailure } from '../types/IterableAuthFailure'; @@ -967,7 +968,11 @@ export class Iterable { * * Event Handlers: * - `handleUrlCalled`: Invokes the URL handler if configured, with a delay on Android to allow the activity to wake up. + * When the action source is a push notification, the last push payload is automatically fetched and + * attached to the `IterableActionContext` as `pushPayload`, making custom push data available in the handler. * - `handleCustomActionCalled`: Invokes the custom action handler if configured. + * When the action source is a push notification, the last push payload is automatically fetched and + * attached to the `IterableActionContext` as `pushPayload`. * - `handleInAppCalled`: Invokes the in-app handler if configured and sets the in-app show response. * - `handleAuthCalled`: Invokes the authentication handler if configured and handles the promise result. * - `handleAuthSuccessCalled`: Sets the authentication response callback to success. @@ -988,13 +993,34 @@ export class Iterable { const context = IterableActionContext.fromDict(dict.context); Iterable.wakeApp(); - if (Platform.OS === 'android') { - //Give enough time for Activity to wake up. - setTimeout(() => { - callUrlHandler(Iterable.savedConfig, url, context); - }, 1000); + // When the action originates from a push notification, fetch the last + // push payload and attach it to the context so that URL handlers can + // access custom push data (e.g. promo codes, deep link metadata). + const enrichAndHandle = (ctx: IterableActionContext) => { + if (Platform.OS === 'android') { + //Give enough time for Activity to wake up. + setTimeout(() => { + callUrlHandler(Iterable.savedConfig, url, ctx); + }, 1000); + } else { + callUrlHandler(Iterable.savedConfig, url, ctx); + } + }; + + if (context.source === IterableActionSource.push) { + Iterable.getLastPushPayload() + .then((payload) => { + if (payload && typeof payload === 'object') { + context.pushPayload = payload as Record; + } + enrichAndHandle(context); + }) + .catch(() => { + // If fetching the payload fails, proceed without it + enrichAndHandle(context); + }); } else { - callUrlHandler(Iterable.savedConfig, url, context); + enrichAndHandle(context); } }); } @@ -1005,7 +1031,25 @@ export class Iterable { (dict) => { const action = IterableAction.fromDict(dict.action); const context = IterableActionContext.fromDict(dict.context); - Iterable.savedConfig.customActionHandler!(action, context); + + // When the action originates from a push notification, fetch the last + // push payload and attach it to the context so that custom action + // handlers can access custom push data. + if (context.source === IterableActionSource.push) { + Iterable.getLastPushPayload() + .then((payload) => { + if (payload && typeof payload === 'object') { + context.pushPayload = payload as Record; + } + Iterable.savedConfig.customActionHandler!(action, context); + }) + .catch(() => { + // If fetching the payload fails, proceed without it + Iterable.savedConfig.customActionHandler!(action, context); + }); + } else { + Iterable.savedConfig.customActionHandler!(action, context); + } } ); } diff --git a/src/core/classes/IterableActionContext.ts b/src/core/classes/IterableActionContext.ts index 064025d10..6e1d9ac56 100644 --- a/src/core/classes/IterableActionContext.ts +++ b/src/core/classes/IterableActionContext.ts @@ -14,6 +14,28 @@ export class IterableActionContext { */ source: IterableActionSource; + /** + * The push notification payload, if the action originated from a push notification. + * + * This field is automatically populated when the action source is + * {@link IterableActionSource.push}. It contains the custom payload data + * from the push notification that triggered the action, allowing you to + * access campaign-specific metadata in your URL or custom action handlers. + * + * @example + * ```typescript + * const config = new IterableConfig(); + * config.urlHandler = (url, context) => { + * if (context.pushPayload) { + * const promoCode = context.pushPayload.promoCode; + * // Use the custom payload data from the push notification + * } + * return true; + * }; + * ``` + */ + pushPayload?: Record; + /** * Creates an instance of IterableActionContext. */