Reputation: 2560
I'm using react-navigation
and react-native-push-notification
. How can I open a certain StackNavigator's
screen in onNotification
callback? Should work when:
I only need it working in Android for now.
I've tried to pass a callback function to notification in my component:
_handleClick() {
PushNotification.localNotification({
foreground: false
userInteraction: false
message: 'My Notification Message'
onOpen: () => { this.props.navigation.navigate("OtherScreen") },
})
}
And to fire onOpen
in PushNotification
config:
onNotification: function(notification) {
notification.onOpen()
}
But it seems that functions can't be passed to notification, unless a value is a string it's ignored, causing onOpen
to be undefined.
Upvotes: 23
Views: 40449
Reputation: 8591
This information may be useful to someone.
In my scenario, if a user receives a notification but hasn't logged in, then the app should not redirect them to the desired screen.
If the user is not on the Login screen, and user press the notification, we should redirect them (Screen2 in my case).
For this I send the navigation reference to my NotificationHelper
Using:
@react-navigation/native: 6.X
@react-native-firebase/messaging: 17.x
@notifee/react-native: 7.x
Code:
MainScreen.tsx
import { createNavigationContainerRef } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const MainScreen: React.FC = () => {
const Stack = createNativeStackNavigator();
const navigationRef = createNavigationContainerRef();
return (
<NavigationContainer ref={navigationRef}>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Login" component={LoginScree} />
<Stack.Screen name="Screen1" component={Screen1} />
<Stack.Screen
name="Screen2"
component={Screen2}
/>
</Stack.Navigator>
<NotificationHelper navigationRef={navigationRef} />
</NavigationContainer>
);
};
NotificationHelper.tsx
import messaging, {
FirebaseMessagingTypes,
} from '@react-native-firebase/messaging';
import { NavigationContainerRefWithCurrent } from '@react-navigation/native';
import { FC, useCallback, useEffect } from 'react';
type PropsType = {
navigationRef: NavigationContainerRefWithCurrent<ReactNavigation.RootParamList>;
};
const NotificationHelper: FC<PropsType> = (props: PropsType) => {
const redirectToScreen = useCallback(
(notification: any) => {
if (props.navigationRef) {
const currentRoute = props.navigationRef.getCurrentRoute()?.name;
if (currentRoute) {
if (currentRoute !== 'Login') {
props.navigationRef.navigate('Screen2', {
param1: notification.property1,
param2: notification.property2,
});
}
}
}
},
[props.navigationRef],
);
useEffect(() => {
const unsubscribeOpenedApp = messaging().onNotificationOpenedApp(
async (remoteMessage) => {
if (remoteMessage.data) {
console.debug('User pressed notification');
redirectToScreen(remoteMessage.data);
}
},
);
const unsubscribeForegroundOpenApp = notifee.onForegroundEvent(
({ type, detail }) => {
switch (type) {
case EventType.PRESS:
console.debug('User pressed notification');
if (detail.notification && detail.notification.data) {
redirectToScreen(
detail.notification.data,
);
}
break;
}
},
);
return () => {
unsubscribeOpenedApp();
unsubscribeForegroundOpenApp();
};
}, [redirectToScreen]);
return null;
};
export default NotificationHelper;
Upvotes: 0
Reputation: 2322
Seeing as how im using the legacy react-native-firebase
I figured id post my solution for this issue, given that its slightly different than one of the above answers which utilizes RN-firebase V6. My solution is only slightly different, this solution works for notification handling with react-native-firebase
v5.x :
import * as React from 'react';
import { Text, TextInput } from 'react-native';
import AppNavigation from './src/navigation';
import { Provider } from 'react-redux';
import { store, persistor } from './src/store/index.js';
import 'react-native-gesture-handler';
import firebase from 'react-native-firebase';
import { PersistGate } from 'redux-persist/integration/react';
export default class App extends React.Component {
constructor(props) {
super(props);
if (firebase.apps.length === 0) {
firebase.initializeApp({});
}
}
async componentDidMount() {
// Initialize listener for when a notification has been displayed
this.removeNotificationDisplayedListener = firebase.notifications().onNotificationDisplayed((notification) => {
// process notification as required... android remote notif's do not have a "channel ID".
});
// Initialize listener for when a notification is received
this.removeNotificationListener = firebase.notifications().onNotification((notification) => {
// Process notification
});
// Listener for notification tap if in FOREGROUND & BACKGROUND
this.removeNotificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen) => {
// get the action trigger by the notification being opened
const action = notificationOpen.action;
// get info about the opened notification
const info = notificationOpen.notification;
// log for testing
console.log('ACTION => ' + action + '\nNOTIFICATION INFO => ' + JSON.stringify(info));
});
// Listener for notification tap if app closed
const notificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
// App was opened by notification
const action = notificationOpen.action;
const info = notificationOpen.notification;
// log for testing:
console.log('ACTION => ' + action + '\nNOTIFICATION INFO => ' + JSON.stringify(info));
}
}
componentWillUnmount() {
// Invoke these functions to un-subscribe the listener
this.removeNotificationDisplayedListener();
this.removeNotificationListener();
this.removeNotificationOpenedListener();
}
render() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<AppNavigation />
</PersistGate>
</Provider>
);
}
}
Upvotes: 0
Reputation: 221
This solution I found over the official website of Firebase and this seems to be the best example/sample work for this. Below is the sample snippet and also the link attached. Hope it help others.
import React, { useState, useEffect } from 'react';
import messaging from '@react-native-firebase/messaging';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
const Stack = createStackNavigator();
function App() {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [initialRoute, setInitialRoute] = useState('Home');
useEffect(() => {
// Assume a message-notification contains a "type" property in the data payload of the screen to open
messaging().onNotificationOpenedApp(remoteMessage => {
console.log(
'Notification caused app to open from background state:',
remoteMessage.notification,
);
navigation.navigate(remoteMessage.data.type);
});
// Check whether an initial notification is available
messaging()
.getInitialNotification()
.then(remoteMessage => {
if (remoteMessage) {
console.log(
'Notification caused app to open from quit state:',
remoteMessage.notification,
);
setInitialRoute(remoteMessage.data.type); // e.g. "Settings"
}
setLoading(false);
});
}, []);
if (loading) {
return null;
}
return (
<NavigationContainer>
<Stack.Navigator initialRouteName={initialRoute}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Link: https://rnfirebase.io/messaging/notifications#handling-interaction
Upvotes: 14
Reputation: 2560
Okay, it seems like I gotta post my own solution :)
// src/services/push-notification.js
const PushNotification = require('react-native-push-notification')
export function setupPushNotification(handleNotification) {
PushNotification.configure({
onNotification: function(notification) {
handleNotification(notification)
},
popInitialNotification: true,
requestPermissions: true,
})
return PushNotification
}
// Some notification-scheduling component
import {setupPushNotification} from "src/services/push-notification"
class SomeComponent extends PureComponent {
componentDidMount() {
this.pushNotification = setupPushNotification(this._handleNotificationOpen)
}
_handleNotificationOpen = () => {
const {navigate} = this.props.navigation
navigate("SomeOtherScreen")
}
_handlePress = () => {
this.pushNotification.localNotificationSchedule({
message: 'Some message',
date: new Date(Date.now() + (10 * 1000)), // to schedule it in 10 secs in my case
})
}
render() {
// use _handlePress function somewhere to schedule notification
}
}
Upvotes: 19