@loyalytics/swan-react-native-sdk 2.3.0 → 2.3.1-beta.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.
Files changed (63) hide show
  1. package/lib/commonjs/index.js +89 -32
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/providers/FirebasePushProvider.js +2 -2
  4. package/lib/commonjs/providers/FirebasePushProvider.js.map +1 -1
  5. package/lib/commonjs/providers/NullPushProvider.js +1 -1
  6. package/lib/commonjs/providers/NullPushProvider.js.map +1 -1
  7. package/lib/commonjs/providers/PushNotificationProvider.js.map +1 -1
  8. package/lib/commonjs/services/PushTokenService.js +2 -2
  9. package/lib/commonjs/services/PushTokenService.js.map +1 -1
  10. package/lib/commonjs/utils/FirebaseNotificationManager.js +9 -5
  11. package/lib/commonjs/utils/FirebaseNotificationManager.js.map +1 -1
  12. package/lib/commonjs/utils/NotificationSoundHelper.js +72 -0
  13. package/lib/commonjs/utils/NotificationSoundHelper.js.map +1 -0
  14. package/lib/commonjs/version.js +1 -1
  15. package/lib/commonjs/version.js.map +1 -1
  16. package/lib/module/index.js +89 -32
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/module/providers/FirebasePushProvider.js +2 -2
  19. package/lib/module/providers/FirebasePushProvider.js.map +1 -1
  20. package/lib/module/providers/NullPushProvider.js +1 -1
  21. package/lib/module/providers/NullPushProvider.js.map +1 -1
  22. package/lib/module/providers/PushNotificationProvider.js.map +1 -1
  23. package/lib/module/services/PushTokenService.js +2 -2
  24. package/lib/module/services/PushTokenService.js.map +1 -1
  25. package/lib/module/utils/FirebaseNotificationManager.js +9 -5
  26. package/lib/module/utils/FirebaseNotificationManager.js.map +1 -1
  27. package/lib/module/utils/NotificationSoundHelper.js +66 -0
  28. package/lib/module/utils/NotificationSoundHelper.js.map +1 -0
  29. package/lib/module/version.js +1 -1
  30. package/lib/module/version.js.map +1 -1
  31. package/lib/typescript/commonjs/src/index.d.ts +1 -1
  32. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/src/providers/FirebasePushProvider.d.ts +1 -1
  34. package/lib/typescript/commonjs/src/providers/FirebasePushProvider.d.ts.map +1 -1
  35. package/lib/typescript/commonjs/src/providers/NullPushProvider.d.ts +1 -1
  36. package/lib/typescript/commonjs/src/providers/NullPushProvider.d.ts.map +1 -1
  37. package/lib/typescript/commonjs/src/providers/PushNotificationProvider.d.ts +1 -1
  38. package/lib/typescript/commonjs/src/providers/PushNotificationProvider.d.ts.map +1 -1
  39. package/lib/typescript/commonjs/src/services/PushTokenService.d.ts +1 -1
  40. package/lib/typescript/commonjs/src/services/PushTokenService.d.ts.map +1 -1
  41. package/lib/typescript/commonjs/src/utils/FirebaseNotificationManager.d.ts +1 -1
  42. package/lib/typescript/commonjs/src/utils/FirebaseNotificationManager.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/src/utils/NotificationSoundHelper.d.ts +34 -0
  44. package/lib/typescript/commonjs/src/utils/NotificationSoundHelper.d.ts.map +1 -0
  45. package/lib/typescript/commonjs/src/version.d.ts +1 -1
  46. package/lib/typescript/commonjs/src/version.d.ts.map +1 -1
  47. package/lib/typescript/module/src/index.d.ts +1 -1
  48. package/lib/typescript/module/src/index.d.ts.map +1 -1
  49. package/lib/typescript/module/src/providers/FirebasePushProvider.d.ts +1 -1
  50. package/lib/typescript/module/src/providers/FirebasePushProvider.d.ts.map +1 -1
  51. package/lib/typescript/module/src/providers/NullPushProvider.d.ts +1 -1
  52. package/lib/typescript/module/src/providers/NullPushProvider.d.ts.map +1 -1
  53. package/lib/typescript/module/src/providers/PushNotificationProvider.d.ts +1 -1
  54. package/lib/typescript/module/src/providers/PushNotificationProvider.d.ts.map +1 -1
  55. package/lib/typescript/module/src/services/PushTokenService.d.ts +1 -1
  56. package/lib/typescript/module/src/services/PushTokenService.d.ts.map +1 -1
  57. package/lib/typescript/module/src/utils/FirebaseNotificationManager.d.ts +1 -1
  58. package/lib/typescript/module/src/utils/FirebaseNotificationManager.d.ts.map +1 -1
  59. package/lib/typescript/module/src/utils/NotificationSoundHelper.d.ts +34 -0
  60. package/lib/typescript/module/src/utils/NotificationSoundHelper.d.ts.map +1 -0
  61. package/lib/typescript/module/src/version.d.ts +1 -1
  62. package/lib/typescript/module/src/version.d.ts.map +1 -1
  63. package/package.json +1 -1
@@ -43,6 +43,7 @@ var _DeviceRegistrationService = require("./services/DeviceRegistrationService.j
43
43
  var _PushTokenService = require("./services/PushTokenService.js");
44
44
  var _NullPushProvider = require("./providers/NullPushProvider.js");
45
45
  var _SharedCredentialsManager = require("./utils/SharedCredentialsManager.js");
46
+ var _NotificationSoundHelper = require("./utils/NotificationSoundHelper.js");
46
47
  var _ApiUrls = _interopRequireDefault(require("./constants/ApiUrls.js"));
47
48
  var _jsxRuntime = require("react/jsx-runtime");
48
49
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -2127,6 +2128,11 @@ class SwanSDK {
2127
2128
  // Get priority from payload (for Android 7.1 and below)
2128
2129
  const priority = this.getPriorityFromPayload(remoteMessage);
2129
2130
 
2131
+ // Resolve sound from data.sound payload field
2132
+ const soundConfig = (0, _NotificationSoundHelper.resolveSoundFromPayload)(remoteMessage?.data?.sound);
2133
+ const androidSound = (0, _NotificationSoundHelper.buildAndroidSound)(soundConfig);
2134
+ const iosSound = (0, _NotificationSoundHelper.buildIosSound)(soundConfig);
2135
+
2130
2136
  // Build notification config
2131
2137
  const notificationConfig = {
2132
2138
  id: remoteMessage?.messageId || String(Date.now()),
@@ -2157,7 +2163,7 @@ class SwanSDK {
2157
2163
  foregroundPresentationOptions: {
2158
2164
  alert: true,
2159
2165
  badge: true,
2160
- sound: true
2166
+ sound: soundConfig.enabled
2161
2167
  },
2162
2168
  // Pass through category for Content Extension (e.g., carousel)
2163
2169
  ...(remoteMessage?.category && {
@@ -2166,6 +2172,14 @@ class SwanSDK {
2166
2172
  }
2167
2173
  };
2168
2174
 
2175
+ // Add sound if resolved
2176
+ if (androidSound !== undefined) {
2177
+ notificationConfig.android.sound = androidSound;
2178
+ }
2179
+ if (iosSound !== undefined) {
2180
+ notificationConfig.ios.sound = iosSound;
2181
+ }
2182
+
2169
2183
  // Add image if present
2170
2184
  if (imageUrl) {
2171
2185
  // Dynamically import AndroidStyle to avoid import errors
@@ -2184,8 +2198,7 @@ class SwanSDK {
2184
2198
  // Display notification
2185
2199
  _Logger.default.log('[SwanSDK] Displaying notification on channel:', channelId, priority !== undefined ? `with priority: ${priority}` : '(using channel importance)');
2186
2200
  _Logger.default.log('[SwanSDK] Notification config:', JSON.stringify(notificationConfig));
2187
- const notificationId = await notifee.displayNotification(notificationConfig);
2188
- _Logger.default.log('[SwanSDK] Notification displayed successfully with ID:', notificationId);
2201
+ await routeNotificationForDisplay(remoteMessage, notificationConfig, notifee);
2189
2202
  } catch (error) {
2190
2203
  _Logger.default.error('[SwanSDK] Error displaying foreground notification:', error);
2191
2204
  // Emit event so app can handle display on error
@@ -2939,18 +2952,18 @@ class SwanSDK {
2939
2952
  * @param description - User visible description
2940
2953
  * @param channelId - Custom Channel ID (defaults to App ID)
2941
2954
  */
2942
- async createNotificationChannel(channelName = 'General Notifications', importance = 4, description, channelId) {
2955
+ async createNotificationChannel(channelName = 'General Notifications', importance = 4, description, channelId, sound) {
2943
2956
  // Use provided channelId or fallback to appId
2944
2957
  const id = channelId || this.appId;
2945
2958
 
2946
2959
  // Use new PushTokenService if available
2947
2960
  if (this.pushService) {
2948
- return await this.pushService.createNotificationChannel(id, channelName, importance, description);
2961
+ return await this.pushService.createNotificationChannel(id, channelName, importance, description, sound);
2949
2962
  }
2950
2963
 
2951
2964
  // Fallback to firebaseManager for backward compatibility
2952
2965
  if (this.firebaseManager) {
2953
- return await this.firebaseManager.createNotificationChannel(id, channelName, importance, description);
2966
+ return await this.firebaseManager.createNotificationChannel(id, channelName, importance, description, sound);
2954
2967
  }
2955
2968
  _Logger.default.warn('[SwanSDK] Push notifications not initialized');
2956
2969
  return null;
@@ -3271,6 +3284,44 @@ class SwanSDK {
3271
3284
  }
3272
3285
  }
3273
3286
 
3287
+ /**
3288
+ * Routes notification display to either the native carousel module (Android)
3289
+ * or Notifee's standard display. Shared between foreground and background handlers.
3290
+ */
3291
+ exports.SwanEcomSDK = exports.default = SwanSDK;
3292
+ async function routeNotificationForDisplay(remoteMessage, notificationConfig, notifee) {
3293
+ const isAndroidCarousel = _reactNative.Platform.OS === 'android' && remoteMessage?.data?.notificationType === 'carousel';
3294
+ if (isAndroidCarousel) {
3295
+ _Logger.default.log('[SwanSDK] Routing to native carousel template');
3296
+ const {
3297
+ SwanNotificationModule
3298
+ } = _reactNative.NativeModules;
3299
+ if (SwanNotificationModule) {
3300
+ await SwanNotificationModule.displayTemplateNotification({
3301
+ notificationType: remoteMessage.data.notificationType,
3302
+ messageId: notificationConfig.id,
3303
+ title: notificationConfig.title,
3304
+ body: notificationConfig.body,
3305
+ channelId: notificationConfig.android.channelId,
3306
+ carouselMode: remoteMessage.data.carouselMode || 'manual',
3307
+ carouselVariant: remoteMessage.data.carouselVariant || 'standard',
3308
+ carouselItems: remoteMessage.data.carouselItems || '[]',
3309
+ defaultRoute: remoteMessage.data.defaultRoute || '',
3310
+ ...(remoteMessage.data.carouselInterval && {
3311
+ carouselInterval: parseInt(remoteMessage.data.carouselInterval, 10)
3312
+ })
3313
+ });
3314
+ _Logger.default.log('[SwanSDK] Native carousel displayed successfully');
3315
+ } else {
3316
+ _Logger.default.warn('[SwanSDK] SwanNotificationModule not available, falling back to Notifee');
3317
+ await notifee.displayNotification(notificationConfig);
3318
+ }
3319
+ } else {
3320
+ const notificationId = await notifee.displayNotification(notificationConfig);
3321
+ _Logger.default.log('[SwanSDK] Notification displayed successfully with ID:', notificationId);
3322
+ }
3323
+ }
3324
+
3274
3325
  /**
3275
3326
  * Foreground Message Handler for index.js
3276
3327
  *
@@ -3289,7 +3340,6 @@ class SwanSDK {
3289
3340
  * messaging().onMessage(createForegroundMessageHandler());
3290
3341
  * ```
3291
3342
  */
3292
- exports.SwanEcomSDK = exports.default = SwanSDK;
3293
3343
  function createForegroundMessageHandler() {
3294
3344
  return async remoteMessage => {
3295
3345
  _Logger.default.log('[SwanSDK] Foreground notification received:', remoteMessage?.messageId);
@@ -3351,12 +3401,9 @@ function createBackgroundMessageHandler() {
3351
3401
  // Handle silent push (no UI display)
3352
3402
  if (isSilent) {
3353
3403
  console.log('[SwanSDK] Silent push received in background, skipping notification display');
3354
- console.log('[SwanSDK] ✅ Silent push handled');
3404
+ _Logger.default.log('[SwanSDK] ✅ Silent push handled');
3355
3405
  return;
3356
3406
  }
3357
- if (sdkInstance && sdkInstance.isReady()) {
3358
- await sdkInstance.sendNotificationAck(messageId, 'delivered');
3359
- }
3360
3407
  try {
3361
3408
  // Import notifee dynamically to avoid issues if not installed
3362
3409
  let notifee;
@@ -3371,10 +3418,10 @@ function createBackgroundMessageHandler() {
3371
3418
  // Android may kill headless JS task during network requests
3372
3419
  // So we must show notification before any network calls
3373
3420
  const messageId = remoteMessage?.messageId;
3374
- console.log('[SwanSDK] Background handler - messageId:', messageId);
3421
+ _Logger.default.log('[SwanSDK] Background handler - messageId:', messageId);
3375
3422
 
3376
3423
  // Extract notification data from data payload (data-only messages)
3377
- console.log('[SwanSDK] Extracting notification data from payload...');
3424
+ _Logger.default.log('[SwanSDK] Extracting notification data from payload...');
3378
3425
  const title = remoteMessage.data?.title || 'Notification';
3379
3426
  const body = remoteMessage.data?.body || 'New message';
3380
3427
  const imageUrl = remoteMessage.data?.image || remoteMessage.data?.fcm_options?.image || '';
@@ -3398,6 +3445,11 @@ function createBackgroundMessageHandler() {
3398
3445
  };
3399
3446
  const priority = getPriority();
3400
3447
 
3448
+ // Resolve sound from data.sound payload field
3449
+ const soundConfig = (0, _NotificationSoundHelper.resolveSoundFromPayload)(remoteMessage?.data?.sound);
3450
+ const androidSound = (0, _NotificationSoundHelper.buildAndroidSound)(soundConfig);
3451
+ const iosSound = (0, _NotificationSoundHelper.buildIosSound)(soundConfig);
3452
+
3401
3453
  // Use messageId as notification ID for click tracking
3402
3454
  const notificationId = messageId || `swan_bg_${Date.now()}`;
3403
3455
  const notificationConfig = {
@@ -3420,9 +3472,18 @@ function createBackgroundMessageHandler() {
3420
3472
  pressAction: {
3421
3473
  id: 'default'
3422
3474
  }
3423
- }
3475
+ },
3476
+ ios: {}
3424
3477
  };
3425
3478
 
3479
+ // Add sound if resolved
3480
+ if (androidSound !== undefined) {
3481
+ notificationConfig.android.sound = androidSound;
3482
+ }
3483
+ if (iosSound !== undefined) {
3484
+ notificationConfig.ios.sound = iosSound;
3485
+ }
3486
+
3426
3487
  // Add iOS category for Content Extension (e.g., carousel)
3427
3488
  if (remoteMessage?.category) {
3428
3489
  notificationConfig.ios = {
@@ -3448,30 +3509,26 @@ function createBackgroundMessageHandler() {
3448
3509
  }]
3449
3510
  };
3450
3511
  }
3451
-
3452
- // STEP 1: Display notification FIRST (fast local operation)
3453
- console.log('[SwanSDK] STEP 1: Displaying notification...');
3454
- await notifee.displayNotification(notificationConfig);
3455
- console.log('[SwanSDK] ✅ Notification displayed successfully');
3512
+ await routeNotificationForDisplay(remoteMessage, notificationConfig, notifee);
3456
3513
 
3457
3514
  // STEP 2: Send delivery ACK (network operation - may be killed by OS)
3458
- console.log('[SwanSDK] STEP 2: Sending delivery ACK...');
3515
+ _Logger.default.log('[SwanSDK] STEP 2: Sending delivery ACK...');
3459
3516
  const isSDKReady = sdkInstance && sdkInstance.isReady();
3460
3517
  if (isSDKReady) {
3461
- console.log('[SwanSDK] Using SDK instance for delivery ACK');
3518
+ _Logger.default.log('[SwanSDK] Using SDK instance for delivery ACK');
3462
3519
  await sdkInstance.sendNotificationAck(messageId, 'delivered');
3463
3520
  } else if (messageId) {
3464
3521
  // SDK not fully initialized (app was killed) - send delivery ACK directly via fetch
3465
3522
  console.log('[SwanSDK] SDK not ready, sending delivery ACK via direct fetch');
3466
3523
  // Fire and forget - don't await since OS may kill us
3467
3524
  sendDirectNotificationAck(messageId, 'delivered').catch(err => {
3468
- console.log('[SwanSDK] Direct delivery ACK failed:', err);
3525
+ _Logger.default.log('[SwanSDK] Direct delivery ACK failed:', err);
3469
3526
  });
3470
3527
  }
3471
- console.log('[SwanSDK] ✅ Background handler completed');
3528
+ _Logger.default.log('[SwanSDK] ✅ Background handler completed');
3472
3529
  } catch (error) {
3473
- console.log('[SwanSDK] Error handling background message:', error?.message || error);
3474
- console.log('[SwanSDK] Error stack:', error?.stack);
3530
+ _Logger.default.error('[SwanSDK] Error handling background message:', error?.message || error);
3531
+ _Logger.default.error('[SwanSDK] Error stack:', error?.stack);
3475
3532
  }
3476
3533
  };
3477
3534
  }
@@ -3752,13 +3809,13 @@ async function sendDirectNotificationAck(messageId, event) {
3752
3809
  const Base64Module = require('react-native-base64').default;
3753
3810
 
3754
3811
  // Read credentials from AsyncStorage
3755
- console.log('[SwanSDK] Reading credentials from AsyncStorage...');
3812
+ _Logger.default.log('[SwanSDK] Reading credentials from AsyncStorage...');
3756
3813
  const encoded = await AsyncStorageModule.getItem('swanCredentials');
3757
3814
  if (!encoded) {
3758
3815
  console.log(`[SwanSDK] No credentials found, cannot send ${event} ACK`);
3759
3816
  return;
3760
3817
  }
3761
- console.log('[SwanSDK] Credentials found, decoding...');
3818
+ _Logger.default.log('[SwanSDK] Credentials found, decoding...');
3762
3819
  const credentials = JSON.parse(Base64Module.decode(encoded));
3763
3820
  const {
3764
3821
  appId,
@@ -3783,13 +3840,13 @@ async function sendDirectNotificationAck(messageId, event) {
3783
3840
  };
3784
3841
  const urlType = isProduction === 'PROD' ? 'PROD' : 'STAGE';
3785
3842
  console.log(`[SwanSDK] Sending ${event} ACK to: ${_ApiUrls.default.WEBHOOK_MOBILE_PUSH_URL[urlType]}`);
3786
- console.log('[SwanSDK] Payload:', JSON.stringify(payload));
3843
+ _Logger.default.log('[SwanSDK] Payload:', JSON.stringify(payload));
3787
3844
  const controller = new AbortController();
3788
3845
  const timeoutId = setTimeout(() => {
3789
- console.log('[SwanSDK] ACK request timeout triggered (10s)');
3846
+ _Logger.default.log('[SwanSDK] ACK request timeout triggered (10s)');
3790
3847
  controller.abort();
3791
3848
  }, 10000);
3792
- console.log('[SwanSDK] Starting fetch...');
3849
+ _Logger.default.log('[SwanSDK] Starting fetch...');
3793
3850
  const response = await fetch(_ApiUrls.default.WEBHOOK_MOBILE_PUSH_URL[urlType], {
3794
3851
  method: 'POST',
3795
3852
  headers: {
@@ -3799,7 +3856,7 @@ async function sendDirectNotificationAck(messageId, event) {
3799
3856
  body: JSON.stringify(payload),
3800
3857
  signal: controller.signal
3801
3858
  });
3802
- console.log('[SwanSDK] Fetch completed, status:', response.status);
3859
+ _Logger.default.log('[SwanSDK] Fetch completed, status:', response.status);
3803
3860
  clearTimeout(timeoutId);
3804
3861
  if (response.ok) {
3805
3862
  console.log(`[SwanSDK] ✅ ${event} ACK sent successfully via direct fetch`);
@@ -3808,7 +3865,7 @@ async function sendDirectNotificationAck(messageId, event) {
3808
3865
  console.log(`[SwanSDK] ${event} ACK failed - HTTP ${response.status}: ${responseText}`);
3809
3866
  }
3810
3867
  } catch (error) {
3811
- console.log('[SwanSDK] Caught error in sendDirectNotificationAck');
3868
+ _Logger.default.log('[SwanSDK] Caught error in sendDirectNotificationAck');
3812
3869
  if (error?.name === 'AbortError') {
3813
3870
  console.log(`[SwanSDK] ${event} ACK timed out (AbortError)`);
3814
3871
  } else {