@loyalytics/swan-react-native-sdk 2.5.1-beta.7 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -623,22 +623,32 @@ export default class SwanSDK {
623
623
  }
624
624
  this.listeners[event].push(callback);
625
625
 
626
- // On first NOTIFICATION_OPENED listener, check for pending cold-start notifications.
627
- // The native side holds click data until consumed we just need to ask when ready.
628
- if (event === SwanSDK.EVENTS.NOTIFICATION_OPENED && !this.coldStartCheckDone) {
629
- this.coldStartCheckDone = true;
626
+ // On every NOTIFICATION_OPENED listener registration, check for buffered notifications.
627
+ // Runs on cold start (process dead getInitialNotification buffered), warm restart
628
+ // (process alive but Activity destroyed by swipe-from-recents on Android), and
629
+ // Metro hot-reload (component unmount/remount cycles).
630
+ if (event === SwanSDK.EVENTS.NOTIFICATION_OPENED) {
630
631
  // Defer to next microtask so the listener is fully registered before events emit
631
632
  Promise.resolve().then(() => {
632
- // Deliver buffered standard/image push notification (one-shot APIs like
633
- // getInitialNotification() can't be re-read data was buffered in emitNotificationOpened).
634
- // Skip carousel notifications — checkPendingCarouselClick() handles those with per-item routes.
635
- const payload = this.pendingNotificationPayload;
636
- this.pendingNotificationPayload = null;
637
- if (payload && payload.notificationType !== 'carousel') {
638
- this.emitNotificationOpened(payload);
633
+ // Deliver buffered standard/image push notification.
634
+ // Skip carousel checkPendingCarouselClick() handles those with per-item routes.
635
+ if (this.pendingNotificationPayload) {
636
+ const payload = this.pendingNotificationPayload;
637
+ this.pendingNotificationPayload = null;
638
+ if (payload.notificationType !== 'carousel') {
639
+ this.emit(SwanSDK.EVENTS.NOTIFICATION_OPENED, payload);
640
+ this.emitDeepLinkOpened({
641
+ ...payload,
642
+ source: 'push'
643
+ });
644
+ }
639
645
  }
640
- // Check for pending carousel click (native side holds data until consumed)
646
+ // Check for pending carousel click (native side holds data until consumed).
647
+ // Unconditional: handles both cold start and warm restart carousel taps.
641
648
  this.checkPendingCarouselClick();
649
+ if (!this.coldStartCheckDone) {
650
+ this.coldStartCheckDone = true;
651
+ }
642
652
  });
643
653
  }
644
654
 
@@ -668,9 +678,11 @@ export default class SwanSDK {
668
678
  */
669
679
  emitNotificationOpened(payload) {
670
680
  const listeners = this.listeners[SwanSDK.EVENTS.NOTIFICATION_OPENED];
671
- if ((!listeners || listeners.length === 0) && !this.coldStartCheckDone) {
672
- // Cold start: buffer for delivery when first listener registers.
673
- // One-shot APIs like getInitialNotification() won't have this data later.
681
+ if (!listeners || listeners.length === 0) {
682
+ // No listeners yet — buffer for delivery when first listener registers.
683
+ // Handles: true cold start (process dead), Activity recreation (process alive
684
+ // but Activity destroyed by swipe-from-recents on Android), and Metro
685
+ // hot-reload (component unmount/remount cycles).
674
686
  Logger.log('[SwanSDK] Buffering notification payload for deferred delivery');
675
687
  this.pendingNotificationPayload = payload;
676
688
  return;