expo-notifications 0.27.7 → 0.27.8

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 (30) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/notifications/NotificationsPackage.java +17 -1
  4. package/android/src/main/java/expo/modules/notifications/badge/BadgeHelper.kt +6 -1
  5. package/android/src/main/java/expo/modules/notifications/notifications/NotificationManager.java +33 -0
  6. package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java +28 -5
  7. package/android/src/main/java/expo/modules/notifications/notifications/emitting/NotificationsEmitter.kt +12 -16
  8. package/android/src/main/java/expo/modules/notifications/notifications/handling/NotificationsHandler.kt +7 -1
  9. package/android/src/main/java/expo/modules/notifications/notifications/handling/SingleNotificationHandlerTask.java +8 -4
  10. package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationListener.java +10 -0
  11. package/android/src/main/java/expo/modules/notifications/service/delegates/ExpoNotificationLifecycleListener.java +75 -0
  12. package/build/NotificationsEmitter.d.ts.map +1 -1
  13. package/build/NotificationsEmitter.js +5 -1
  14. package/build/NotificationsEmitter.js.map +1 -1
  15. package/build/useLastNotificationResponse.d.ts.map +1 -1
  16. package/build/useLastNotificationResponse.js +5 -2
  17. package/build/useLastNotificationResponse.js.map +1 -1
  18. package/build/utils/mapNotificationResponse.d.ts +21 -0
  19. package/build/utils/mapNotificationResponse.d.ts.map +1 -0
  20. package/build/utils/mapNotificationResponse.js +25 -0
  21. package/build/utils/mapNotificationResponse.js.map +1 -0
  22. package/package.json +2 -2
  23. package/plugin/build/withNotifications.d.ts +5 -0
  24. package/plugin/build/withNotificationsAndroid.d.ts +6 -2
  25. package/plugin/build/withNotificationsAndroid.js +21 -7
  26. package/plugin/src/withNotifications.ts +5 -0
  27. package/plugin/src/withNotificationsAndroid.ts +49 -6
  28. package/src/NotificationsEmitter.ts +5 -1
  29. package/src/useLastNotificationResponse.ts +5 -2
  30. package/src/utils/mapNotificationResponse.ts +27 -0
package/CHANGELOG.md CHANGED
@@ -10,6 +10,20 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 0.27.8 — 2024-06-13
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [Android] adjust logic in handling responses when app in background or killed ([#29659](https://github.com/expo/expo/pull/29659) by [@douglowder](https://github.com/douglowder)))
18
+ - [Android] Fix setBadgeCount() when badge count is 0. ([#29657](https://github.com/expo/expo/pull/29657) by [@douglowder](https://github.com/douglowder))
19
+ - [Android] Add default channel plugin prop, restore legacy icon and color. ([#29491](https://github.com/expo/expo/pull/29491) by [@douglowder](https://github.com/douglowder))
20
+ - Remove console.log line. ([#29443](https://github.com/expo/expo/pull/29443) by [@douglowder](https://github.com/douglowder))
21
+ - [Android] Remove unneeded logging. ([#29370](https://github.com/expo/expo/pull/29370) by [@douglowder](https://github.com/douglowder))
22
+ - [Android] Fix FCMv1 icons and NPE. ([#29204](https://github.com/expo/expo/pull/29204) by [@douglowder](https://github.com/douglowder))
23
+ - [Android] Correctly map response in useLastNotificationResponse hook. ([#28938](https://github.com/expo/expo/pull/28938) by [@douglowder](https://github.com/douglowder))
24
+ - [Android] fix response handling when app in background or not running. ([#28883](https://github.com/expo/expo/pull/28883) by [@douglowder](https://github.com/douglowder))
25
+ - [Android] Fix notifications events were using an incorrect event emitter. ([#28207](https://github.com/expo/expo/pull/28207) by [@lukmccall](https://github.com/lukmccall))
26
+
13
27
  ## 0.27.7 — 2024-04-15
14
28
 
15
29
  ### 💡 Others
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '0.27.7'
6
+ version = '0.27.8'
7
7
 
8
8
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
9
9
  if (expoModulesCorePlugin.exists()) {
@@ -94,7 +94,7 @@ android {
94
94
  namespace "expo.modules.notifications"
95
95
  defaultConfig {
96
96
  versionCode 21
97
- versionName '0.27.7'
97
+ versionName '0.27.8'
98
98
  }
99
99
 
100
100
  buildFeatures {
@@ -3,24 +3,34 @@ package expo.modules.notifications;
3
3
  import android.content.Context;
4
4
 
5
5
  import java.util.Arrays;
6
+ import java.util.Collections;
6
7
  import java.util.List;
7
8
 
8
9
  import expo.modules.core.BasePackage;
9
10
  import expo.modules.core.ExportedModule;
10
11
  import expo.modules.core.interfaces.InternalModule;
12
+ import expo.modules.core.interfaces.ReactActivityLifecycleListener;
11
13
  import expo.modules.core.interfaces.SingletonModule;
12
14
  import expo.modules.notifications.notifications.NotificationManager;
13
15
  import expo.modules.notifications.notifications.categories.ExpoNotificationCategoriesModule;
14
16
  import expo.modules.notifications.notifications.categories.serializers.ExpoNotificationsCategoriesSerializer;
15
17
  import expo.modules.notifications.notifications.channels.AndroidXNotificationsChannelsProvider;
18
+ import expo.modules.notifications.service.delegates.ExpoNotificationLifecycleListener;
16
19
  import expo.modules.notifications.tokens.PushTokenManager;
17
20
 
18
21
  public class NotificationsPackage extends BasePackage {
22
+
23
+ private NotificationManager mNotificationManager;
24
+
25
+ public NotificationsPackage() {
26
+ mNotificationManager = new NotificationManager();
27
+ }
28
+
19
29
  @Override
20
30
  public List<SingletonModule> createSingletonModules(Context context) {
21
31
  return Arrays.asList(
22
32
  new PushTokenManager(),
23
- new NotificationManager()
33
+ mNotificationManager
24
34
  );
25
35
  }
26
36
 
@@ -31,4 +41,10 @@ public class NotificationsPackage extends BasePackage {
31
41
  new ExpoNotificationsCategoriesSerializer()
32
42
  );
33
43
  }
44
+
45
+ @Override
46
+ public List<ReactActivityLifecycleListener> createReactActivityLifecycleListeners(Context activityContext) {
47
+ return Collections.singletonList(new ExpoNotificationLifecycleListener(activityContext, mNotificationManager));
48
+ }
34
49
  }
50
+
@@ -12,7 +12,12 @@ object BadgeHelper {
12
12
 
13
13
  fun setBadgeCount(context: Context, badgeCount: Int): Boolean {
14
14
  return try {
15
- ShortcutBadger.applyCountOrThrow(context.applicationContext, badgeCount)
15
+ if (badgeCount == 0) {
16
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager
17
+ notificationManager.cancelAll()
18
+ } else {
19
+ ShortcutBadger.applyCountOrThrow(context.applicationContext, badgeCount)
20
+ }
16
21
  BadgeHelper.badgeCount = badgeCount
17
22
  true
18
23
  } catch (e: ShortcutBadgeException) {
@@ -1,5 +1,8 @@
1
1
  package expo.modules.notifications.notifications;
2
2
 
3
+ import android.os.Bundle;
4
+ import android.util.Log;
5
+
3
6
  import expo.modules.core.interfaces.SingletonModule;
4
7
 
5
8
  import java.lang.ref.WeakReference;
@@ -22,6 +25,7 @@ public class NotificationManager implements SingletonModule, expo.modules.notifi
22
25
  */
23
26
  private WeakHashMap<NotificationListener, WeakReference<NotificationListener>> mListenerReferenceMap;
24
27
  private Collection<NotificationResponse> mPendingNotificationResponses = new ArrayList<>();
28
+ private Collection<Bundle> mPendingNotificationResponsesFromExtras = new ArrayList<>();
25
29
 
26
30
  public NotificationManager() {
27
31
  mListenerReferenceMap = new WeakHashMap<>();
@@ -53,6 +57,11 @@ public class NotificationManager implements SingletonModule, expo.modules.notifi
53
57
  listener.onNotificationResponseReceived(pendingResponse);
54
58
  }
55
59
  }
60
+ if (!mPendingNotificationResponsesFromExtras.isEmpty()) {
61
+ for (Bundle extras : mPendingNotificationResponsesFromExtras) {
62
+ listener.onNotificationResponseIntentReceived(extras);
63
+ }
64
+ }
56
65
  }
57
66
  }
58
67
 
@@ -116,4 +125,28 @@ public class NotificationManager implements SingletonModule, expo.modules.notifi
116
125
  }
117
126
  }
118
127
  }
128
+
129
+ public void onNotificationResponseFromExtras(Bundle extras) {
130
+ // We're going to be passed in extras from either
131
+ // a killed state (ExpoNotificationLifecycleListener::onCreate)
132
+ // OR a background state (ExpoNotificationLifecycleListener::onNewIntent)
133
+
134
+ // If we've just come from a background state, we'll have listeners set up
135
+ // pass on the notification to them
136
+ if (!mListenerReferenceMap.isEmpty()) {
137
+ for (WeakReference<NotificationListener> listenerReference : mListenerReferenceMap.values()) {
138
+ NotificationListener listener = listenerReference.get();
139
+ if (listener != null) {
140
+ listener.onNotificationResponseIntentReceived(extras);
141
+ }
142
+ }
143
+ } else {
144
+ // Otherwise, the app has been launched from a killed state, and our listeners
145
+ // haven't yet been setup. We'll add this to a list of pending notifications
146
+ // for them to process once they've been initialized.
147
+ if (mPendingNotificationResponsesFromExtras.isEmpty()) {
148
+ mPendingNotificationResponsesFromExtras.add(extras);
149
+ }
150
+ }
151
+ }
119
152
  }
@@ -3,9 +3,9 @@ package expo.modules.notifications.notifications;
3
3
  import android.os.Build;
4
4
  import android.os.Bundle;
5
5
 
6
- import androidx.annotation.NonNull;
7
6
  import androidx.annotation.Nullable;
8
7
 
8
+ import org.jetbrains.annotations.NotNull;
9
9
  import org.json.JSONArray;
10
10
  import org.json.JSONObject;
11
11
  import expo.modules.core.arguments.MapArguments;
@@ -19,16 +19,12 @@ import java.util.Set;
19
19
 
20
20
  import expo.modules.notifications.notifications.interfaces.NotificationTrigger;
21
21
  import expo.modules.notifications.notifications.model.Notification;
22
- import expo.modules.notifications.notifications.model.NotificationAction;
23
- import expo.modules.notifications.notifications.model.NotificationCategory;
24
22
  import expo.modules.notifications.notifications.model.NotificationContent;
25
23
  import expo.modules.notifications.notifications.model.NotificationRequest;
26
24
  import expo.modules.notifications.notifications.model.NotificationResponse;
27
- import expo.modules.notifications.notifications.model.TextInputNotificationAction;
28
25
  import expo.modules.notifications.notifications.model.TextInputNotificationResponse;
29
26
  import expo.modules.notifications.notifications.model.triggers.FirebaseNotificationTrigger;
30
27
 
31
- import expo.modules.notifications.notifications.triggers.ChannelAwareTrigger;
32
28
  import expo.modules.notifications.notifications.triggers.DailyTrigger;
33
29
  import expo.modules.notifications.notifications.triggers.DateTrigger;
34
30
  import expo.modules.notifications.notifications.triggers.TimeIntervalTrigger;
@@ -199,4 +195,31 @@ public class NotificationSerializer {
199
195
  }
200
196
  return null;
201
197
  }
198
+
199
+ @NotNull
200
+ public static Bundle toResponseBundleFromExtras(Bundle extras) {
201
+ Bundle serializedContent = new Bundle();
202
+ serializedContent.putString("title", extras.getString("title"));
203
+ serializedContent.putString("body", extras.getString("message"));
204
+ serializedContent.putString("dataString", extras.getString("body"));
205
+
206
+ Bundle serializedTrigger = new Bundle();
207
+ serializedTrigger.putString("type", "push");
208
+ serializedTrigger.putString("channelId", extras.getString("channelId"));
209
+
210
+ Bundle serializedRequest = new Bundle();
211
+ serializedRequest.putString("identifier", extras.getString("google.message_id"));
212
+ serializedRequest.putBundle("trigger", serializedTrigger);
213
+ serializedRequest.putBundle("content", serializedContent);
214
+
215
+ Bundle serializedNotification = new Bundle();
216
+ serializedNotification.putLong("date", extras.getLong("google.sent_time"));
217
+ serializedNotification.putBundle("request", serializedRequest);
218
+
219
+ Bundle serializedResponse = new Bundle();
220
+ serializedResponse.putString("actionIdentifier", "expo.modules.notifications.actions.DEFAULT");
221
+ serializedResponse.putBundle("notification", serializedNotification);
222
+
223
+ return serializedResponse;
224
+ }
202
225
  }
@@ -1,10 +1,8 @@
1
1
  package expo.modules.notifications.notifications.emitting
2
2
 
3
3
  import android.os.Bundle
4
- import expo.modules.core.interfaces.services.EventEmitter
5
4
  import expo.modules.kotlin.modules.Module
6
5
  import expo.modules.kotlin.modules.ModuleDefinition
7
- import expo.modules.notifications.ModuleNotFoundException
8
6
  import expo.modules.notifications.notifications.NotificationSerializer
9
7
  import expo.modules.notifications.notifications.interfaces.NotificationListener
10
8
  import expo.modules.notifications.notifications.interfaces.NotificationManager
@@ -17,8 +15,7 @@ private const val MESSAGES_DELETED_EVENT_NAME = "onNotificationsDeleted"
17
15
 
18
16
  open class NotificationsEmitter : Module(), NotificationListener {
19
17
  private lateinit var notificationManager: NotificationManager
20
- private var lastNotificationResponse: NotificationResponse? = null
21
- private var eventEmitter: EventEmitter? = null
18
+ private var lastNotificationResponseBundle: Bundle? = null
22
19
 
23
20
  override fun definition() = ModuleDefinition {
24
21
  Name("ExpoNotificationsEmitter")
@@ -30,9 +27,6 @@ open class NotificationsEmitter : Module(), NotificationListener {
30
27
  )
31
28
 
32
29
  OnCreate {
33
- eventEmitter = appContext.legacyModule<EventEmitter>()
34
- ?: throw ModuleNotFoundException(EventEmitter::class)
35
-
36
30
  // Register the module as a listener in NotificationManager singleton module.
37
31
  // Deregistration happens in onDestroy callback.
38
32
  notificationManager = requireNotNull(appContext.legacyModuleRegistry.getSingletonModule("NotificationManager", NotificationManager::class.java))
@@ -44,7 +38,7 @@ open class NotificationsEmitter : Module(), NotificationListener {
44
38
  }
45
39
 
46
40
  AsyncFunction("getLastNotificationResponseAsync") {
47
- lastNotificationResponse?.let(NotificationSerializer::toBundle)
41
+ lastNotificationResponseBundle
48
42
  }
49
43
  }
50
44
 
@@ -55,7 +49,7 @@ open class NotificationsEmitter : Module(), NotificationListener {
55
49
  * @param notification Notification received
56
50
  */
57
51
  override fun onNotificationReceived(notification: Notification) {
58
- eventEmitter?.emit(NEW_MESSAGE_EVENT_NAME, NotificationSerializer.toBundle(notification))
52
+ sendEvent(NEW_MESSAGE_EVENT_NAME, NotificationSerializer.toBundle(notification))
59
53
  }
60
54
 
61
55
  /**
@@ -66,12 +60,14 @@ open class NotificationsEmitter : Module(), NotificationListener {
66
60
  * @return Whether notification has been handled
67
61
  */
68
62
  override fun onNotificationResponseReceived(response: NotificationResponse): Boolean {
69
- lastNotificationResponse = response
70
- eventEmitter?.let {
71
- it.emit(NEW_RESPONSE_EVENT_NAME, NotificationSerializer.toBundle(response))
72
- return true
73
- }
74
- return false
63
+ lastNotificationResponseBundle = NotificationSerializer.toBundle(response)
64
+ sendEvent(NEW_RESPONSE_EVENT_NAME, lastNotificationResponseBundle)
65
+ return true
66
+ }
67
+
68
+ override fun onNotificationResponseIntentReceived(extras: Bundle?) {
69
+ lastNotificationResponseBundle = NotificationSerializer.toResponseBundleFromExtras(extras)
70
+ sendEvent(NEW_RESPONSE_EVENT_NAME, lastNotificationResponseBundle)
75
71
  }
76
72
 
77
73
  /**
@@ -79,6 +75,6 @@ open class NotificationsEmitter : Module(), NotificationListener {
79
75
  * Emits a [MESSAGES_DELETED_EVENT_NAME] event.
80
76
  */
81
77
  override fun onNotificationsDropped() {
82
- eventEmitter?.emit(MESSAGES_DELETED_EVENT_NAME, Bundle.EMPTY)
78
+ sendEvent(MESSAGES_DELETED_EVENT_NAME, Bundle.EMPTY)
83
79
  }
84
80
  }
@@ -115,7 +115,13 @@ open class NotificationsHandler : Module(), NotificationListener {
115
115
  */
116
116
  override fun onNotificationReceived(notification: Notification) {
117
117
  val context = appContext.reactContext ?: return
118
- val task = SingleNotificationHandlerTask(context, handler, moduleRegistry, notification, this)
118
+ val task = SingleNotificationHandlerTask(
119
+ context,
120
+ appContext.eventEmitter(this),
121
+ handler,
122
+ notification,
123
+ this
124
+ )
119
125
  tasksMap[task.identifier] = task
120
126
  task.start()
121
127
  }
@@ -5,10 +5,8 @@ import android.os.Bundle;
5
5
  import android.os.Handler;
6
6
  import android.os.ResultReceiver;
7
7
 
8
- import expo.modules.core.ModuleRegistry;
9
8
  import expo.modules.core.Promise;
10
9
  import expo.modules.core.interfaces.services.EventEmitter;
11
-
12
10
  import expo.modules.notifications.notifications.NotificationSerializer;
13
11
  import expo.modules.notifications.notifications.model.Notification;
14
12
  import expo.modules.notifications.notifications.model.NotificationBehavior;
@@ -42,10 +40,16 @@ public class SingleNotificationHandlerTask {
42
40
 
43
41
  private Runnable mTimeoutRunnable = SingleNotificationHandlerTask.this::handleTimeout;
44
42
 
45
- /* package */ SingleNotificationHandlerTask(Context context, Handler handler, ModuleRegistry moduleRegistry, Notification notification, NotificationsHandler delegate) {
43
+ /* package */ SingleNotificationHandlerTask(
44
+ Context context,
45
+ EventEmitter eventEmitter,
46
+ Handler handler,
47
+ Notification notification,
48
+ NotificationsHandler delegate
49
+ ) {
46
50
  mContext = context;
47
51
  mHandler = handler;
48
- mEventEmitter = moduleRegistry.getModule(EventEmitter.class);
52
+ mEventEmitter = eventEmitter;
49
53
  mNotification = notification;
50
54
  mDelegate = delegate;
51
55
  }
@@ -1,5 +1,7 @@
1
1
  package expo.modules.notifications.notifications.interfaces;
2
2
 
3
+ import android.os.Bundle;
4
+
3
5
  import com.google.firebase.messaging.FirebaseMessagingService;
4
6
 
5
7
  import expo.modules.notifications.notifications.model.Notification;
@@ -28,6 +30,14 @@ public interface NotificationListener {
28
30
  return false;
29
31
  }
30
32
 
33
+ /**
34
+ * Callback called when notification response is received through package lifecycle listeners
35
+ *
36
+ * @param extras Bundle of extras from the lifecycle method
37
+ */
38
+ default void onNotificationResponseIntentReceived(Bundle extras) {
39
+ }
40
+
31
41
  /**
32
42
  * Callback called when some notifications are dropped.
33
43
  * See {@link FirebaseMessagingService#onDeletedMessages()}
@@ -0,0 +1,75 @@
1
+ package expo.modules.notifications.service.delegates;
2
+
3
+ import android.app.Activity;
4
+ import android.app.NotificationChannel;
5
+ import android.app.PendingIntent;
6
+ import android.content.Context;
7
+ import android.content.Intent;
8
+ import android.os.Bundle;
9
+ import android.os.Parcel;
10
+ import android.util.Log;
11
+
12
+ import androidx.core.app.NotificationCompat;
13
+
14
+ import java.util.Objects;
15
+
16
+ import expo.modules.core.interfaces.ReactActivityLifecycleListener;
17
+ import expo.modules.notifications.notifications.NotificationManager;
18
+ import expo.modules.notifications.notifications.model.Notification;
19
+ import expo.modules.notifications.notifications.model.NotificationResponse;
20
+
21
+ public class ExpoNotificationLifecycleListener implements ReactActivityLifecycleListener {
22
+
23
+ private NotificationManager mNotificationManager;
24
+
25
+ public ExpoNotificationLifecycleListener(Context context, NotificationManager notificationManager) {
26
+ mNotificationManager = notificationManager;
27
+ }
28
+
29
+ /**
30
+ * This will be triggered if the app is not running,
31
+ * and is started from clicking on a notification.
32
+ * <p>
33
+ * Notification data will be in activity.intent.extras
34
+ *
35
+ * @param activity
36
+ * @param savedInstanceState
37
+ */
38
+ @Override
39
+ public void onCreate(Activity activity, Bundle savedInstanceState) {
40
+ Intent intent = activity.getIntent();
41
+ if (intent != null) {
42
+ Bundle extras = intent.getExtras();
43
+ if (extras != null) {
44
+ if (extras.containsKey("notificationResponse")) {
45
+ Log.d("ReactNativeJS", "[native] ExpoNotificationLifecycleListener contains an unmarshaled notification response. Skipping.");
46
+ return;
47
+ }
48
+ mNotificationManager.onNotificationResponseFromExtras(extras);
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * This will be triggered if the app is running and in the background,
55
+ * and the user clicks on a notification to open the app.
56
+ * <p>
57
+ * Notification data will be in intent.extras
58
+ *
59
+ * @param intent
60
+ * @return
61
+ */
62
+ @Override
63
+ public boolean onNewIntent(Intent intent) {
64
+ Bundle extras = intent.getExtras();
65
+ if (extras != null) {
66
+ if (extras.containsKey("notificationResponse")) {
67
+ Log.d("ReactNativeJS", "[native] ExpoNotificationLifecycleListener contains an unmarshaled notification response. Skipping.");
68
+ intent.removeExtra("notificationResponse");
69
+ return ReactActivityLifecycleListener.super.onNewIntent(intent);
70
+ }
71
+ mNotificationManager.onNotificationResponseFromExtras(extras);
72
+ }
73
+ return ReactActivityLifecycleListener.super.onNewIntent(intent);
74
+ }
75
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationsEmitter.d.ts","sourceRoot":"","sources":["../src/NotificationsEmitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEpF,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAW3E,eAAO,MAAM,yBAAyB,+CAA+C,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GACtC,YAAY,CAEd;AAED;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,YAAY,CAElF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAC9C,YAAY,CAKd;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,YAAY,QAExE;AAGD;;GAEG;AACH,wBAAsB,gCAAgC,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAK7F"}
1
+ {"version":3,"file":"NotificationsEmitter.d.ts","sourceRoot":"","sources":["../src/NotificationsEmitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEpF,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAY3E,eAAO,MAAM,yBAAyB,+CAA+C,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GACtC,YAAY,CAEd;AAED;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,YAAY,CAElF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAC9C,YAAY,CAQd;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,YAAY,QAExE;AAGD;;GAEG;AACH,wBAAsB,gCAAgC,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAK7F"}
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter, UnavailabilityError } from 'expo-modules-core';
2
2
  import NotificationsEmitterModule from './NotificationsEmitterModule';
3
+ import { mapNotificationResponse } from './utils/mapNotificationResponse';
3
4
  // Web uses SyntheticEventEmitter
4
5
  const emitter = new EventEmitter(NotificationsEmitterModule);
5
6
  const didReceiveNotificationEventName = 'onDidReceiveNotification';
@@ -72,7 +73,10 @@ export function addNotificationsDroppedListener(listener) {
72
73
  * @header listen
73
74
  */
74
75
  export function addNotificationResponseReceivedListener(listener) {
75
- return emitter.addListener(didReceiveNotificationResponseEventName, listener);
76
+ return emitter.addListener(didReceiveNotificationResponseEventName, (response) => {
77
+ const mappedResponse = mapNotificationResponse(response);
78
+ listener(mappedResponse);
79
+ });
76
80
  }
77
81
  /**
78
82
  * Removes a notification subscription returned by an `addNotificationListener` call.
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationsEmitter.js","sourceRoot":"","sources":["../src/NotificationsEmitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGpF,OAAO,0BAA0B,MAAM,8BAA8B,CAAC;AAEtE,iCAAiC;AACjC,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,0BAA0B,CAAC,CAAC;AAE7D,MAAM,+BAA+B,GAAG,0BAA0B,CAAC;AACnE,MAAM,6BAA6B,GAAG,wBAAwB,CAAC;AAC/D,MAAM,uCAAuC,GAAG,kCAAkC,CAAC;AAEnF,eAAe;AACf,MAAM,CAAC,MAAM,yBAAyB,GAAG,4CAA4C,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,+BAA+B,CAC7C,QAAuC;IAEvC,OAAO,OAAO,CAAC,WAAW,CAAe,+BAA+B,EAAE,QAAQ,CAAC,CAAC;AACtF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,+BAA+B,CAAC,QAAoB;IAClE,OAAO,OAAO,CAAC,WAAW,CAAO,6BAA6B,EAAE,QAAQ,CAAC,CAAC;AAC5E,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,uCAAuC,CACrD,QAA+C;IAE/C,OAAO,OAAO,CAAC,WAAW,CACxB,uCAAuC,EACvC,QAAQ,CACT,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAAC,YAA0B;IACvE,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;AAC3C,CAAC;AAED,eAAe;AACf;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC;IACpD,IAAI,CAAC,0BAA0B,CAAC,gCAAgC,EAAE;QAChE,MAAM,IAAI,mBAAmB,CAAC,mBAAmB,EAAE,kCAAkC,CAAC,CAAC;KACxF;IACD,OAAO,MAAM,0BAA0B,CAAC,gCAAgC,EAAE,CAAC;AAC7E,CAAC","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError } from 'expo-modules-core';\n\nimport { Notification, NotificationResponse } from './Notifications.types';\nimport NotificationsEmitterModule from './NotificationsEmitterModule';\n\n// Web uses SyntheticEventEmitter\nconst emitter = new EventEmitter(NotificationsEmitterModule);\n\nconst didReceiveNotificationEventName = 'onDidReceiveNotification';\nconst didDropNotificationsEventName = 'onNotificationsDeleted';\nconst didReceiveNotificationResponseEventName = 'onDidReceiveNotificationResponse';\n\n// @docsMissing\nexport const DEFAULT_ACTION_IDENTIFIER = 'expo.modules.notifications.actions.DEFAULT';\n\n/**\n * Listeners registered by this method will be called whenever a notification is received while the app is running.\n * @param listener A function accepting a notification ([`Notification`](#notification)) as an argument.\n * @return A [`Subscription`](#subscription) object represents the subscription of the provided listener.\n * @example Registering a notification listener using a React hook:\n * ```jsx\n * import React from 'react';\n * import * as Notifications from 'expo-notifications';\n *\n * export default function App() {\n * React.useEffect(() => {\n * const subscription = Notifications.addNotificationReceivedListener(notification => {\n * console.log(notification);\n * });\n * return () => subscription.remove();\n * }, []);\n *\n * return (\n * // Your app content\n * );\n * }\n * ```\n * @header listen\n */\nexport function addNotificationReceivedListener(\n listener: (event: Notification) => void\n): Subscription {\n return emitter.addListener<Notification>(didReceiveNotificationEventName, listener);\n}\n\n/**\n * Listeners registered by this method will be called whenever some notifications have been dropped by the server.\n * Applicable only to Firebase Cloud Messaging which we use as a notifications service on Android. It corresponds to `onDeletedMessages()` callback.\n * More information can be found in [Firebase docs](https://firebase.google.com/docs/cloud-messaging/android/receive#override-ondeletedmessages).\n * @param listener A callback function.\n * @return A [`Subscription`](#subscription) object represents the subscription of the provided listener.\n * @header listen\n */\nexport function addNotificationsDroppedListener(listener: () => void): Subscription {\n return emitter.addListener<void>(didDropNotificationsEventName, listener);\n}\n\n/**\n * Listeners registered by this method will be called whenever a user interacts with a notification (for example, taps on it).\n * @param listener A function accepting notification response ([`NotificationResponse`](#notificationresponse)) as an argument.\n * @return A [`Subscription`](#subscription) object represents the subscription of the provided listener.\n * @example Register a notification responder listener:\n * ```jsx\n * import React from 'react';\n * import { Linking } from 'react-native';\n * import * as Notifications from 'expo-notifications';\n *\n * export default function Container() {\n * React.useEffect(() => {\n * const subscription = Notifications.addNotificationResponseReceivedListener(response => {\n * const url = response.notification.request.content.data.url;\n * Linking.openURL(url);\n * });\n * return () => subscription.remove();\n * }, []);\n *\n * return (\n * // Your app content\n * );\n * }\n * ```\n * @header listen\n */\nexport function addNotificationResponseReceivedListener(\n listener: (event: NotificationResponse) => void\n): Subscription {\n return emitter.addListener<NotificationResponse>(\n didReceiveNotificationResponseEventName,\n listener\n );\n}\n\n/**\n * Removes a notification subscription returned by an `addNotificationListener` call.\n * @param subscription A subscription returned by `addNotificationListener` method.\n * @header listen\n */\nexport function removeNotificationSubscription(subscription: Subscription) {\n emitter.removeSubscription(subscription);\n}\n\n// @docsMissing\n/**\n * @header listen\n */\nexport async function getLastNotificationResponseAsync(): Promise<NotificationResponse | null> {\n if (!NotificationsEmitterModule.getLastNotificationResponseAsync) {\n throw new UnavailabilityError('ExpoNotifications', 'getLastNotificationResponseAsync');\n }\n return await NotificationsEmitterModule.getLastNotificationResponseAsync();\n}\n"]}
1
+ {"version":3,"file":"NotificationsEmitter.js","sourceRoot":"","sources":["../src/NotificationsEmitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGpF,OAAO,0BAA0B,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,iCAAiC;AACjC,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,0BAA0B,CAAC,CAAC;AAE7D,MAAM,+BAA+B,GAAG,0BAA0B,CAAC;AACnE,MAAM,6BAA6B,GAAG,wBAAwB,CAAC;AAC/D,MAAM,uCAAuC,GAAG,kCAAkC,CAAC;AAEnF,eAAe;AACf,MAAM,CAAC,MAAM,yBAAyB,GAAG,4CAA4C,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,+BAA+B,CAC7C,QAAuC;IAEvC,OAAO,OAAO,CAAC,WAAW,CAAe,+BAA+B,EAAE,QAAQ,CAAC,CAAC;AACtF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,+BAA+B,CAAC,QAAoB;IAClE,OAAO,OAAO,CAAC,WAAW,CAAO,6BAA6B,EAAE,QAAQ,CAAC,CAAC;AAC5E,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,uCAAuC,CACrD,QAA+C;IAE/C,OAAO,OAAO,CAAC,WAAW,CACxB,uCAAuC,EACvC,CAAC,QAA8B,EAAE,EAAE;QACjC,MAAM,cAAc,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QACzD,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC3B,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAAC,YAA0B;IACvE,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;AAC3C,CAAC;AAED,eAAe;AACf;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC;IACpD,IAAI,CAAC,0BAA0B,CAAC,gCAAgC,EAAE;QAChE,MAAM,IAAI,mBAAmB,CAAC,mBAAmB,EAAE,kCAAkC,CAAC,CAAC;KACxF;IACD,OAAO,MAAM,0BAA0B,CAAC,gCAAgC,EAAE,CAAC;AAC7E,CAAC","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError } from 'expo-modules-core';\n\nimport { Notification, NotificationResponse } from './Notifications.types';\nimport NotificationsEmitterModule from './NotificationsEmitterModule';\nimport { mapNotificationResponse } from './utils/mapNotificationResponse';\n\n// Web uses SyntheticEventEmitter\nconst emitter = new EventEmitter(NotificationsEmitterModule);\n\nconst didReceiveNotificationEventName = 'onDidReceiveNotification';\nconst didDropNotificationsEventName = 'onNotificationsDeleted';\nconst didReceiveNotificationResponseEventName = 'onDidReceiveNotificationResponse';\n\n// @docsMissing\nexport const DEFAULT_ACTION_IDENTIFIER = 'expo.modules.notifications.actions.DEFAULT';\n\n/**\n * Listeners registered by this method will be called whenever a notification is received while the app is running.\n * @param listener A function accepting a notification ([`Notification`](#notification)) as an argument.\n * @return A [`Subscription`](#subscription) object represents the subscription of the provided listener.\n * @example Registering a notification listener using a React hook:\n * ```jsx\n * import React from 'react';\n * import * as Notifications from 'expo-notifications';\n *\n * export default function App() {\n * React.useEffect(() => {\n * const subscription = Notifications.addNotificationReceivedListener(notification => {\n * console.log(notification);\n * });\n * return () => subscription.remove();\n * }, []);\n *\n * return (\n * // Your app content\n * );\n * }\n * ```\n * @header listen\n */\nexport function addNotificationReceivedListener(\n listener: (event: Notification) => void\n): Subscription {\n return emitter.addListener<Notification>(didReceiveNotificationEventName, listener);\n}\n\n/**\n * Listeners registered by this method will be called whenever some notifications have been dropped by the server.\n * Applicable only to Firebase Cloud Messaging which we use as a notifications service on Android. It corresponds to `onDeletedMessages()` callback.\n * More information can be found in [Firebase docs](https://firebase.google.com/docs/cloud-messaging/android/receive#override-ondeletedmessages).\n * @param listener A callback function.\n * @return A [`Subscription`](#subscription) object represents the subscription of the provided listener.\n * @header listen\n */\nexport function addNotificationsDroppedListener(listener: () => void): Subscription {\n return emitter.addListener<void>(didDropNotificationsEventName, listener);\n}\n\n/**\n * Listeners registered by this method will be called whenever a user interacts with a notification (for example, taps on it).\n * @param listener A function accepting notification response ([`NotificationResponse`](#notificationresponse)) as an argument.\n * @return A [`Subscription`](#subscription) object represents the subscription of the provided listener.\n * @example Register a notification responder listener:\n * ```jsx\n * import React from 'react';\n * import { Linking } from 'react-native';\n * import * as Notifications from 'expo-notifications';\n *\n * export default function Container() {\n * React.useEffect(() => {\n * const subscription = Notifications.addNotificationResponseReceivedListener(response => {\n * const url = response.notification.request.content.data.url;\n * Linking.openURL(url);\n * });\n * return () => subscription.remove();\n * }, []);\n *\n * return (\n * // Your app content\n * );\n * }\n * ```\n * @header listen\n */\nexport function addNotificationResponseReceivedListener(\n listener: (event: NotificationResponse) => void\n): Subscription {\n return emitter.addListener<NotificationResponse>(\n didReceiveNotificationResponseEventName,\n (response: NotificationResponse) => {\n const mappedResponse = mapNotificationResponse(response);\n listener(mappedResponse);\n }\n );\n}\n\n/**\n * Removes a notification subscription returned by an `addNotificationListener` call.\n * @param subscription A subscription returned by `addNotificationListener` method.\n * @header listen\n */\nexport function removeNotificationSubscription(subscription: Subscription) {\n emitter.removeSubscription(subscription);\n}\n\n// @docsMissing\n/**\n * @header listen\n */\nexport async function getLastNotificationResponseAsync(): Promise<NotificationResponse | null> {\n if (!NotificationsEmitterModule.getLastNotificationResponseAsync) {\n throw new UnavailabilityError('ExpoNotifications', 'getLastNotificationResponseAsync');\n }\n return await NotificationsEmitterModule.getLastNotificationResponseAsync();\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"useLastNotificationResponse.d.ts","sourceRoot":"","sources":["../src/useLastNotificationResponse.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAI7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,OAAO,UAAU,2BAA2B,4CA4BlD"}
1
+ {"version":3,"file":"useLastNotificationResponse.d.ts","sourceRoot":"","sources":["../src/useLastNotificationResponse.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAK7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,OAAO,UAAU,2BAA2B,4CA8BlD"}
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useLayoutEffect, useState } from 'react';
2
2
  import { addNotificationResponseReceivedListener } from './NotificationsEmitter';
3
3
  import NotificationsEmitterModule from './NotificationsEmitterModule';
4
+ import { mapNotificationResponse } from './utils/mapNotificationResponse';
4
5
  /**
5
6
  * A React hook always returns the notification response that was received most recently
6
7
  * (a notification response designates an interaction with a notification, such as tapping on it).
@@ -41,7 +42,8 @@ export default function useLastNotificationResponse() {
41
42
  // useLayoutEffect ensures the listener is registered as soon as possible
42
43
  useLayoutEffect(() => {
43
44
  const subscription = addNotificationResponseReceivedListener((response) => {
44
- setLastNotificationResponse(response);
45
+ const mappedResponse = mapNotificationResponse(response);
46
+ setLastNotificationResponse(mappedResponse);
45
47
  });
46
48
  return () => {
47
49
  subscription.remove();
@@ -55,7 +57,8 @@ export default function useLastNotificationResponse() {
55
57
  // We only update the state with the resolved value if it's empty,
56
58
  // because if it's not empty it must have been populated by the `useLayoutEffect`
57
59
  // listener which returns "live" values.
58
- setLastNotificationResponse((currentResponse) => currentResponse ?? response);
60
+ const mappedResponse = response ? mapNotificationResponse(response) : response;
61
+ setLastNotificationResponse((currentResponse) => currentResponse ?? mappedResponse);
59
62
  });
60
63
  }, []);
61
64
  return lastNotificationResponse;
@@ -1 +1 @@
1
- {"version":3,"file":"useLastNotificationResponse.js","sourceRoot":"","sources":["../src/useLastNotificationResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAG7D,OAAO,EAAE,uCAAuC,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,0BAA0B,MAAM,8BAA8B,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,OAAO,UAAU,2BAA2B;IACjD,MAAM,CAAC,wBAAwB,EAAE,2BAA2B,CAAC,GAAG,QAAQ,CAEtE,SAAS,CAAC,CAAC;IAEb,yEAAyE;IACzE,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,YAAY,GAAG,uCAAuC,CAAC,CAAC,QAAQ,EAAE,EAAE;YACxE,2BAA2B,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,iEAAiE;IACjE,8DAA8D;IAC9D,+CAA+C;IAC/C,SAAS,CAAC,GAAG,EAAE;QACb,0BAA0B,CAAC,gCAAgC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;YAChF,kEAAkE;YAClE,iFAAiF;YACjF,wCAAwC;YACxC,2BAA2B,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC,eAAe,IAAI,QAAQ,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,wBAAwB,CAAC;AAClC,CAAC","sourcesContent":["import { useEffect, useLayoutEffect, useState } from 'react';\n\nimport { NotificationResponse } from './Notifications.types';\nimport { addNotificationResponseReceivedListener } from './NotificationsEmitter';\nimport NotificationsEmitterModule from './NotificationsEmitterModule';\n\n/**\n * A React hook always returns the notification response that was received most recently\n * (a notification response designates an interaction with a notification, such as tapping on it).\n *\n * > If you don't want to use a hook, you can use `Notifications.getLastNotificationResponseAsync()` instead.\n *\n * @return The hook may return one of these three types/values:\n * - `undefined` - until we're sure of what to return,\n * - `null` - if no notification response has been received yet,\n * - a [`NotificationResponse`](#notificationresponse) object - if a notification response was received.\n *\n * @example Responding to a notification tap by opening a URL that could be put into the notification's `data`\n * (opening the URL is your responsibility and is not a part of the `expo-notifications` API):\n * ```jsx\n * import * as Notifications from 'expo-notifications';\n * import { Linking } from 'react-native';\n *\n * export default function App() {\n * const lastNotificationResponse = Notifications.useLastNotificationResponse();\n * React.useEffect(() => {\n * if (\n * lastNotificationResponse &&\n * lastNotificationResponse.notification.request.content.data.url &&\n * lastNotificationResponse.actionIdentifier === Notifications.DEFAULT_ACTION_IDENTIFIER\n * ) {\n * Linking.openURL(lastNotificationResponse.notification.request.content.data.url);\n * }\n * }, [lastNotificationResponse]);\n * return (\n * // Your app content\n * );\n * }\n * ```\n * @header listen\n */\nexport default function useLastNotificationResponse() {\n const [lastNotificationResponse, setLastNotificationResponse] = useState<\n NotificationResponse | null | undefined\n >(undefined);\n\n // useLayoutEffect ensures the listener is registered as soon as possible\n useLayoutEffect(() => {\n const subscription = addNotificationResponseReceivedListener((response) => {\n setLastNotificationResponse(response);\n });\n return () => {\n subscription.remove();\n };\n }, []);\n\n // On each mount of this hook we fetch last notification response\n // from the native module which is an \"always active listener\"\n // and always returns the most recent response.\n useEffect(() => {\n NotificationsEmitterModule.getLastNotificationResponseAsync?.().then((response) => {\n // We only update the state with the resolved value if it's empty,\n // because if it's not empty it must have been populated by the `useLayoutEffect`\n // listener which returns \"live\" values.\n setLastNotificationResponse((currentResponse) => currentResponse ?? response);\n });\n }, []);\n\n return lastNotificationResponse;\n}\n"]}
1
+ {"version":3,"file":"useLastNotificationResponse.js","sourceRoot":"","sources":["../src/useLastNotificationResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAG7D,OAAO,EAAE,uCAAuC,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,0BAA0B,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,OAAO,UAAU,2BAA2B;IACjD,MAAM,CAAC,wBAAwB,EAAE,2BAA2B,CAAC,GAAG,QAAQ,CAEtE,SAAS,CAAC,CAAC;IAEb,yEAAyE;IACzE,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,YAAY,GAAG,uCAAuC,CAAC,CAAC,QAAQ,EAAE,EAAE;YACxE,MAAM,cAAc,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;YACzD,2BAA2B,CAAC,cAAc,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,iEAAiE;IACjE,8DAA8D;IAC9D,+CAA+C;IAC/C,SAAS,CAAC,GAAG,EAAE;QACb,0BAA0B,CAAC,gCAAgC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;YAChF,kEAAkE;YAClE,iFAAiF;YACjF,wCAAwC;YACxC,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC/E,2BAA2B,CAAC,CAAC,eAAe,EAAE,EAAE,CAAC,eAAe,IAAI,cAAc,CAAC,CAAC;QACtF,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,wBAAwB,CAAC;AAClC,CAAC","sourcesContent":["import { useEffect, useLayoutEffect, useState } from 'react';\n\nimport { NotificationResponse } from './Notifications.types';\nimport { addNotificationResponseReceivedListener } from './NotificationsEmitter';\nimport NotificationsEmitterModule from './NotificationsEmitterModule';\nimport { mapNotificationResponse } from './utils/mapNotificationResponse';\n\n/**\n * A React hook always returns the notification response that was received most recently\n * (a notification response designates an interaction with a notification, such as tapping on it).\n *\n * > If you don't want to use a hook, you can use `Notifications.getLastNotificationResponseAsync()` instead.\n *\n * @return The hook may return one of these three types/values:\n * - `undefined` - until we're sure of what to return,\n * - `null` - if no notification response has been received yet,\n * - a [`NotificationResponse`](#notificationresponse) object - if a notification response was received.\n *\n * @example Responding to a notification tap by opening a URL that could be put into the notification's `data`\n * (opening the URL is your responsibility and is not a part of the `expo-notifications` API):\n * ```jsx\n * import * as Notifications from 'expo-notifications';\n * import { Linking } from 'react-native';\n *\n * export default function App() {\n * const lastNotificationResponse = Notifications.useLastNotificationResponse();\n * React.useEffect(() => {\n * if (\n * lastNotificationResponse &&\n * lastNotificationResponse.notification.request.content.data.url &&\n * lastNotificationResponse.actionIdentifier === Notifications.DEFAULT_ACTION_IDENTIFIER\n * ) {\n * Linking.openURL(lastNotificationResponse.notification.request.content.data.url);\n * }\n * }, [lastNotificationResponse]);\n * return (\n * // Your app content\n * );\n * }\n * ```\n * @header listen\n */\nexport default function useLastNotificationResponse() {\n const [lastNotificationResponse, setLastNotificationResponse] = useState<\n NotificationResponse | null | undefined\n >(undefined);\n\n // useLayoutEffect ensures the listener is registered as soon as possible\n useLayoutEffect(() => {\n const subscription = addNotificationResponseReceivedListener((response) => {\n const mappedResponse = mapNotificationResponse(response);\n setLastNotificationResponse(mappedResponse);\n });\n return () => {\n subscription.remove();\n };\n }, []);\n\n // On each mount of this hook we fetch last notification response\n // from the native module which is an \"always active listener\"\n // and always returns the most recent response.\n useEffect(() => {\n NotificationsEmitterModule.getLastNotificationResponseAsync?.().then((response) => {\n // We only update the state with the resolved value if it's empty,\n // because if it's not empty it must have been populated by the `useLayoutEffect`\n // listener which returns \"live\" values.\n const mappedResponse = response ? mapNotificationResponse(response) : response;\n setLastNotificationResponse((currentResponse) => currentResponse ?? mappedResponse);\n });\n }, []);\n\n return lastNotificationResponse;\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import { NotificationResponse } from '../Notifications.types';
2
+ /**
3
+ * @hidden
4
+ *
5
+ * Does any required processing of a notification response from native code
6
+ * before it is passed to a notification response listener, or to the
7
+ * last notification response hook.
8
+ *
9
+ * @param response The raw response passed in from native code
10
+ * @returns the mapped response.
11
+ */
12
+ export declare const mapNotificationResponse: (response: NotificationResponse) => NotificationResponse & {
13
+ notification: {
14
+ request: {
15
+ content: {
16
+ dataString?: string;
17
+ };
18
+ };
19
+ };
20
+ };
21
+ //# sourceMappingURL=mapNotificationResponse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapNotificationResponse.d.ts","sourceRoot":"","sources":["../../src/utils/mapNotificationResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,aAAc,oBAAoB;kBAEpD;QAAE,OAAO,EAAE;YAAE,OAAO,EAAE;gBAAE,UAAU,CAAC,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE;CAYlE,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @hidden
3
+ *
4
+ * Does any required processing of a notification response from native code
5
+ * before it is passed to a notification response listener, or to the
6
+ * last notification response hook.
7
+ *
8
+ * @param response The raw response passed in from native code
9
+ * @returns the mapped response.
10
+ */
11
+ export const mapNotificationResponse = (response) => {
12
+ const mappedResponse = { ...response };
13
+ try {
14
+ const dataString = mappedResponse?.notification?.request?.content['dataString'];
15
+ if (typeof dataString === 'string') {
16
+ mappedResponse.notification.request.content.data = JSON.parse(dataString);
17
+ delete mappedResponse.notification.request.content.dataString;
18
+ }
19
+ }
20
+ catch (e) {
21
+ console.log(`Error in response: ${e}`);
22
+ }
23
+ return mappedResponse;
24
+ };
25
+ //# sourceMappingURL=mapNotificationResponse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapNotificationResponse.js","sourceRoot":"","sources":["../../src/utils/mapNotificationResponse.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,QAA8B,EAAE,EAAE;IACxE,MAAM,cAAc,GAEhB,EAAE,GAAG,QAAQ,EAAE,CAAC;IACpB,IAAI;QACF,MAAM,UAAU,GAAG,cAAc,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;QAChF,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,cAAc,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC1E,OAAO,cAAc,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC;SAC/D;KACF;IAAC,OAAO,CAAM,EAAE;QACf,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;KACxC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC,CAAC","sourcesContent":["import { NotificationResponse } from '../Notifications.types';\n\n/**\n * @hidden\n *\n * Does any required processing of a notification response from native code\n * before it is passed to a notification response listener, or to the\n * last notification response hook.\n *\n * @param response The raw response passed in from native code\n * @returns the mapped response.\n */\nexport const mapNotificationResponse = (response: NotificationResponse) => {\n const mappedResponse: NotificationResponse & {\n notification: { request: { content: { dataString?: string } } };\n } = { ...response };\n try {\n const dataString = mappedResponse?.notification?.request?.content['dataString'];\n if (typeof dataString === 'string') {\n mappedResponse.notification.request.content.data = JSON.parse(dataString);\n delete mappedResponse.notification.request.content.dataString;\n }\n } catch (e: any) {\n console.log(`Error in response: ${e}`);\n }\n return mappedResponse;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-notifications",
3
- "version": "0.27.7",
3
+ "version": "0.27.8",
4
4
  "description": "Notifications module",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -55,5 +55,5 @@
55
55
  "peerDependencies": {
56
56
  "expo": "*"
57
57
  },
58
- "gitHead": "88a8226609b870c0635c39da43ac8306c4dc7031"
58
+ "gitHead": "72c76f834ed3742dbc0c79b43f0f3c11381f82d9"
59
59
  }
@@ -13,6 +13,11 @@ export type NotificationsPluginProps = {
13
13
  * @platform android
14
14
  */
15
15
  color?: string;
16
+ /**
17
+ * Default channel for FCMv1 notifications.
18
+ * @platform android
19
+ */
20
+ defaultChannel?: string;
16
21
  /**
17
22
  * Array of local paths to sound files (.wav recommended) that can be used as custom notification sounds.
18
23
  */
@@ -8,8 +8,11 @@ type dpiMap = Record<DPIString, {
8
8
  }>;
9
9
  export declare const ANDROID_RES_PATH = "android/app/src/main/res/";
10
10
  export declare const dpiValues: dpiMap;
11
- export declare const META_DATA_NOTIFICATION_ICON = "expo.modules.notifications.default_notification_icon";
12
- export declare const META_DATA_NOTIFICATION_ICON_COLOR = "expo.modules.notifications.default_notification_color";
11
+ export declare const META_DATA_NOTIFICATION_ICON = "com.google.firebase.messaging.default_notification_icon";
12
+ export declare const META_DATA_NOTIFICATION_ICON_COLOR = "com.google.firebase.messaging.default_notification_color";
13
+ export declare const META_DATA_NOTIFICATION_DEFAULT_CHANNEL_ID = "com.google.firebase.messaging.default_notification_channel_id";
14
+ export declare const META_DATA_LEGACY_NOTIFICATION_ICON = "expo.modules.notifications.default_notification_icon";
15
+ export declare const META_DATA_LEGACY_NOTIFICATION_ICON_COLOR = "expo.modules.notifications.default_notification_color";
13
16
  export declare const NOTIFICATION_ICON = "notification_icon";
14
17
  export declare const NOTIFICATION_ICON_RESOURCE: string;
15
18
  export declare const NOTIFICATION_ICON_COLOR = "notification_icon_color";
@@ -23,6 +26,7 @@ export declare const withNotificationIconColor: ConfigPlugin<{
23
26
  export declare const withNotificationManifest: ConfigPlugin<{
24
27
  icon: string | null;
25
28
  color: string | null;
29
+ defaultChannel: string | null;
26
30
  }>;
27
31
  export declare const withNotificationSounds: ConfigPlugin<{
28
32
  sounds: string[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withNotificationsAndroid = exports.setNotificationSounds = exports.setNotificationIconAsync = exports.setNotificationIconColor = exports.getNotificationColor = exports.getNotificationIcon = exports.withNotificationSounds = exports.withNotificationManifest = exports.withNotificationIconColor = exports.withNotificationIcons = exports.NOTIFICATION_ICON_COLOR_RESOURCE = exports.NOTIFICATION_ICON_COLOR = exports.NOTIFICATION_ICON_RESOURCE = exports.NOTIFICATION_ICON = exports.META_DATA_NOTIFICATION_ICON_COLOR = exports.META_DATA_NOTIFICATION_ICON = exports.dpiValues = exports.ANDROID_RES_PATH = void 0;
3
+ exports.withNotificationsAndroid = exports.setNotificationSounds = exports.setNotificationIconAsync = exports.setNotificationIconColor = exports.getNotificationColor = exports.getNotificationIcon = exports.withNotificationSounds = exports.withNotificationManifest = exports.withNotificationIconColor = exports.withNotificationIcons = exports.NOTIFICATION_ICON_COLOR_RESOURCE = exports.NOTIFICATION_ICON_COLOR = exports.NOTIFICATION_ICON_RESOURCE = exports.NOTIFICATION_ICON = exports.META_DATA_LEGACY_NOTIFICATION_ICON_COLOR = exports.META_DATA_LEGACY_NOTIFICATION_ICON = exports.META_DATA_NOTIFICATION_DEFAULT_CHANNEL_ID = exports.META_DATA_NOTIFICATION_ICON_COLOR = exports.META_DATA_NOTIFICATION_ICON = exports.dpiValues = exports.ANDROID_RES_PATH = void 0;
4
4
  const image_utils_1 = require("@expo/image-utils");
5
5
  const config_plugins_1 = require("expo/config-plugins");
6
6
  const fs_1 = require("fs");
@@ -17,8 +17,11 @@ exports.dpiValues = {
17
17
  const { addMetaDataItemToMainApplication, getMainApplicationOrThrow, removeMetaDataItemFromMainApplication, } = config_plugins_1.AndroidConfig.Manifest;
18
18
  const BASELINE_PIXEL_SIZE = 24;
19
19
  const ERROR_MSG_PREFIX = 'An error occurred while configuring Android notifications. ';
20
- exports.META_DATA_NOTIFICATION_ICON = 'expo.modules.notifications.default_notification_icon';
21
- exports.META_DATA_NOTIFICATION_ICON_COLOR = 'expo.modules.notifications.default_notification_color';
20
+ exports.META_DATA_NOTIFICATION_ICON = 'com.google.firebase.messaging.default_notification_icon';
21
+ exports.META_DATA_NOTIFICATION_ICON_COLOR = 'com.google.firebase.messaging.default_notification_color';
22
+ exports.META_DATA_NOTIFICATION_DEFAULT_CHANNEL_ID = 'com.google.firebase.messaging.default_notification_channel_id';
23
+ exports.META_DATA_LEGACY_NOTIFICATION_ICON = 'expo.modules.notifications.default_notification_icon';
24
+ exports.META_DATA_LEGACY_NOTIFICATION_ICON_COLOR = 'expo.modules.notifications.default_notification_color';
22
25
  exports.NOTIFICATION_ICON = 'notification_icon';
23
26
  exports.NOTIFICATION_ICON_RESOURCE = `@drawable/${exports.NOTIFICATION_ICON}`;
24
27
  exports.NOTIFICATION_ICON_COLOR = 'notification_icon_color';
@@ -44,12 +47,13 @@ const withNotificationIconColor = (config, { color }) => {
44
47
  });
45
48
  };
46
49
  exports.withNotificationIconColor = withNotificationIconColor;
47
- const withNotificationManifest = (config, { icon, color }) => {
50
+ const withNotificationManifest = (config, { icon, color, defaultChannel }) => {
48
51
  // If no icon or color provided in the config plugin props, fallback to value from app.json
49
52
  icon = icon || getNotificationIcon(config);
50
53
  color = color || getNotificationColor(config);
54
+ defaultChannel = defaultChannel || null;
51
55
  return (0, config_plugins_1.withAndroidManifest)(config, (config) => {
52
- config.modResults = setNotificationConfig({ icon, color }, config.modResults);
56
+ config.modResults = setNotificationConfig({ icon, color, defaultChannel }, config.modResults);
53
57
  return config;
54
58
  });
55
59
  };
@@ -95,15 +99,25 @@ function setNotificationConfig(props, manifest) {
95
99
  const mainApplication = getMainApplicationOrThrow(manifest);
96
100
  if (props.icon) {
97
101
  addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON, exports.NOTIFICATION_ICON_RESOURCE, 'resource');
102
+ addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_LEGACY_NOTIFICATION_ICON, exports.NOTIFICATION_ICON_RESOURCE, 'resource');
98
103
  }
99
104
  else {
100
105
  removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON);
106
+ removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_LEGACY_NOTIFICATION_ICON);
101
107
  }
102
108
  if (props.color) {
103
109
  addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON_COLOR, exports.NOTIFICATION_ICON_COLOR_RESOURCE, 'resource');
110
+ addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_LEGACY_NOTIFICATION_ICON_COLOR, exports.NOTIFICATION_ICON_COLOR_RESOURCE, 'resource');
104
111
  }
105
112
  else {
106
113
  removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_ICON_COLOR);
114
+ removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_LEGACY_NOTIFICATION_ICON_COLOR);
115
+ }
116
+ if (props.defaultChannel) {
117
+ addMetaDataItemToMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_DEFAULT_CHANNEL_ID, props.defaultChannel, 'value');
118
+ }
119
+ else {
120
+ removeMetaDataItemFromMainApplication(mainApplication, exports.META_DATA_NOTIFICATION_DEFAULT_CHANNEL_ID);
107
121
  }
108
122
  return manifest;
109
123
  }
@@ -174,10 +188,10 @@ function writeNotificationSoundFile(soundFileRelativePath, projectRoot) {
174
188
  }
175
189
  }
176
190
  }
177
- const withNotificationsAndroid = (config, { icon = null, color = null, sounds = [] }) => {
191
+ const withNotificationsAndroid = (config, { icon = null, color = null, sounds = [], defaultChannel = null }) => {
178
192
  config = (0, exports.withNotificationIconColor)(config, { color });
179
193
  config = (0, exports.withNotificationIcons)(config, { icon });
180
- config = (0, exports.withNotificationManifest)(config, { icon, color });
194
+ config = (0, exports.withNotificationManifest)(config, { icon, color, defaultChannel });
181
195
  config = (0, exports.withNotificationSounds)(config, { sounds });
182
196
  return config;
183
197
  };
@@ -19,6 +19,11 @@ export type NotificationsPluginProps = {
19
19
  * @platform android
20
20
  */
21
21
  color?: string;
22
+ /**
23
+ * Default channel for FCMv1 notifications.
24
+ * @platform android
25
+ */
26
+ defaultChannel?: string;
22
27
  /**
23
28
  * Array of local paths to sound files (.wav recommended) that can be used as custom notification sounds.
24
29
  */
@@ -32,9 +32,19 @@ const {
32
32
  } = AndroidConfig.Manifest;
33
33
  const BASELINE_PIXEL_SIZE = 24;
34
34
  const ERROR_MSG_PREFIX = 'An error occurred while configuring Android notifications. ';
35
- export const META_DATA_NOTIFICATION_ICON = 'expo.modules.notifications.default_notification_icon';
35
+
36
+ export const META_DATA_NOTIFICATION_ICON =
37
+ 'com.google.firebase.messaging.default_notification_icon';
36
38
  export const META_DATA_NOTIFICATION_ICON_COLOR =
39
+ 'com.google.firebase.messaging.default_notification_color';
40
+ export const META_DATA_NOTIFICATION_DEFAULT_CHANNEL_ID =
41
+ 'com.google.firebase.messaging.default_notification_channel_id';
42
+
43
+ export const META_DATA_LEGACY_NOTIFICATION_ICON =
44
+ 'expo.modules.notifications.default_notification_icon';
45
+ export const META_DATA_LEGACY_NOTIFICATION_ICON_COLOR =
37
46
  'expo.modules.notifications.default_notification_color';
47
+
38
48
  export const NOTIFICATION_ICON = 'notification_icon';
39
49
  export const NOTIFICATION_ICON_RESOURCE = `@drawable/${NOTIFICATION_ICON}`;
40
50
  export const NOTIFICATION_ICON_COLOR = 'notification_icon_color';
@@ -67,12 +77,14 @@ export const withNotificationIconColor: ConfigPlugin<{ color: string | null }> =
67
77
  export const withNotificationManifest: ConfigPlugin<{
68
78
  icon: string | null;
69
79
  color: string | null;
70
- }> = (config, { icon, color }) => {
80
+ defaultChannel: string | null;
81
+ }> = (config, { icon, color, defaultChannel }) => {
71
82
  // If no icon or color provided in the config plugin props, fallback to value from app.json
72
83
  icon = icon || getNotificationIcon(config);
73
84
  color = color || getNotificationColor(config);
85
+ defaultChannel = defaultChannel || null;
74
86
  return withAndroidManifest(config, (config) => {
75
- config.modResults = setNotificationConfig({ icon, color }, config.modResults);
87
+ config.modResults = setNotificationConfig({ icon, color, defaultChannel }, config.modResults);
76
88
  return config;
77
89
  });
78
90
  };
@@ -117,7 +129,7 @@ export async function setNotificationIconAsync(projectRoot: string, icon: string
117
129
  }
118
130
 
119
131
  function setNotificationConfig(
120
- props: { icon: string | null; color: string | null },
132
+ props: { icon: string | null; color: string | null; defaultChannel?: string | null },
121
133
  manifest: AndroidConfig.Manifest.AndroidManifest
122
134
  ) {
123
135
  const mainApplication = getMainApplicationOrThrow(manifest);
@@ -128,8 +140,15 @@ function setNotificationConfig(
128
140
  NOTIFICATION_ICON_RESOURCE,
129
141
  'resource'
130
142
  );
143
+ addMetaDataItemToMainApplication(
144
+ mainApplication,
145
+ META_DATA_LEGACY_NOTIFICATION_ICON,
146
+ NOTIFICATION_ICON_RESOURCE,
147
+ 'resource'
148
+ );
131
149
  } else {
132
150
  removeMetaDataItemFromMainApplication(mainApplication, META_DATA_NOTIFICATION_ICON);
151
+ removeMetaDataItemFromMainApplication(mainApplication, META_DATA_LEGACY_NOTIFICATION_ICON);
133
152
  }
134
153
  if (props.color) {
135
154
  addMetaDataItemToMainApplication(
@@ -138,8 +157,32 @@ function setNotificationConfig(
138
157
  NOTIFICATION_ICON_COLOR_RESOURCE,
139
158
  'resource'
140
159
  );
160
+ addMetaDataItemToMainApplication(
161
+ mainApplication,
162
+ META_DATA_LEGACY_NOTIFICATION_ICON_COLOR,
163
+ NOTIFICATION_ICON_COLOR_RESOURCE,
164
+ 'resource'
165
+ );
141
166
  } else {
142
167
  removeMetaDataItemFromMainApplication(mainApplication, META_DATA_NOTIFICATION_ICON_COLOR);
168
+ removeMetaDataItemFromMainApplication(
169
+ mainApplication,
170
+ META_DATA_LEGACY_NOTIFICATION_ICON_COLOR
171
+ );
172
+ }
173
+
174
+ if (props.defaultChannel) {
175
+ addMetaDataItemToMainApplication(
176
+ mainApplication,
177
+ META_DATA_NOTIFICATION_DEFAULT_CHANNEL_ID,
178
+ props.defaultChannel,
179
+ 'value'
180
+ );
181
+ } else {
182
+ removeMetaDataItemFromMainApplication(
183
+ mainApplication,
184
+ META_DATA_NOTIFICATION_DEFAULT_CHANNEL_ID
185
+ );
143
186
  }
144
187
  return manifest;
145
188
  }
@@ -229,11 +272,11 @@ function writeNotificationSoundFile(soundFileRelativePath: string, projectRoot:
229
272
 
230
273
  export const withNotificationsAndroid: ConfigPlugin<NotificationsPluginProps> = (
231
274
  config,
232
- { icon = null, color = null, sounds = [] }
275
+ { icon = null, color = null, sounds = [], defaultChannel = null }
233
276
  ) => {
234
277
  config = withNotificationIconColor(config, { color });
235
278
  config = withNotificationIcons(config, { icon });
236
- config = withNotificationManifest(config, { icon, color });
279
+ config = withNotificationManifest(config, { icon, color, defaultChannel });
237
280
  config = withNotificationSounds(config, { sounds });
238
281
  return config;
239
282
  };
@@ -2,6 +2,7 @@ import { EventEmitter, Subscription, UnavailabilityError } from 'expo-modules-co
2
2
 
3
3
  import { Notification, NotificationResponse } from './Notifications.types';
4
4
  import NotificationsEmitterModule from './NotificationsEmitterModule';
5
+ import { mapNotificationResponse } from './utils/mapNotificationResponse';
5
6
 
6
7
  // Web uses SyntheticEventEmitter
7
8
  const emitter = new EventEmitter(NotificationsEmitterModule);
@@ -86,7 +87,10 @@ export function addNotificationResponseReceivedListener(
86
87
  ): Subscription {
87
88
  return emitter.addListener<NotificationResponse>(
88
89
  didReceiveNotificationResponseEventName,
89
- listener
90
+ (response: NotificationResponse) => {
91
+ const mappedResponse = mapNotificationResponse(response);
92
+ listener(mappedResponse);
93
+ }
90
94
  );
91
95
  }
92
96
 
@@ -3,6 +3,7 @@ import { useEffect, useLayoutEffect, useState } from 'react';
3
3
  import { NotificationResponse } from './Notifications.types';
4
4
  import { addNotificationResponseReceivedListener } from './NotificationsEmitter';
5
5
  import NotificationsEmitterModule from './NotificationsEmitterModule';
6
+ import { mapNotificationResponse } from './utils/mapNotificationResponse';
6
7
 
7
8
  /**
8
9
  * A React hook always returns the notification response that was received most recently
@@ -47,7 +48,8 @@ export default function useLastNotificationResponse() {
47
48
  // useLayoutEffect ensures the listener is registered as soon as possible
48
49
  useLayoutEffect(() => {
49
50
  const subscription = addNotificationResponseReceivedListener((response) => {
50
- setLastNotificationResponse(response);
51
+ const mappedResponse = mapNotificationResponse(response);
52
+ setLastNotificationResponse(mappedResponse);
51
53
  });
52
54
  return () => {
53
55
  subscription.remove();
@@ -62,7 +64,8 @@ export default function useLastNotificationResponse() {
62
64
  // We only update the state with the resolved value if it's empty,
63
65
  // because if it's not empty it must have been populated by the `useLayoutEffect`
64
66
  // listener which returns "live" values.
65
- setLastNotificationResponse((currentResponse) => currentResponse ?? response);
67
+ const mappedResponse = response ? mapNotificationResponse(response) : response;
68
+ setLastNotificationResponse((currentResponse) => currentResponse ?? mappedResponse);
66
69
  });
67
70
  }, []);
68
71
 
@@ -0,0 +1,27 @@
1
+ import { NotificationResponse } from '../Notifications.types';
2
+
3
+ /**
4
+ * @hidden
5
+ *
6
+ * Does any required processing of a notification response from native code
7
+ * before it is passed to a notification response listener, or to the
8
+ * last notification response hook.
9
+ *
10
+ * @param response The raw response passed in from native code
11
+ * @returns the mapped response.
12
+ */
13
+ export const mapNotificationResponse = (response: NotificationResponse) => {
14
+ const mappedResponse: NotificationResponse & {
15
+ notification: { request: { content: { dataString?: string } } };
16
+ } = { ...response };
17
+ try {
18
+ const dataString = mappedResponse?.notification?.request?.content['dataString'];
19
+ if (typeof dataString === 'string') {
20
+ mappedResponse.notification.request.content.data = JSON.parse(dataString);
21
+ delete mappedResponse.notification.request.content.dataString;
22
+ }
23
+ } catch (e: any) {
24
+ console.log(`Error in response: ${e}`);
25
+ }
26
+ return mappedResponse;
27
+ };