I am new to React Native and need to implement a functionality where the app needs to send the user a notification every day at a certain time. The data to be shown for each day is stored in a json file on the client side and will not change. The notifications are on a schedule. Given that I was hoping there could be a way to just trigger a notification from the app itself.
Does anyone know of a way to achieve this without having to detach the app from expo? I can't use 'react-native-push-notification'without running react-native link and that requires me to detach the app. Is that right?
Is this possible?
Thanks :)
You can do this with expo using the scheduleLocalNotificationAsync function (have a look at these docs for more details). Make sure you have permission to send notifications first. Note that if the notification triggers when the app is in the foreground you won't see a notification but you can still listen to this event.
i. Ask for permission
import { Permissions } from 'expo';
// ... somewhere before scheduling notifications ...
const { status } = await Permissions.getAsync(Permissions.NOTIFICATIONS);
if (status !== 'granted') {
await Permissions.askAsync(Permissions.NOTIFICATIONS);
}
ii. Schedule the notification
import { Notifications } from 'expo';
const notification = {
title: 'Hi there!',
body: 'Tap me to open the app.',
android: { sound: true }, // Make a sound on Android
ios: { sound: true }, // Make a sound on iOS
};
const options = {
time: Date.now() + 10000, // Schedule it in 10 seconds
repeat: 'day', // Repeat it daily
};
// ... somewhere after requesting permission ...
const id = Notifications.scheduleLocalNotificationAsync(notification, options)
// If you want to react even when your app is still in the
// foreground, you can listen to the event like this:
Notifications.addListener(() => {
console.log('triggered!');
});
iii. Cancel the scheduled notification
You can use the returned id of the scheduleLocalNotificationAsync function to cancel the notification.
import { Notifications } from 'expo';
// ... get the id of the scheduled notification ...
Notifications.cancelScheduledNotificationAsync(id)
Related
I developed react native application with rnfirebase and notifee for sending the push notification. foreground is working properly, message is displayed only once. but the background notification is displaying twice like one is from messaging().setBackgroundMessageHandler and another one is android's default push notification. First message is from default push notification and next one is from firebase messaging. So how do I remove android's default push notification. I'm also checked that first default notification is not using the firebase messaging and notifee. It's comes from outside of react native like android's native push notification
The notifications that you are seeing are most likely one from firebase and another from Notifee.
In my project I was handling the notifications that were coming from firebase via firebase.messaging().onMessage and inside this listener I was showing a local notification using Notifee so that the notification shows in the foreground.
async showNotificationInForeground(message: FirebaseMessagingTypes.RemoteMessage) {
const { messageId, notification, data } = message
const channelId = await Notifee.createChannel({
id: messageId,
name: 'Pressable Channel',
importance: AndroidImportance.HIGH,
})
await Notifee.displayNotification({
title: notification?.title || '',
body: notification?.body || '',
data,
android: {
channelId,
importance: AndroidImportance.HIGH,
pressAction: {
id: messageId,
},
smallIcon: 'ic_stat_name',
localOnly: true,
},
})
}
However what was happening was that I was calling this showNotificationInForeground method to show the local Notifee notification on both firebase's background and messaging listeners ie: firebase.messaging().onMessage and firebase.messaging().setBackgroundMessageHandler
So what I ended up doing was only calling the showNotificationInForeground method in onMessage listener and not in setBackgroundMessageHandler, which resulted in showing the local notification in the foreground, and the firebase notification in the background.
If this is not the case for you, you are most likely registering an extra notification receiver inside your AndroidManifest.xml file which is causing the duplication
I had the same problem, where there were two notifications a) 1 from notifee (which I wanted to keep) b) from FCM (which I didnt want).
Sending a data only message from my custom server solved the issue. Below is a snippet of the serverside for sending data only message :
const admin = require("firebase-admin");
// Do other stuff like create an express server to listen and trigger sending message
// Note dont forget to to initialize the app
await admin.messaging().sendToDevice(
tokens, // ['token_1', 'token_2', ...]
{
data: {
owner: "Me",
user: "My Friend",
},
},
{
// Required for background/quit data-only messages on iOS
contentAvailable: true,
// Required for background/quit data-only messages on Android
priority: "high",
}
);
You can use other methods as well, just make sure that FCM doesnt include notification and is data only.
I'm using this package for implementing local push notification:
https://github.com/zo0r/react-native-push-notification
I'm using action button like this to show buttons in my notification along with a text and a title:
PushNotification.localNotification({
...
actions: '["Yes", "No"]'
})
I wanted to know how I can call a function when user clicks on of these actions and app becomes visible?
I've tried PushNotification.configure in my componentDidMount method in my home screen like this but nothing comes up in the console:
PushNotification.configure({
// (required) Called when a remote or local notification is opened or received
onNotification: function(notification) {
console.log("NOTIFICATION:", notification);
if (notification.userInteraction) {
console.log("NOTIFICATION:");
}
// process the notification
}
});
I got it working.
In your App.js you need to set popInitialNotification to true. Something like this:
async componentDidMount() {
PushNotification.configure({
// (required) Called when a remote or local notification is opened or received
onNotification: function(notification) {
console.log("NOTIFICATION:", notification.action);
},
// IOS ONLY (optional): default: all - Permissions to register.
permissions: {
alert: true,
badge: true,
sound: true
},
// Should the initial notification be popped automatically
// default: true
popInitialNotification: true,
/**
* (optional) default: true
* - Specified if permissions (ios) and token (android and ios) will requested or not,
* - if not, you must call PushNotificationsHandler.requestPermissions() later
*/
requestPermissions: true
});
}
notification.action will gibe you the label of the button clicked.
In your button/app active event you forgot to call to schedule the notification and actually set when it will arise, so you need to
PushNotification.localNotificationSchedule(details: Object)
Schedule it for now with the same id, then your notification will come up immediately.
See all options for scheduling here
import PushNotificationAndroid from 'react-native-push-notification'
(function() {
// Register all the valid actions for notifications here and add the action handler for each action
PushNotificationAndroid.registerNotificationActions(['Accept','Reject','Yes','No']);
DeviceEventEmitter.addListener('notificationActionReceived', function(action){
console.log ('Notification action received: ' + action);
const info = JSON.parse(action.dataJSON);
if (info.action == 'Accept') {
// Do work pertaining to Accept action here
} else if (info.action == 'Reject') {
// Do work pertaining to Reject action here
}
// Add all the required actions handlers
});
})();
DO NOT USE .configure() INSIDE A COMPONENT, EVEN App
If you do, notification handlers will not fire, because they are not loaded. Instead, use .configure() in the app's first file, usually index.js.
It's mentioned in the documentation.
Try to follow their example for implementation It will help you to setup in your project.
I'm sending multiple notifications to my app. What I want to achieve is whenever a user clicks one notification then all notifications in the notification tray disspear.
I've tried adding
notification.android.setAutoCancel(true)
which does the trick for only one notification (the one which is being clicked)
I've also tried:
firebase.notifications().removeAllDeliveredNotifications()
which doesn't have any effect.
How can I achieve this?
Here's my full code:
componentDidMount() {
firebase.notifications().removeAllDeliveredNotifications()
this.notificationDisplayedListener = firebase.notifications().onNotificationDisplayed((notification) => {
});
this.notificationListener = firebase.notifications().onNotification(async (notification) => {
// Process your notification as required
notification.android.setAutoCancel(true)
firebase.notifications().removeAllDeliveredNotifications()
}
async componentWillMount() {
this.notificationDisplayedListener = firebase.notifications().onNotificationDisplayed((notification) => {
});
this.notificationListener = firebase.notifications().onNotification((notification) => {
});
this.notificationDisplayedListener();
this.notificationListener();
}
Try moving the code for removing notifications
(removeAllDeliveredNotifications()) from onNotification listener to onNotificationOpened listener. There might be a race condition as you are trying to remove notifications on arrival.
Also because you want to clear notifications when user taps on one notification.
PS. Keep the notification listeners in either componentDidMount or componentWillMount, not both.
I am using react-native-fcm library for android device. I am getting notification properly when my application is running, but when my application is in the background or killed then I am getting notification data in JSON format similarly in an image I shared here.
componentDidMount() {
// iOS: show permission prompt for the first call. later just check permission in user settings
// Android: check permission in user settings
FCM.requestPermissions().then(()=>console.log('granted')).catch(()=>console.log('notification permission rejected'));
/*FCM.getFCMToken().then(token => {
console.log('Token',token)
// store fcm token in your server
});*/
this.notificationListener = FCM.on(FCMEvent.Notification, async(notif) => {
console.log('FCM notification', notif)
this.sendRemote(notif)
});
// initial notification contains the notification that launchs the app. If user launchs app by clicking banner, the banner notification info will be here rather than through FCM.on event
// sometimes Android kills activity when app goes to background, and when resume it broadcasts notification before JS is run. You can use FCM.getInitialNotification() to capture those missed events.
// initial notification will be triggered all the time even when open app by icon so send some action identifier when you send notification
/*FCM.getInitialNotification().then(notif => {
console.log('FCM', notif)
this.sendRemote(notif)
//console.log('Initial Notification',notif)
});*/
FCM.getInitialNotification().then((notif: any) => {
// for android/ios app killed state
console.log("ifAny",notif)
if (notif) {
console.log("Any",notif)
// there are two parts of notif. notif.notification contains the notification payload, notif.data contains data payload
}
});
}
sendRemote(notif) {
var data = notif.fcm.body;
var title = notif.fcm.title;
FCM.presentLocalNotification({
title: 'App Name',
body: title,
big_text: title,
large_icon: 'ic_launcher',
priority: 'high',
sound: "default",
click_action: this.clickActions(notif),
show_in_foreground: true,
wake_screen: true,
local: true,
param: notif.notify_about,
paramData: data
});
}
notify_about:'',
fcm:{action:null,
body:"{data:'',time:''}",
color:null,
icon: '',
tag:null,
title:"Notification title"}
this is my data format which I am sending from the server.
Here I want to show only data body. But when the app is killed or in the background, it shows the complete body in the notification.And Its working fine when the app is running.
I am using react native v0.45.1.
How can I add to my application notification (no matter if the app is in the background or foreground) that the user can remove only after click an acknowledge button.
I don't want the user to swipe the notification aside without notice it.
how can it be done?
I am not sure https://github.com/wix/react-native-notifications will do what I need.
Edit
I want to have a notification that will act like:
'USB debugging connected
'Touch to disable USB debugging'
The notification can't be removed unless the user actively do something, in my case it will be 'click' on a button
after a lot of digging the solution I implemented was:
setting repeat interval for the notification.
for android:
repeatType: 'time',
repeatTime: timeSpan,
for iOS:
repeatType: 'minute',
when the user click the notification (decided its not the right approached to add actions - buttons - to notification):
PushNotification.configure({
onNotification: (notification) => {
console.log('NOTIFICATION:', notification);
const clicked = notification.userInteraction;
if (clicked) {
if (Platform.OS === 'ios') {
PushNotification.cancelLocalNotifications({ id: notification.data.id });
} else {
PushNotification.cancelLocalNotifications({ id: notification.id });
}
}
},
});
I am aware that this might not be the exact answer to this question. However,
I was looking for a similar solution earlier; how to remove notifications that hasn't been dismissed or opened by a user.
Sometimes the user might open the application / screen, without clicking on the notification. The notification will still be displayed in that case.
Using React Native with Expo, this is the solution I created for removing old notifications created using their Push Notification API.
import * as Notifications from "expo-notifications";
//...
// Remove notifications that exists for this conversation!
useEffect(() => {
Notifications.getPresentedNotificationsAsync().then(res => {
for (let k in res) {
if (
res[k].request.content.data &&
res[k].request.content.data.screen === "Conversations" &&
res[k].request.content.data.conversationId === conversationId
) {
Notifications.dismissNotificationAsync(res[k].request.identifier);
console.log("removed a notification for this conversation");
}
}
});
}, []);
By using the data sent to the Push Notification API, we can determine which notifications should be removed. in this case all notifications containing a conversationId matching the current value from the route are dismissed.