I have an issue with the onOpened function of oneSignal (happens on iOS and Android). When I receive a push notification, clicking on it opens the app. I have a code in the app, that gets the notification text on the onOpened function and opens another screen inside the app, to display the full notification text.
But clicking the notification it opens the app, but does nothing else. When I close the app and reopen it, the other screen opens with the full notification text.
So, first time app opens on click notification, nothing happens, close app and opening it a second time, it works.
I use the following code in the App.js:
componentDidMount () {
OneSignal.addEventListener('opened', this.onOpened);
}
onOpened(openResult) {
console.log('Message: ', openResult.notification.payload.body);
console.log('Data: ', openResult.notification.payload.additionalData);
console.log('isActive: ', openResult.notification.isAppInFocus);
console.log('openResult: ', openResult);
if (openResult.notification.payload.body !== '') {
const pushTitle = openResult.notification.payload.title;
const pushBody = openResult.notification.payload.body;
AsyncStorage.multiSet([['pushtitle', pushTitle], ['pushbody', pushBody]]);
}
};
On the home component (the first screen):
componentDidMount() {
const { navigation } = this.props;
AsyncStorage.multiGet(["pushtitle", "pushbody"]).then(responses => {
responses.map((result, i, response) => {
if (response[1][1] !== '' && response[1][1] !== null) {
setTimeout(() => {
navigation.navigate('PushDetails', {
title: response[0][1],
body: response[1][1]
});
}, 100)
}
})
})
}
The "PushDetails" is the extra screen to show the full notification text.
The push notifications are send from an external page using the oneSignal Api.
Anybody an idea what is going on?
Related
My question is simple but I'm not being able to find any documentation about this.
I have a notification that the app sends to the user periodically if the user is not loggeIn, when the user taps on the notification it opens the app and opens the "Login" screen, the problem is that if the app is killed the app opens but it doesn't navigate to the "Login" screen, it stays at the home screen.
Any ideas how to do this?
Thank you very much in advance
Right now I'm using RootNavigation.navigate('Login') when the user taps on the notification.
Your problem is that navigation not loaded yet in moment you use Root Navigation.navigate().
You can make it like this
const Deferred = () => {
let d = {};
d.promise = new Promise(function(resolve, reject) {
d.resolve = resolve;
d.reject = reject;
});
return d;
};
const navigationDeferred = new Deferred()
<NavigationContainer ref={navigationRef} onReady={()=>{navigationDeferred.resolve()}} >
messaging()
.getInitialNotification()
.then(remoteMessage => {
if (remoteMessage) {
console.log(
'Notification caused app to open from quit state:',
remoteMessage.notification,
);
navigationDeferred.promise.then(()=>{
NavHelper.navigate("YourScreen");;
})
}
});
NavHelper it helper from documentation https://reactnavigation.org/docs/navigating-without-navigation-prop/
I know that I can listen to the change of the screen BUT what if I am calling AGAIN the same screen I am already focused on? I tried with the listener below and it doesnt detect:
async componentDidMount() {
this.subscribeFocusHomeScreen = this.props.navigation.addListener('focus', () => {
const logmeout = (this.props.route.params && this.props.route.params.data) ? this.props.route.params.data : '';
if (logmeout == 'Logout') {
this.popOutToastMsg();
this.removeTimerFromNavHeader();
this.cancelAllSubscriptions();
this._logOutHandler();
this.props.navigation.navigate('Login');
}
});
...
}
'focus' listener works when I go from screen B to A but not if I click on a button on sidebar which sends me to screen A (if I am already on screen A).
I'm usnig fcm for notification in nodejs. So I made this And when I push message from nodejs to android, I got message well. When the app is running, I can get the small Icon image. But when the app is not running, I got the notification with no image. how can I get the push message with image when the app is in background? here is my nodejs code
let pushMsg = req.body.pushMsg;
let groupName = req.body.groupName;
console.log(groupName)
var condition = "'"+groupName+"' in topics"; //"'all' in topics || 'industry-tech' in topics";
var message = {
notification: {
title: 'schedule updated',
body: pushMsg,
image:'./upload/haiilogo.png'
},
condition: condition
};
admin.messaging().send(message)
.then((response) => {
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
I solved. I added manifest to default icon like this
<meta-data android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#drawable/haiilogo" />
reading from the expo docs:
For iOS, you would be wise to handle push notifications that are
received while the app is foregrounded, because otherwise the user
will never see them. Notifications that arrive while the app are
foregrounded on iOS do not show up in the system notification list. A
common solution is to just show the notification manually. For
example, if you get a message on Messenger for iOS, have the app
foregrounded, but do not have that conversation open, you will see the
notification slide down from the top of the screen with a custom
notification UI.
What I don't understand is what is the best approach for that? is there an Expo API for showing such messages? or should I create an alert component of my own? It is not really clear from the docs.
Thanks.
This answer is outdated as of February 20, 2020. Please see https://stackoverflow.com/a/60344280/2441420 for how to show iOS Notification when your application is in the Foreground
There isn't an Expo API for showing those messages. You can use any 'toast' library of your choosing and display the notification message, but that should be all your code.
For example, this is how we are doing right now:
export default class HomeScreen extends React.Component {
componentDidMount() {
this.notificationSubscription = Notifications.addListener(
(notification) => this.handlePushNotification(notification),
);
}
handlePushNotification(notification) {
const { navigation } = this.props;
PushNotificationsService.handleNotification(notification, navigation);
}
(...)
import Toast from 'react-native-root-toast';
export default class PushNotificationsService {
static handleNotification(notification, navigation) {
if (notification.data.screen && notification.origin === 'selected') {
navigation.navigate(notification.data.screen);
}
Toast.show(notification.data.message);
}
}
Toast libraries include:
react-native-root-toast
react-native-easy-toast
react-native-simple-toast
Now you can just add that in one of your app entry point. The shouldShowAlert is what you want here
import * as Notifications from 'expo-notifications';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
App.json :
{
"expo": {
"notification": {
"iosDisplayInForeground": true
}
}
DEMO
I'm not sure exactly when this was added to Expo, but as of Expo version 36 is easily doable.
To show Expo Push Notifications on iOS when your app is in the foreground, please do the following:
import { Vibration } from "react-native";
import { Notifications } from "expo";
import * as Permissions from "expo-permissions";
import Constants from "expo-constants";
registerForPushNotificationsAsync = async () => {
if (Constants.isDevice) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Permissions.askAsync(
Permissions.NOTIFICATIONS
);
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
let token = await Notifications.getExpoPushTokenAsync();
console.log("Go to https://expo.io/notifications and copy the token below to easily send yourself a notification.");
console.warn("Notifications on iOS (and I believe Android) ONLY WORK ON A PHYSICAL DEVICE, not a simulator or emulator!!!")
console.log(token);
this.setState({ expoPushToken: token });
} else {
alert("Must use physical device for Push Notifications");
}
};
componentDidMount() {
this.registerForPushNotificationsAsync();
this._notificationSubscription = Notifications.addListener(
this._handleNotification
);
}
_handleNotification = async notification => {
if (notification.remote) {
Vibration.vibrate();
const notificationId = Notifications.presentLocalNotificationAsync({
title: "Follow #technoplato",
body: "To learn yourself goodly (also follow PewDiePie)",
ios: { _displayInForeground: true } // <-- HERE'S WHERE THE MAGIC HAPPENS
});
}
};
Quick and Easy Sanity Check
1) Go here: https://expo.io/notifications
2) Copy the token that is output to the terminal when your application is run.
3) Open your application on iOS.
4) Send a notification to yourself from https://expo.io/notifications and observe that it shows up even when your app is foregrounded.
Notes
Notifications WILL NOT BE RECEIVED ON AN IOS SIMULATOR
Expo makes Notifications ridiculously easy. I honestly can't believe it.
No idea why displayInForeground is false by default and not more prominent in the documentation. I'll submit a PR for it if I can.
Code originally found at this Snack: https://snack.expo.io/#documentation/pushnotifications?platform=ios
LocalNotification.ios._displayInForeground found here: https://docs.expo.io/versions/v36.0.0/sdk/notifications/#localnotification
Here is my code, how can I navigate user to the desired screen when clicked on a notification or button in a notification.
componentWillMount() {
OneSignal.addEventListener('received', this.onReceived);
OneSignal.addEventListener('opened', this.onOpened);
OneSignal.addEventListener('registered', this.onRegistered);
OneSignal.addEventListener('ids', this.onIds);
OneSignal.inFocusDisplaying(2);
OneSignal.requestPermissions({
alert: true,
badge: true,
sound: true
});
}
componentWillUnmount() {
this.isUnmounted = true;
OneSignal.removeEventListener('received', this.onReceived);
OneSignal.removeEventListener('opened', this.onOpened);
OneSignal.removeEventListener('registered', this.onRegistered);
OneSignal.removeEventListener('ids', this.onIds);
}
onReceived(notification) {
console.log("Notification received: ", notification);
}
onOpened(openResult) { // HERE I WANT TO NAVIGATE TO ANOTHER SCREEN INSTEAD OF HOME SCREEN
this.isNotification = true;
let data = openResult.notification.payload.additionalData;
let inFocus = openResult.notification.isAppInFocus;
console.log('Message: ', openResult.notification.payload.body);
console.log('Data: ', openResult.notification.payload.additionalData);
console.log('isActive: ', openResult.notification.isAppInFocus);
console.log('openResult: ', openResult);
}
onRegistered(notifData) {
console.log("Device had been registered for push notifications!", notifData);
}
onIds(device) {
try {
AsyncStorage.setItem("#SC:deviceInfo", JSON.stringify(device));
} catch (error) {
console.log(error);
}
}
Do anyone have knowledge about all this, React Native + OneSignal + React Navigation + Redux. Please help!
To achieve the desired behavior you can do couple of things. You can manually check the notification and state of the router and if its necessary redirect the user to the screen or you can use the Deep Linking functionality.
To use Deep Linking you attach url parameter to your notification while sending it. To direct user to the correct screen in your app you can use react-navigation deep linking functionality.
From One Signal Documentation
url string The URL to open in the browser when a user clicks on the
notification. Example: http://www.google.com
Note: iOS needs https or updated NSAppTransportSecurity in plist
From React Navigation Documentation
Deep Linking
In this guide we will set up our app to handle external URIs. Let's start with the SimpleApp that we created in the
getting started guide. In this example, we want a URI like
mychat://chat/Taylor to open our app and link straight into Taylor's
chat page.
You can dispatch a NavigationAction or perform a navigate action when onOpened is fired. Following snippet should work:
componentWillMount() {
OneSignal.inFocusDisplaying(0);
OneSignal.removeEventListener('opened', this.onOpened.bind(this));
OneSignal.addEventListener('opened', this.onOpened.bind(this));
}
onOpened(openResult) {
let data = openResult.notification.payload.additionalData;
// ScreenName is the name of the screen you defined in StackNavigator
this.props.navigation.navigate('ScreenName', data)
}
In search for the solution I landed on this question and I think most of the answers are now old. So, in case anyone looking for the solution can try this.
OneSignal.setNotificationOpenedHandler((notificationResponse) => {
const { notification } = notificationResponse;
if (notification) {
const { additionalData = null } = notification;
if (additionalData) {
const { type } = additionalData;
navigateToScreen(type);
}
}
});
const navigateToScreen = (type) => {
switch (type) {
case "post":
case "track":
navigation.navigate("SinglePost");
return;
default:
return;
}
};
In case someone else comes with a similar problem to mine I want to add onto what #Mostafiz Rahman said. The app I was working on had a bunch of nested stacks and tabs (react-navigation v1) inside of a drawer, and if Stack1 was backgrounded and the notification was for Stack2 I couldn't get them to jump around.
I ended up putting the logic as described by Mr. Rahman in every one of the stacks' first screens -- 1st screen of Stack1, 1st screen of Stack2, etc -- and that did it!