how to navigate to specific screen after pressing rnfirebase notification - android

i'm using react native firebase and i'm getting notification whenever is needed, and these notifications have some data to navigate to specific screen.
i used the firebase documentation to implement the functionality but it's not working as it's supposed to
Here is the document i've used Firebase & React-Navigation
and my code looks something like this :
const Stack = createStackNavigator();
const Router = () => {
const navigation = useNavigation();
const [loading, setLoading] = useState(true);
const [initialRoute, setInitialRoute] = useState('Splash');
useEffect(() => {
//fcm
registerAppWithFCM();
// checkRNFBPermission();
const unsubscribe = messaging().onMessage(async remoteMessage => {
console.log('remote DATAAAAAAAAAAAAAAAAAAAAAAAA : ',remoteMessage.data);
// switch (remoteMessage.data.screen) {
// case 'answer':{
// console.log('inside switch condition 1 !!!!!!!!!!!!!');
// useNavigation().navigate('Profile');
// break;
// }
// case 'AnswerQuestion':{
// console.log('inside switch condition 2 !!!!!!!!!!!!!');
// useNavigation().navigate('Profile');
// break;
// }
// default:
// break;
// }
// Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
// const owner = JSON.parse(remoteMessage.data.owner);
// const user = JSON.parse(remoteMessage.data.user);
// const picture = JSON.parse(remoteMessage.data.picture);
});
// 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('Profile');
});
// Check whether an initial notification is available
messaging()
.getInitialNotification()
.then(remoteMessage => {
if (remoteMessage) {
console.log(
'Notification caused app to open from quit state:',
remoteMessage.data, //notification
);
}
setLoading(false);
});
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);
});
return unsubscribe;
//fcm
}, []);
//fcm
checkRNFBPermission = async() => {
const enabled = await messaging().hasPermission();
if(enabled){
messaging()
.getToken()
.then(token => {
// console.log('deviceeeee fcm token ------> ', token);
});
}else{
requestUserPermission();
}
}
registerAppWithFCM = async() => {
await messaging().registerDeviceForRemoteMessages();
}
requestUserPermission = async() => {
const settings = await messaging().requestPermission();
if (settings) {
console.log('Permission settings:', settings);
}
}
//fcm
renderLoading = () => (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Domanda</Text>
<ActivityIndicator size='large' color={colors.darkerTeal} />
</View>
);
//firebase
if (loading) {
return null;
}
//firebase
return(
<Provider store={store}>
<PersistGate persistor={persistor} loading={this.renderLoading()}>
<Root>
<NavigationContainer>
<Stack.Navigator initialRouteName={initialRoute} headerMode="none">
<Stack.Screen name="Splash" component={Splash} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Main" component={Main} />
<Stack.Screen name="AppIntro" component={AppIntro} />
<Stack.Screen name="Tags" component={Tags} />
<Stack.Screen name="Answers" component={Answers} />
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="EditInfo" component={EditInfo} />
<Stack.Screen name="ChangePassword" component={ChangePassword} />
<Stack.Screen name="AnswerQuestion" component={AnswerQuestion} />
<Stack.Screen name="ContactUs" component={ContactUs} />
</Stack.Navigator>
</NavigationContainer>
</Root>
</PersistGate>
</Provider>
)
};
export default Router;
but when i add usenavigation and i want to use it it throws this error:
Error: We couldn't find a navigation object. Is your component inside a screen in a navigator?
i can not use navigation.navigate('Profile'); to navigate to a specific screen.

You're receiving the message in App.js whats outside of your StackNavigator.
You can use a ref to use the navigation property of the navigator
define the navigator in the top of you app.js
var navigator = null;
then add an ref to the navigator
<Stack.Navigator
initialRouteName={initialRoute}
headerMode="none"
ref={nav => {
navigator = nav;
}}
>
and push your route inside the receive method
navigator.dispatch(
NavigationActions.navigate({
routeName: 'theRoute',
params: {},
}),
);

Was struggling 4 hours...
Some were in component, were navigation is available (in my case "Home screen")
// last import
import { ScrollWrapper } from './styles'
export const navigationRef = React.createRef();
export const isReadyRef = React.createRef();
export function navigate(name, params) {
if (isReadyRef.current && navigationRef.current) {
// Perform navigation if the app has mounted
navigationRef.current.navigate(name, params);
} else {
console.log(' else [ELSE] --- ')
// You can decide what to do if the app hasn't mounted
// You can ignore this, or add these actions to a queue you can call later
}
}
// component start
export const SocialHomeScreen = () => {...
In App.js
import { navigate, navigationRef, isReadyRef } from './screens/PeopleAroundYou/index'
// .... navigators
const App = () => {
const [isAuth, setIsAuth] = useState(false)
AsyncStorage.getItem('pushNotify').then(value => {
console.log('value --- ', value)
console.log('JSON.parse(value) --- ', JSON.parse(value))
}).catch(error => {
console.log('error --- ', error)
})
// foreground message arrived
useEffect(() => {
return messaging().onMessage(async remoteMessage => {
const { data, notification } = remoteMessage
if (data.type === 'activity-check-in') {
console.log(' A new FCM message arrived! --- ')
console.log('data --- ', data)
console.log('notification --- ', notification)
console.log(' navigator --- ', navigate)
console.log('navigationRef.current.getRootState() --- ', navigationRef.current.getRootState())
switch (data.category) {
case 'fitness':
// navigate to nested screen
navigate(routes.Fitness, {
screen: routes.ActivityDetails,
params: { activityId: data.eventId}
})
break
case 'companionship':
navigate(routes.Companionships, {
screen: routes.ActivityDetails,
params: { activityId: data.eventId}
})
break
case 'volunteering':
navigate(routes.Volunteering, {
screen: routes.ActivityDetails,
params: { activityId: data.eventId}
})
break
case 'wellbeing':
navigate(routes.Wellbeing, {
screen: routes.ActivityDetails,
params: { activityId: data.eventId}
})
break
}
}
})
}, [])
useEffect(() => {
SplashScreen.hide()
fcmService.registerAppWithFCM()
fcmService.register(onRegister, onNotification, onOpenNotification)
localNotificationService.configure(onOpenNotification)
function onRegister(token) {
console.log('[App] onRegister: ', token)
}
function onNotification(notify) {
console.log('[App] onNotification: ', notify)
const options = {
soundName: 'default',
playSound: true, //,
// largeIcon: 'ic_launcher', // add icon large for Android (Link: app/src/main/mipmap)
// smallIcon: 'ic_launcher' // add icon small for Android (Link: app/src/main/mipmap)
}
localNotificationService.showNotification(
0,
notify.title,
notify.body,
notify,
options,
)
}
function onOpenNotification(notify) {
console.log('[App] onOpenNotification: ', notify)
Alert.alert('Open Notification: ' + notify.body)
}
return () => {
console.log('[App] unRegister')
fcmService.unRegister()
localNotificationService.unregister()
}
}, [])
const authContext = useMemo(() => {
return {
login: () => {
setIsAuth(true)
},
logout: () => {
setIsAuth(false)
},
}
})
return (
<AuthContext.Provider value={authContext}>
<ThemeProvider theme={theme}>
<NavigationContainer
ref={navigationRef}
onReady={() => {
isReadyRef.current = true
}}
linking={linking}
fallback={
<View style={{ justifyContent: 'center', alignItems: 'center' }}>
<Loader loading size='large' color='#61A5C8'/>
</View>
}
>
{isAuth ? <AuthorizedTabs /> : <NonAuthorizedStack/>}
</NavigationContainer>
</ThemeProvider>
</AuthContext.Provider>
)
}

Related

React-native fbsdknext when first logIn on android Profile.getCurrentProfile return null

Faced a problem while using react-native-fbsdk-next.
The first call to Profile.getCurrentProfile on android returns null. Everything works correctly on ios. If authorization is then called again, the user profile is returned correctly.
If you log out and then try to log in, the first attempt will return null.
Please tell me how to fix this?
const platformPermissions =
Platform.OS === 'ios'
? [['public_profile', 'email'], 'limited', 'my_nonce']
: [['public_profile', 'email']];
const accessToken = yield LoginManager.logInWithPermissions(
...platformPermissions,
).then(
async response => {
if (response.isCancelled) {
return;
}
if (Platform.OS === 'ios') {
const result = await AuthenticationToken.getAuthenticationTokenIOS();
return result?.authenticationToken;
}
if (Platform.OS === 'android') {
const result = await AccessToken.getCurrentAccessToken();
return result?.accessToken;
}
},
error => {},
);
if (accessToken) {
const currentProfile = yield Profile.getCurrentProfile(
profile => profile,
);
if (currentProfile) {
const userData =
Platform.OS === 'ios'
? {
...currentProfile,
firstName: currentProfile.name.split(' ')[0] || null,
lastName: currentProfile.name.split(' ')[1] || null,
}
: currentProfile;
const {data} = yield Axios.post(ROUTES.AUTH.FACEBOOK_SIGN, {
...userData,
accessToken,
});
yield put(setCurrentUser({user: data}));
yield put(setMethod({method: 'facebook'}));
}
}
The issue is in the library itself. PR needs to be created for the solution. Checkout the progress at https://github.com/thebergamo/react-native-fbsdk-next/issues/73
I was facing this issue with the LoginButton. I have no issue when using LoginManager with a custom Button.
My Custom Login button:
const FBLoginButton = () => {
const nav = useNavigation();
return (
<Button
title="Login with Facebook"
onPress={() => {
LoginManager.logInWithPermissions(["public_profile"]).then(
function (result) {
if (result.isCancelled) {
console.log("Login cancelled");
} else {
console.log("Success");
nav.navigate("Home"); // Go to Home screen
}
},
function (error) {
console.log("Login fail with error: " + error);
}
);
}}
/>
);
};
My Home Screen
import { Profile } from "react-native-fbsdk-next";
const HomeScreen = () => {
const [currentUser, setCurrentUser] = useState(null);
const nav = useNavigation();
useEffect(() => {
Profile.getCurrentProfile().then((profile) => {
if (profile) {
setCurrentUser(profile);
}
});
}, []);
return (
<View>
{currentUser ? (
<>
<Text>{`id: ${currentUser.userID}, name: ${currentUser.name}`}</Text>
<Button
title="Logout"
onPress={() => {
LoginManager.logOut();
nav.navigate("Login");
}}
/>
</>
) : (
<Text>Loading...</Text>
)}
</View>
);
};

firebase.auth().signOut() not redirecting back to mainNav after clicking logout

I am building a simple react native app using react native and firebase and facing a small issue when try to log out a user, currently when logout is pressed, it does not redirect back to login page.
home
<Text>Home Screen</Text>
<Button title="Sign Out" onPress={signOut} />
Signout function
const signOut = async () => {
firebase.auth().signOut();
}
mainNav
const MainNav : FC = () => {
const [user, setUser] = useState<firebase.User | null>(null);
const bootstrap = () => {
firebase.auth().onAuthStateChanged(_user => {
if(_user){
setUser(_user)
}
})
}
useEffect(() => {
bootstrap()
}, [])
return (
<NavigationContainer>
{user !== null ? <AppStack /> : <AuthStack />}
</NavigationContainer>
)
}
export default MainNav;
authstack
import React, { FC } from 'react';
import {createStackNavigator} from '#react-navigation/stack'
import {SignUp, Login} from '../screens'
const {Navigator, Screen} = createStackNavigator();
const AuthStack : FC = () => {
return (
<Navigator screenOptions={{headerShown: false}}>
<Screen name="signup" component={SignUp}/>
<Screen name="login" component={Login}/>
</Navigator>
)
}
export default AuthStack;
appstack
const AppStack : FC = () => {
return (
<Navigator screenOptions={{headerShown: false}}>
<Screen name="home" component={Home}/>
<Screen name="dashboard" component={Dashboard}/>
</Navigator>
)
}
export default AppStack;
That's because you are effectively ignoring the logout event:
const bootstrap = () => {
firebase.auth().onAuthStateChanged(_user => {
if(_user) { // <-- this will be null when logged out!
setUser(_user)
}
})
}
In addition, you shouldn't be wrapping the onAuthStateChanged function and ignoring its return value (it's unsubscribe function) like you are here:
useEffect(() => {
bootstrap()
}, [])
You should be using (or a variant of it):
useEffect(() => firebase.auth().onAuthStateChanged(_user => setUser(_user)), [])
The above line keeps user updated with the latest value from firebase.auth() and properly unsubscribes the listener when the component is unmounted.

How to create React Native Firebase v7 push notification Channel

I have install the react native firebase notificaiton ,notifications are working fine but when i try to send custom sound it wont work.
I find out that on Android version > 7 we have to create firebase channel in order to play custom sound but i dont know how to and where to create notification channel.
I have created two files inside the src folder of the root folder of my project where i handle notificatons.
1.LocalNotificationService.js
2.FcmService.js
Code for LocalNotificationService.js
import PushNotification from 'react-native-push-notification';
import {Platform} from 'react-native';
// import { notifications } from "react-native-firebase";
class LocalNotificationService {
configure = (onOpenNotification) => {
PushNotification.configure({
onRegister: function (token) {
console.log('[LocalNotificationService] onRegister', token);
},
onNotification: function (notification) {
console.log('[LocalNotificationService] onNotification', notification);
if (!notification?.data) {
return;
}
notification.userInteraction = true;
onOpenNotification(
Platform.OS === 'ios' ? notification.data.item : notification.data,
);
//Only call callback if not from foreground
if (Platform.OS === 'ios') {
notification.finish(PushNotificationIOS.FetchResult.NoData);
}
},
// 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
* - if you are not using remote notification or do not have Firebase installed, use this:
* requestPermissions: Platform.OS === 'ios'
*/
requestPermissions: true,
});
};
unregister = () => {
PushNotification: this.unregister();
};
showNotification = (id, title, message, data = {}, options = {}) => {
PushNotification.localNotification({
...this.buildAndroidNotification(id, title, message, data, options),
title: title || '',
message: message || '',
playSound: options.playSound || true,
soundName: options.soundName || 'default',
// sound:'test',
userInteracsstion: false, /// boolean if the condition was opened by the user from the notification
});
};
buildAndroidNotification = (id, title, message, data = {}, options = {}) => {
return {
id: id,
// sound:"test",
autoCancel: true,
largeIcon: options.largeIcon || 'icon',
smallIcon: options.smallIcon || 'test_icon',
bigText: message || '',
subText: title || '',
color: 'green',
vibrate: options.vibrate || true,
vibration: options.vibration || 1000,
priority: options.priority || ' high',
importance: options.importance || 'high', // (options) set notification importance , default high
// data : data,
};
};
cancelAllLocalNotification = () => {
if (Platform.OS === 'ios') {
PushNotificationIOS.removedAllDeliveredNotification();
} else {
PushNotification.cancelAllLocalNotification();
}
};
removedDeliveredNotificationById = (notificationId) => {
console.log(
'[LocalNotificationService] removeDeliveredNotificationById: ',
notificationId,
);
PushNotification.cancelLocalNotfication({id: `${notificationId}`});
};
}
export const localNotificationService = new LocalNotificationService();
Code for FcmService.js
import messaging from '#react-native-firebase/messaging'
// import type {Notification,NotificationOpen} from 'react-native-firebase'
import { Platform } from 'react-native'
class FCMService {
register = (onRegister,onNotification,onOpenNotification) => {
this.checkPermission(onRegister)
this.createNotificationListeners(onRegister,onNotification,onOpenNotification)
}
registerAppWithFCM = async() => {
if(Platform.OS === "ios"){
await messaging().registerDeviceForRemoteMessages();
await messaging().setAutoInitEnabled(true)
}
}
checkPermission = (onRegister) => {
messaging().hasPermission()
.then(enabled => {
if(enabled) {
//User has permission
this.getToken(onRegister)
}
else{
//User Dont have permission
this.requestPermission(onRegister)
}
}).catch(error => {
console.log("Permission Rejected",error)
})
}
getToken = (onRegister) => {
messaging().getToken()
.then(fcmToken => {
if(fcmToken) {
onRegister(fcmToken)
}
else{
console.log("User Dont ave a Device Token")
}
}).catch(error => {
console.log("get token rejected", error)
})
}
requestPermission = (onRegister) => {
messaging().requestPermission()
.then(() => {
this.getToken(onRegister)
}).catch(error => {
console.log("Request Permission Rejected",error)
})
}
deleteToken = () => {
console.log("Delete Token")
messaging().deleteToken()
.catch(error => {
console.log("Delete token error",error)
})
}
createNotificationListeners = (onRegister, onNotification,onOpenNotification) => {
messaging().onNotificationOpenedApp(remoteMessage => {
console.log("onNotificationOpenedApp notification caused to open app")
if(remoteMessage){
const notification = remoteMessage.notification
onOpenNotification(notification)
}
});
//When the application is opened from a quite state.
messaging().getInitialNotification().then(remoteMessage => {
console.log("getInitialNotification notification caused to open app")
if(remoteMessage){
const notification = remoteMessage.notification
onOpenNotification(notification)
}
})
//Foreground state messages
this.messageListner = messaging().onMessage(async remoteMessage => {
console.log("A new FCM Message Arrived",remoteMessage)
if(remoteMessage){
let notification = null
if(Platform.OS === "ios") {
notification = remoteMessage.data.notification
}else{
notification = remoteMessage.notification
}
onNotification(notification)
}
})
}
unRegister = () => {
}
export const fcmService = new FCMService()
App.js
import React , {useEffect,useState} from 'react'
import {View,StyleSheet,Text,Button,TextInput} from 'react-native'
import {fcmService} from './src/FCMService'
import {localNotificationService} from './src/LocalNotificationService'
import auth from '#react-native-firebase/auth';
export default function App() {
useEffect(() => {
fcmService.registerAppWithFCM()
fcmService.register(onRegister,onNotification,onOpenNotification)
localNotificationService.configure(onOpenNotification)
function onRegister(token) {
console.log("[App] onRegister: ",token)
setTokenwa(token)
}
function onNotification(notify) {
console.log("[App] onNotification: ",notify)
const options = {
soundName : 'test',
playSound : true
}
localNotificationService.showNotification(
0,
notify.title,
notify.body,
notify,
options
)
}
function onOpenNotification(notify) {
console.log("[App] onOpenNotification : " , notify)
alert("Open Notification" + notify.body)
}
return () => {
console.log("[App] unRegister")
fcmService.unRegister()
localNotificationService.unregister()
}
}, [])
const [confirm, setConfirm] = useState(null);
const [code, setCode] = useState('');
const [tokenwa, setTokenwa] = useState('');
// Handle the button press
async function signInWithPhoneNumber(phoneNumber) {
const confirmation = await auth().signInWithPhoneNumber(phoneNumber);
setConfirm(confirmation);
}
async function confirmCode() {
try {
console.log("code send")
await confirm.confirm(code);
} catch (error) {
console.log('Invalid code.');
}
}
if (!confirm) {
return (
<Button
title="Phone Number Sign In"
onPress={() => signInWithPhoneNumber('+91 7769948296')}
/>
);
}
return (
<>
<Text>{tokenwa}</Text>
<View style={styles.container}>
<Text>Sample React Native Firebase </Text>
<Button title="Press Me" onPress={() => localNotificationService.cancelAllLocalNotification}></Button>
<TextInput value={code} placeholder="test" onChangeText={text => setCode(text)} />
<Button title="Confirm Code" onPress={() => confirmCode()} />
</View>
</>
)
}
const styles = StyleSheet.create({
container : {
flex : 1,
alignItems : 'center',
justifyContent : 'center'
}
})

Passing navigation params in react-navigation to component's methods

Trying to figure out how params are passed in react-navigation. Once a user selected an option from the Filter using the left header button, loadItems(filter) should be called with that filter as a parameter. How do I catch such event?
export default class FavoritesView extends Component {
static navigationOptions = ({navigation}) => ({
headerLeft: (
<Button
onPress={()=>{FavoritesView.showFilteringMenu(navigation)}}
title="Filter"
/>
),
});
static showFilteringMenu(navigation) {
let FILTERS = [
'A',
'B',
'C'
];
ActionSheet.showActionSheetWithOptions({
title: "Filter options",
options: FILTERS
},
(buttonIndex) => {
navigation.setParams({
selectedOption: FILTERS[buttonIndex]
}); // A parameter is set here
});
}
loadItems(filter) { // This function should be called
StorageService.getItems(filter).then(v => this.setState({ data: v }));
}
render() {
let {navigation} = this.props;
return (
<SafeAreaView style={styles.container}>
<NavigationEvents
onWillFocus={payload => this.loadItems()} // This works only for initial load
/>
</SafeAreaView>
);
}
}
Here is how I solved it using navigation.getParam() and navigation.setParams().
export default class FavoritesView extends Component {
static navigationOptions = ({navigation}) => ({
headerLeft: (
<Button
onPress={navigation.getParam('showFilteringMenu')}
title="Filter"
/>
),
});
static showFilteringMenu() {
let FILTERS = [
'A',
'B',
'C'
];
ActionSheet.showActionSheetWithOptions({
title: "Filter options",
options: FILTERS
},
(buttonIndex) => {
this.selectedFilter = FILTERS[buttonIndex];
this.loadItems(this.selectedFilter);
});
}
componentDidMount() {
this.props.navigation.setParams({
showFilteringMenu: this._showFilteringMenu.bind(this)
});
}
loadItems(filter) { // This function should be called
StorageService.getItems(filter).then(v => this.setState({ data: v }));
}
render() {
let {navigation} = this.props;
return (
<SafeAreaView style={styles.container}>
<NavigationEvents
onWillFocus={payload => this.loadItems()} // This works only for initial load
/>
</SafeAreaView>
);
}
}

React Native FlatList is not rendring an Axios API request

I am new to react native , and I am facing a problem with handling props and state ,when i am using redux, I get the data needed for rendering the flat list in the right form but some how the data property inside the flat list only see {this.props.customers} as undefined.
Here is my code:
componentWillMount() {
debugger;
this.props.getCustomers();
debugger;
}
componentDidUpdate(prevProps) {
if (prevProps.customers !== this.props.customers) {
this.props.getCustomers();
}
}
render = () => {
return (
<View>
<FlatList
data={this.props.customers}
renderItem={({ item }) =>
<View style={styles.GridViewContainer}>
<Text style={styles.GridViewTextLayout}>{item.name}</Text>
</View>}
keyExtractor={(x, index) => index}
numColumns={3}
/>
</View>
);
}
}
const mapStateToProps = (state) => {
const customers = state.customers;
console.log(customers);
debugger
return customers;
};
export default connect(mapStateToProps, {
getCustomers
})(CustomersList);
And the getCustomers action :
export const getCustomers = () => {
debugger;
return (dispatch) => {
dispatch(setCustomerLoading)
axios
.get('https://calm-sands-26165.herokuapp.com/api/customers')
.then(res =>
dispatch({
type: GET_CUSTOMERS,
payload: res.data,
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: null
})
);
};
}
Thanx in advance.
In mapStateToProps you should return an object, not a value. Each entry in that object will be a prop for the component that's being connected to the store.
In your case, this should be the fix:
const mapStateToProps = (state) => {
const customers = state.customers;
return { customers };
};

Categories

Resources