Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

route when push notification is opened #742

Closed
ddeanto opened this issue Mar 20, 2017 · 9 comments
Closed

route when push notification is opened #742

ddeanto opened this issue Mar 20, 2017 · 9 comments

Comments

@ddeanto
Copy link

ddeanto commented Mar 20, 2017

I want to route the user to a specific screen when they open a push notification. I don't understand how to do this because it seems I can't pass the navigation prop to myhighest order component.

For example, I want to do something like this: http://stackoverflow.com/questions/42359027/react-native-render-a-page-on-push-notification

@ddeanto
Copy link
Author

ddeanto commented Mar 20, 2017

The solution may involve this https://reactnavigation.org/docs/navigators/#Calling-Navigate-on-Top-Level-Component

@raubas
Copy link

raubas commented Mar 20, 2017

I use redux, and have a handler for the push-notification which triggers a navigation-action, works pretty smooth - although the correct view is pushed on the stack when the app is opened (would be neat to have it rendered initially) but works brilliantly for my use-case.

@ddeanto
Copy link
Author

ddeanto commented Mar 20, 2017

@raubas Where is the handler implemented? On your top level component? If so, I am confused how to use navigationActions from the top level component since I do not know how to dispatch them without access to a screen's navigation prop. Am I being stupid lol?

@ddeanto ddeanto closed this as completed Mar 21, 2017
@raubas
Copy link

raubas commented Mar 23, 2017

@ddeanto I have it set up in my root views component did mount where I register for notifications and Deep-links (using branch.io)

Basically I have a onNotification function which extracts all the params needed from the notification, and call:

this.props.dispatch( handleDeepLink(pushparams) )

Then I have som actions defined for handling deep links and navigation,


export function handleDeepLink( pushparams, uri ){
	return async dispatch => {
                // Some additional logic here ofc..
                let { routeName, params } = pushparams
		dispatch( navigate(routeName, params) )
	}
}

export function navigate(routeName, params = {}) {
	return {
		type: NavigationActions.NAVIGATE,
		routeName,
		params
	}
}

@dzpt
Copy link

dzpt commented Aug 20, 2017

@raubas If you use navigate or dispatch reset action. it might redirect to the new screen, not specified route we wanted.

i've tried with this code:

const resetAction = NavigationActions.reset({
			index: 1,
			key: null,
			actions: [
				NavigationActions.navigate({ routeName: 'Home' }),				
				NavigationActions.navigate({ 
					routeName: 'Main',
					params: {changeToTab: 'Tab2'}
				})
			]
		})
		this.navigator.dispatch(resetAction)

My schema:

StackNavigator

  • Home (view component)
  • Main (TabNavigator)
    • Tab1
    • Tab2

Now i want to navigate to Main (TabNavigator) and change to Tab2
It works if my app at Home, but if i already in Main or somewhere else, it will stack to new screen.
It would break the navigation flow.

@dengue8830
Copy link

dengue8830 commented Jul 11, 2018

i fixed with this

navigator: any;

constructor(props: Props) {
    super(props);
    this.onPressNotificacion = this.onPressNotificacion.bind(this); // you need this to use 'this' on 'onPressNotificacion'
    firebase.notifications().onNotificationOpened(this.onPressNotificacion);
  }

onPressNotificacion() {
  this.navigator.dispatch(NavigationActions.navigate({ routeName: 'somescreen', params: someParams }));
}

render() {
    return (
        <AppNavigator ref={nav => { this.navigator = nav; }} />
    );
  }

more info:
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
#71

@johnculviner
Copy link

johnculviner commented Jul 17, 2018

This has been a simple relatively bulletproof way of handling push notifications that work in a variety of circumstances with "react-navigation" and "react-native-push-notification'" ex:

  • The app hasn't isn't loaded yet and user clicks a push notification (the custom Deferred allows the push notification to be navigated from as soon as the navigation object is available which happens async from receiving the push in "onNotification" below)
  • The app is loaded and in the foreground a toast comes up that the user can click to navigate to the correct screen

This also navigates to a relatively nested route (a stack navigator in a switch navigator and resets the navigation hierarchy correctly under the above circumstances)

const SwitchNavigator = createSwitchNavigator(
  {
    Main: {
      screen: mainStackNavigator,
      path: 'main',
    },
  },
  {
    initialRouteName: 'Main',
  }
)

const navigationDeferred = new Deferred()

PushNotification.configure({
  onRegister: function(token) {}, //TODO associate with the user logged in user

  onNotification: notification => {
    console.log('notification received', notification)
    navigationDeferred.promise.then(
      navigation =>
        notification.foreground
          ? // toast the message as to not remove the user from what they are doing
            // they can click it to go to the target or click the x to dismiss
            showToast(navigation, notification)
          : // wait until we have a navigation object available to actually navigate
            // which happens if the app is just starting
            navigateFromPushNotification(navigation, notification.data)
    )

    notification.finish(PushNotificationIOS.FetchResult.NoData)
  },

  senderID: 'YOUR GCM SENDER ID', // ANDROID ONLY: GCM Sender ID
  popInitialNotification: true,
  requestPermissions: true, //todo turn off and call PushNotificationsHandler.requestPermissions() later
})

function navigateFromPushNotification(navigation, notificationData) {
  const config = notificationScreenConfig[notificationData.notificationTypeCode]
  if (!config) return

  const resetAction = StackActions.reset({
    index: 2,
    actions: [
      NavigationActions.navigate({ routeName: 'Home' }),
      NavigationActions.navigate({ routeName: config.parentScreen }),
      NavigationActions.navigate({ routeName: 'Notification', params: { notification: notificationData } }),
    ],
  })
  navigation.dispatch(resetAction)
}

function Deferred() {
  this.promise = new Promise((resolve, reject) => {
    this.reject = reject
    this.resolve = resolve
  })
}

export default class App extends React.Component {
  render() {
    return <SwitchNavigator ref={navigatorRef => navigationDeferred.resolve(navigatorRef)} />
  }
}

@zashishz
Copy link

Using @johnculviner response i have added following code into my App.js for handling navigations from push notification.
which will wait for navigation object to be available inside notificationOpen method.

Also for NavigationService file please refer here.

import React, { Component } from "react";
import Nav from "./src/routes";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import store, { persistor } from "./src/redux";
import firebase from "react-native-firebase";

import NavigationService from './src/utils/helpers/NavigationService';

const Deferred = () => {
  var d = {};
  d.promise = new Promise(function(resolve, reject) {
      d.resolve = resolve;
      d.reject = reject;
  });
  return d;
};

const navigationDeferred = new Deferred()

class App extends Component {
  
  componentDidMount() {
    this.createNotificationListeners();
  }

  async createNotificationListeners() {
    /*
     * Triggered when a particular notification has been received in foreground
     * */
    this.notificationListener = firebase
      .notifications()
      .onNotification(notification => {
        NavigationService.navigate(notification._data.url);
      });

    /*
     * If your app is in background, you can listen for when a notification is clicked / tapped / opened as follows:
     * */
    this.notificationOpenedListener = firebase
      .notifications()
      .onNotificationOpened(async (notificationOpen) => {
        NavigationService.navigate(notificationOpen.notification._data.url);
      });

    /*
     * If your app is closed, you can check if it was opened by a notification being clicked / tapped / opened as follows:
     * */
    const notificationOpen = await firebase
      .notifications()
      .getInitialNotification();
    if (notificationOpen) {
      navigationDeferred.promise.then(() => {
        NavigationService.navigate(notificationOpen.notification._data.url);
      });
    }
    /*
     * Triggered for data only payload in foreground
     * */
    this.messageListener = firebase.messaging().onMessage(message => {
      //process data message
      console.log(JSON.stringify(message));
    });
  }
  
  render() {
    return (
      <Provider store={store}>
        <PersistGate persistor={persistor}>
          <Nav ref={navigatorRef => {
            NavigationService.setTopLevelNavigator(navigatorRef);
            navigationDeferred.resolve(navigatorRef);
        }} />
        </PersistGate>
      </Provider>
    );
  }
}

export default App;

@Vicky-SilverSky
Copy link

@zashishz can you please attatch your all code releted other files for better understanding.

thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants