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.
- package/CHANGELOG.md +14 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/notifications/NotificationsPackage.java +17 -1
- package/android/src/main/java/expo/modules/notifications/badge/BadgeHelper.kt +6 -1
- package/android/src/main/java/expo/modules/notifications/notifications/NotificationManager.java +33 -0
- package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java +28 -5
- package/android/src/main/java/expo/modules/notifications/notifications/emitting/NotificationsEmitter.kt +12 -16
- package/android/src/main/java/expo/modules/notifications/notifications/handling/NotificationsHandler.kt +7 -1
- package/android/src/main/java/expo/modules/notifications/notifications/handling/SingleNotificationHandlerTask.java +8 -4
- package/android/src/main/java/expo/modules/notifications/notifications/interfaces/NotificationListener.java +10 -0
- package/android/src/main/java/expo/modules/notifications/service/delegates/ExpoNotificationLifecycleListener.java +75 -0
- package/build/NotificationsEmitter.d.ts.map +1 -1
- package/build/NotificationsEmitter.js +5 -1
- package/build/NotificationsEmitter.js.map +1 -1
- package/build/useLastNotificationResponse.d.ts.map +1 -1
- package/build/useLastNotificationResponse.js +5 -2
- package/build/useLastNotificationResponse.js.map +1 -1
- package/build/utils/mapNotificationResponse.d.ts +21 -0
- package/build/utils/mapNotificationResponse.d.ts.map +1 -0
- package/build/utils/mapNotificationResponse.js +25 -0
- package/build/utils/mapNotificationResponse.js.map +1 -0
- package/package.json +2 -2
- package/plugin/build/withNotifications.d.ts +5 -0
- package/plugin/build/withNotificationsAndroid.d.ts +6 -2
- package/plugin/build/withNotificationsAndroid.js +21 -7
- package/plugin/src/withNotifications.ts +5 -0
- package/plugin/src/withNotificationsAndroid.ts +49 -6
- package/src/NotificationsEmitter.ts +5 -1
- package/src/useLastNotificationResponse.ts +5 -2
- 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
|
package/android/build.gradle
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
package/android/src/main/java/expo/modules/notifications/notifications/NotificationManager.java
CHANGED
|
@@ -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
|
}
|
package/android/src/main/java/expo/modules/notifications/notifications/NotificationSerializer.java
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 =
|
|
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;
|
|
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,
|
|
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;
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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.
|
|
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": "
|
|
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 = "
|
|
12
|
-
export declare const META_DATA_NOTIFICATION_ICON_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 = '
|
|
21
|
-
exports.META_DATA_NOTIFICATION_ICON_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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
};
|