Skip to content

Commit 420a144

Browse files
Add callback for push notification opened events with raw payload
Adds a notificationOpenedHandler to IterableConfig that fires whenever a push notification is pressed, providing the raw action and context regardless of whether the notification has a URL, custom action, or no action at all. This addresses the gap where callbacks only fired for URL/custom actions but not for all notification opens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e69c305 commit 420a144

5 files changed

Lines changed: 87 additions & 0 deletions

File tree

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,15 @@ public boolean handleIterableCustomAction(@NonNull IterableAction action, @NonNu
593593
eventDataJson.put("context", actionContextJson);
594594
WritableMap eventData = Serialization.convertJsonToMap(eventDataJson);
595595
sendEvent(EventName.handleCustomActionCalled.name(), eventData);
596+
597+
// Emit notification opened event when source is push
598+
if (actionContext.source == IterableActionContext.Source.PUSH) {
599+
JSONObject notifEventDataJson = new JSONObject();
600+
notifEventDataJson.put("action", actionJson);
601+
notifEventDataJson.put("context", actionContextJson);
602+
WritableMap notifEventData = Serialization.convertJsonToMap(notifEventDataJson);
603+
sendEvent(EventName.handleNotificationOpenedCalled.name(), notifEventData);
604+
}
596605
} catch (JSONException e) {
597606
IterableLogger.e(TAG, "Failed handling custom action");
598607
}
@@ -632,6 +641,16 @@ public boolean handleIterableURL(@NonNull Uri uri, @NonNull IterableActionContex
632641
eventDataJson.put("context", actionContextJson);
633642
WritableMap eventData = Serialization.convertJsonToMap(eventDataJson);
634643
sendEvent(EventName.handleUrlCalled.name(), eventData);
644+
645+
// Emit notification opened event when source is push
646+
if (actionContext.source == IterableActionContext.Source.PUSH) {
647+
JSONObject notifEventDataJson = new JSONObject();
648+
JSONObject actionJson = Serialization.actionToJson(actionContext.action);
649+
notifEventDataJson.put("action", actionJson);
650+
notifEventDataJson.put("context", actionContextJson);
651+
WritableMap notifEventData = Serialization.convertJsonToMap(notifEventDataJson);
652+
sendEvent(EventName.handleNotificationOpenedCalled.name(), notifEventData);
653+
}
635654
} catch (JSONException e) {
636655
IterableLogger.e(TAG, e.getLocalizedMessage());
637656
}
@@ -808,6 +827,7 @@ enum EventName {
808827
handleCustomActionCalled,
809828
handleEmbeddedMessageUpdateCalled,
810829
handleEmbeddedMessagingDisabledCalled,
830+
handleNotificationOpenedCalled,
811831
handleInAppCalled,
812832
handleUrlCalled,
813833
receivedIterableEmbeddedMessagesChanged,

ios/RNIterableAPI/ReactIterableAPI.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import React
3535
case handleAuthFailureCalled
3636
case handleEmbeddedMessageUpdateCalled
3737
case handleEmbeddedMessagingDisabledCalled
38+
case handleNotificationOpenedCalled
3839
}
3940

4041
@objc public static var supportedEvents: [String] {
@@ -710,6 +711,18 @@ extension ReactIterableAPI: IterableURLDelegate {
710711
"url": url.absoluteString,
711712
"context": contextDict,
712713
] as [String: Any])
714+
715+
// Emit notification opened event when source is push
716+
if context.source == .push {
717+
let actionDict = ReactIterableAPI.actionToDictionary(action: context.action)
718+
delegate?.sendEvent(
719+
withName: EventName.handleNotificationOpenedCalled.rawValue,
720+
body: [
721+
"action": actionDict,
722+
"context": contextDict,
723+
] as [String: Any])
724+
}
725+
713726
return true
714727
}
715728

@@ -749,6 +762,17 @@ extension ReactIterableAPI: IterableCustomActionDelegate {
749762
"action": actionDict,
750763
"context": contextDict,
751764
])
765+
766+
// Emit notification opened event when source is push
767+
if context.source == .push {
768+
delegate?.sendEvent(
769+
withName: EventName.handleNotificationOpenedCalled.rawValue,
770+
body: [
771+
"action": actionDict,
772+
"context": contextDict,
773+
])
774+
}
775+
752776
return true
753777
}
754778
}

src/core/classes/Iterable.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,9 @@ export class Iterable {
956956
RNEventEmitter.removeAllListeners(
957957
IterableEventName.handleEmbeddedMessagingDisabledCalled
958958
);
959+
RNEventEmitter.removeAllListeners(
960+
IterableEventName.handleNotificationOpenedCalled
961+
);
959962
}
960963

961964
/**
@@ -1109,6 +1112,17 @@ export class Iterable {
11091112
);
11101113
}
11111114
}
1115+
1116+
if (Iterable.savedConfig.notificationOpenedHandler) {
1117+
RNEventEmitter.addListener(
1118+
IterableEventName.handleNotificationOpenedCalled,
1119+
(dict) => {
1120+
const action = IterableAction.fromDict(dict.action);
1121+
const context = IterableActionContext.fromDict(dict.context);
1122+
Iterable.savedConfig.notificationOpenedHandler!(action, context);
1123+
}
1124+
);
1125+
}
11121126
}
11131127

11141128
/**

src/core/classes/IterableConfig.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,30 @@ export class IterableConfig {
375375
*/
376376
onEmbeddedMessagingDisabled?: () => void;
377377

378+
/**
379+
* A callback function that is called when a push notification is opened/pressed.
380+
*
381+
* This callback fires for all push notification opens, regardless of whether
382+
* the notification has a URL action, custom action, or no action at all.
383+
* It provides access to the raw action and context data from the notification.
384+
*
385+
* @param action - The action associated with the notification press.
386+
* @param context - The context in which the action was triggered, including the source.
387+
*
388+
* @example
389+
* ```typescript
390+
* const config = new IterableConfig();
391+
* config.notificationOpenedHandler = (action, context) => {
392+
* console.log('Notification opened:', action, context);
393+
* };
394+
* Iterable.initialize('<YOUR_API_KEY>', config);
395+
* ```
396+
*/
397+
notificationOpenedHandler?: (
398+
action: IterableAction,
399+
context: IterableActionContext
400+
) => void;
401+
378402
/**
379403
* Converts the IterableConfig instance to a dictionary object.
380404
*
@@ -440,6 +464,9 @@ export class IterableConfig {
440464
encryptionEnforced: this.encryptionEnforced,
441465
retryPolicy: this.retryPolicy,
442466
enableEmbeddedMessaging: this.enableEmbeddedMessaging,
467+
// eslint-disable-next-line eqeqeq
468+
notificationOpenedHandlerPresent:
469+
this.notificationOpenedHandler != undefined,
443470
};
444471
}
445472
}

src/core/enums/IterableEventName.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ export enum IterableEventName {
2323
handleEmbeddedMessageUpdateCalled = 'handleEmbeddedMessageUpdateCalled',
2424
/** Event that fires when embedded messaging is disabled */
2525
handleEmbeddedMessagingDisabledCalled = 'handleEmbeddedMessagingDisabledCalled',
26+
/** Event that fires when a push notification is opened/pressed */
27+
handleNotificationOpenedCalled = 'handleNotificationOpenedCalled',
2628
}

0 commit comments

Comments
 (0)