I have implemented firebase push notifications using Firebase and react-native-push-notification package.
Currently, I have implemented action buttons according to the click_action of each push notification. Once the app is in the foreground it works and once the app is killed state or background action buttons are not displaying.
My FCM helper file
class FCMServiceHelper {
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 permissions
this.deleteToken()
this.getToken(onRegister);
} else {
// User doesn't have permission
this.requestPermission(onRegister);
}
})
.catch((error) => {
console.log('[FCMService] Permission rejected ', error);
});
};
getToken = (onRegister) => {
messaging()
.getToken(undefined,'*')
.then((fcmToken) => {
if (fcmToken) {
onRegister(fcmToken);
} else {
console.log('[FCMService] User does not have a device token');
}
})
.catch((error) => {
console.log('[FCMService] getToken rejected ', error);
});
};
requestPermission = (onRegister) => {
messaging()
.requestPermission()
.then(() => {
this.deleteToken()
this.getToken(onRegister);
})
.catch((error) => {
console.log('[FCMService] Request Permission rejected ', error);
});
};
deleteToken = () => {
console.log('[FCMService] deleteToken ');
messaging()
.deleteToken(undefined,'*')
.catch((error) => {
console.log('[FCMService] Delete token error ', error);
});
};
unregisterDeviceFromNotifications = () => {
console.log('[FCMService] unreg ');
messaging()
.unregisterDeviceForRemoteMessages()
.catch((error) => {
console.log('[FCMService] Unreg device ', error);
});
};
createNotificationListeners = (
onRegister,
onNotification,
onOpenNotification,
) => {
// When the application is running, but in the background
messaging().onNotificationOpenedApp((remoteMessage) => {
console.log(
'[FCMService] onNotificationOpenedApp Notification caused app to open from background state:',
remoteMessage,
);
if (remoteMessage) {
let notification = null;
let data = remoteMessage.data;
let openFromKilling = {"checked" : true}
notification = remoteMessage.notification;
notification.data = data;
notification.checking = openFromKilling;
onOpenNotification(notification);
}
});
// When the application is opened from a quit state.
messaging()
.getInitialNotification()
.then((remoteMessage) => {
console.log(
'[FCMService] getInitialNotification Notification caused app to open from quit state:',
remoteMessage,
);
if (remoteMessage) {
let notification = null;
let data = remoteMessage.data;
let openFromKilling = {"checked" : true}
notification = remoteMessage.notification;
notification.data = data;
notification.checking = openFromKilling;
onOpenNotification(notification);
}
});
// Foreground state messages
this.messageListener = messaging().onMessage(async (remoteMessage) => {
console.log(
'[FCMService] A new FCM message arrived! foreground',
remoteMessage,
);
if (remoteMessage) {
let notification = null;
let data = remoteMessage.data;
if (Platform.OS === 'ios') {
notification = remoteMessage.notification;
} else {
notification = remoteMessage.notification;
}
notification.data = data;
onNotification(notification);
// onOpenNotification(remoteMessage.data);
}
});
// Triggered when have new token
// messaging().onTokenRefresh((fcmToken) => {
// alert('REFRESH TOKEN');
// console.log('[FCMService] New token refresh: ', fcmToken);
// onRegister(fcmToken);
// });
};
unRegister = () => {
// if(this.messageListener){
this.messageListener();
// }
};
}
My notification Handler file
fcmService.registerAppWithFCM();
fcmService.register(onRegister, onNotification, onOpenNotificaion , onAction);
localNotificationService.configure(onOpenNotificaion,onAction);
function onRegister(token) {
saveFCMToken(token);
}
if (Platform.OS == 'android') {
localNotificationService.createChannelAndroid('wapp');
}
function onNotification(notify) {
var RandomNumber = Math.floor(Math.random() * 100) + 1;
let actionData = [];
if(Platform.OS == 'android'){
if(notify.data.click_action == 'alert_dashboard'){
actionData = ["Update contact number"]
}else if(notify.data.click_action == 'account_edit'){
actionData = ["Update Email"]
}
}
const options = {
soundName: 'default',
playSound: true,
};
localNotificationService.showNotification(
RandomNumber,
notify.title,
Platform.OS == 'android' ? notify.body : notify.body,
notify,
options,
'wapp',
actionData
);
}
function onAction(notification) {
console.log ('Notification action received:');
console.log(notification.action);
console.log(notification);
}
Notification Helper File
class NotificationHelper {
configure = (onOpenNotification) => {
PushNotification.configure({
onRegister: function (token) {
console.log('[NotificationManager] onRegister token:', token.token);
},
onNotification: function (notification) {
console.log('[NotificationManager] onNotification:', notification);
if (Platform.OS === 'ios') {
if (notification.data.openedInForeground) {
notification.userInteraction = true;
}
}
if (notification.userInteraction) {
onOpenNotification(notification);
} else {
onNotification(notification);
}
if (Platform.OS === 'android') {
notification.userInteraction = true;
}
// Only call callback if not from foreground
if (Platform.OS === 'ios') {
if (!notification.data.openedInForeground) {
notification.finish('backgroundFetchResultNoData');
}
} else {
notification.finish('backgroundFetchResultNoData');
}
},
onAction: function (notification) {
// alert(notification)
console.log("ACTION:", notification.action);
console.log("NOTIFICATION:", notification);
// notification.userInteraction = true;
// PushNotification.invokeApp(notification);
},
});
};
unregister = () => {
PushNotification.unregister();
};
createChannelAndroid = (channel) => {
PushNotification.createChannel(
{
channelId: channel, // (required)
channelName: 'My channel', // (required)
channelDescription: 'A channel to categorise your notifications', // (optional) default: undefined.
playSound: false, // (optional) default: true
soundName: 'default', // (optional) See `soundName` parameter of `localNotification` function
importance: 4, // (optional) default: 4. Int value of the Android notification importance
vibrate: true, // (optional) default: true. Creates the default vibration patten if true.
},
(created) => console.log(`createChannel returned '${created}'`), // (optional) callback returns whether the channel was created, false means it already existed.
);
};
showNotification = (id, title, message, data = {}, options = {}, channel , testData) => {
PushNotification.localNotification({
/* Android Only Properties */
...this.buildAndroidNotification(
id,
title,
message,
data,
options,
channel,
testData
),
/* iOS and Android properties */
...this.buildIOSNotification(id, title, message, data, options),
/* iOS and Android properties */
title: title || '',
message: message || '',
playSound: options.playSound || true,
soundName: options.soundName || 'default',
userInteraction: true, // BOOLEAN: If the notification was opened by the user from the notification area or not
});
};
buildAndroidNotification = (
id,
title,
message,
data = {},
options = {},
channel,
testData
) => {
console.log('TEST DATA -> ',data)
return {
showWhen: true, // This is probably not needed, since default value is TRUE.
when: new Date().getTime(),
group: "wapp",
groupSummary: true,
channelId: channel,
id: id,
autoCancel: true,
largeIcon: options.largeIcon || 'ic_launcher',
smallIcon: options.smallIcon || 'ic_launcher',
bigText: message || '',
subText: title || '',
vibrate: options.vibrate || true,
vibration: options.vibration || 300,
priority: options.priority || 'high',
importance: options.importance || 'high', // (optional) set notification importance, default: high,
data: data,
actions:testData,
// invokeApp:false,
};
};
buildIOSNotification = (id, title, message, data = {}, options = {}) => {
return {
alertAction: options.alertAction || 'view',
alertBody: message || '',
category: options.category || '',
userInfo: {
id: id,
item: data,
},
};
};
cancelAllLocalNotifications = () => {
if (Platform.OS === 'ios') {
PushNotificationIOS.removeAllDeliveredNotifications();
} else {
PushNotification.cancelAllLocalNotifications();
}
};
removeDeliveredNotificationByID = (notificationId) => {
console.log(
'[LocalNotificationService] removeDeliveredNotificationByID: ',
notificationId,
);
PushNotification.cancelLocalNotifications({id: `${notificationId}`});
};
}
My app index.js file
/**
* #format
*/
import React from 'react';
import 'react-native-gesture-handler';
import { AppRegistry, LogBox, YellowBox } from 'react-native';
import App from './app/Entrypoint';
import { name as appName } from './app.json';
import { enableScreens } from 'react-native-screens';
import messaging from '#react-native-firebase/messaging';
import { backgroundGeo, checkLocationLogics } from './app/helpers/backgroundLocationTracking';
import env from 'react-native-config';
enableScreens();
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
console.log('Message handled in the background!', remoteMessage);
});
function HeadlessCheck({ isHeadless }) {
if (isHeadless) {
// App has been launched in the background by iOS, ignore
return null;
}
return <App />;
}
AppRegistry.registerComponent(appName, () => HeadlessCheck);
Try calling the showNotification method inside setBackgroundMessageHandler , this gets called when the app is not in foreground or killed state
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
console.log('Message handled in the background!', remoteMessage);
const options = {
soundName: 'default',
playSound: true,
};
const {title, body, data} = remoteMessage;
localNotificationService.showNotification(
title,
body,
data,
options,
}
});
Related
I recently purchased a Sunmi V2 device for a client, and I am trying to print something when I receive a notification from expo-notification using react-native. I have successfully implemented this in another application, but I am having difficulty getting it to work on the Sunmi V2 device.
Here is the approach I took: On my StackNavigator.js component, I have...
const { user } = useAuth();
const { registerForPushNotificationsAsync, handleNotifications, handleNotificationsResponse } = useNotifications();
useEffect(() => {
registerForPushNotificationsAsync();
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
const responseListener =
Notifications.addNotificationResponseReceivedListener(
handleNotificationsResponse
);
return () => {
if (responseListener)
Notifications.removeNotificationSubscription(responseListener)
}
}, [user])
Here is the useNotification component:
export const useNotifications = () => {
const { user } = useAuth();
const registerForPushNotificationsAsync = async () => {
if (Device.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
// console.log('Failed to get push token for push notification!')
return;
}
try {
const token = (await Notifications.getExpoPushTokenAsync()).data;
Alert.alert("TOKEN: ", token);
if (user) {
const useRef = doc(db, "users", user.uid);
if (token !== undefined) {
updateDoc(useRef, {
pushTokenExpo: token
});
}
}
} catch (error) {
Alert.alert(error)
}
} else {
if (user) {
alert('Must use physical device for Push Notifications', user.uid)
}
alert('Must use physical device for Push Notifications');
console.log('Must use physical device for Push Notifications')
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}
};
const handleNotifications = (notification: Notifications.Notification) => {
console.log('NEW NOTIFICACTION')
};
const handleNotificationsResponse = (
response: Notifications.NotificationResponse
) => {
console.log(response.notification.request.content.data)
// HERE DO WHAT YOU WHAT AFTER CLICK ON NOTIF
}
return { registerForPushNotificationsAsync, handleNotifications, handleNotificationsResponse }
}
When I run this code on my device, it either crashes the app or does not update the Firebase collection 'user'. How can I fix this issue?
I have set up the Ionic/Capacitor app to receive push notifications following the capacitor tutorial,
I went through all the tutorial successfully and I receive test notifications sent from FCM both in foreground and background
I can successfully register to a topic
I can receive notifications sent to the topic
I can receive notifications sent to the token (test mode)
Now I'm trying to send notifications from a different device,
I just created a two test button to send notification in multicast to an array of tokens and another button to send notification to a given topic
In both cases from my device I receive the notifications in foreground, but not in background
I believe is something wrong with the format I'm using to send the notifications that is not correct in case of background (I can receive those from the FCM test tool)
Client
const send = functions.httpsCallable('sendPushNotificationToUsers');
const sendToTopic = functions.httpsCallable('sendPushNotificationToTopic');
const sendNotification = useCallback(
({
to,
title,
body,
onClick
}) => {
setLoading(true);
send({
to,
title,
body,
onClick
})
.then(() => setSuccess(true))
.catch(() => setError(true))
.finally(() => setLoading(false));
}, [send],
);
const sendNotificationToTopic = useCallback(
({
topic,
title,
body,
onClick
}) => {
setLoading(true);
sendToTopic({
topic,
title,
body,
onClick
})
.then(() => setSuccess(true))
.catch(() => setError(true))
.finally(() => setLoading(false));
}, [sendToTopic],
);
Server / Functions
exports.sendPushNotificationToUsers = functions.https.onCall(
(data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called ' + 'while authenticated.',
);
}
db.collection('users_meta')
.doc(data.to)
.collection('messagingTokens')
.get()
.then(messagingTokens => {
if (messagingTokens && messagingTokens.size) {
const to = messagingTokens.docs.map(i => i.data().token);
console.log(to); // I get to this console log and the tokens are printed correctly in an array
admin.messaging().sendMulticast({
title: data.title,
body: data.body,
data: {
title: data.title,
body: data.body,
onClick: data.onClick || '',
},
tokens: to,
});
}
});
},
);
exports.sendPushNotificationToTopic = functions.https.onCall(
(data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called ' + 'while authenticated.',
);
}
admin.messaging().send({
data: {
title: data.title,
body: data.body,
onClick: data.onClick || '',
},
topic: data.topic,
});
},
);
Notifications handler
const initNative = useCallback(() => {
PushNotifications.register();
PushNotifications.requestPermission().then(result => {
if (result.granted) {
// Register with Apple / Google to receive push via APNS/FCM
PushNotifications.register();
} else {
// Show some error
}
});
PushNotifications.addListener(
'registration',
(token: PushNotificationToken) => {
const device = new DeviceUUID().get();
registerTokenToUser(device, token.value);
alert('Push registration success, token: ' + token.value);
},
);
PushNotifications.addListener('registrationError', (error: any) => {
alert('Error on registration: ' + JSON.stringify(error));
});
PushNotifications.addListener(
'pushNotificationReceived',
(notification: PushNotification) => {
alert(JSON.stringify(notification));
// this array fires correctly with the app in foreground, but nothing on the notifications tray with the app in background if sent from my send functions, works correctly if sent from FCM
},
);
// Method called when tapping on a notification
PushNotifications.addListener(
'pushNotificationActionPerformed',
(notification: PushNotificationActionPerformed) => {
alert(JSON.stringify(notification));
},
);
}, [PushNotifications, history, registerTokenToUser]);
Any suggestion?
Thanks
I've found the error myself,
for the notifications be visible in background mode the notification object needs to have "notification" key populated, that was missing in my case,
correct send function should be
exports.sendPushNotificationToUsers = functions.https.onCall(
(data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called ' + 'while authenticated.',
);
}
db.collection('users_meta')
.doc(data.to)
.collection('messagingTokens')
.get()
.then(messagingTokens => {
if (messagingTokens && messagingTokens.size) {
const to = messagingTokens.docs.map(i => i.data().token);
console.log(to); // I get to this console log and the tokens are printed correctly in an array
admin.messaging().sendMulticast({
title: data.title,
body: data.body,
notification: {
title: data.title,
body: data.body,
},
data: {
title: data.title,
body: data.body,
onClick: data.onClick || '',
},
tokens: to,
});
}
});
},
);
exports.sendPushNotificationToTopic = functions.https.onCall(
(data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called ' + 'while authenticated.',
);
}
admin.messaging().send({
notification: {
title: data.title,
body: data.body,
},
data: {
title: data.title,
body: data.body,
onClick: data.onClick || '',
},
topic: data.topic,
});
},
);
I am using react-native-notifee and react-native-firebase v11.2.0
The problem is, when I press the notification during the app is in foreground, it is not navigating to the required screen. It is working fine while my app is in background.
notification.js
import notifee, {
AndroidCategory,
AndroidImportance,
AndroidStyle,
EventType
} from '#notifee/react-native';
import messaging from '#react-native-firebase/messaging';
import { requestNotificationPermission } from '../helpers/permissions';
import * as RootNavigator from '../navigator/NavigationRef';
const createChannel = async () => {
try {
const channel = await notifee.createChannel({
id: 'default_channel',
name: 'Default Channel',
description: 'Default notification channel',
sound: 'default',
importance: AndroidImportance.HIGH,
vibration: false
});
return channel;
} catch (error) {
console.log('Error in creating notification channel', error);
return 'default_channel';
}
};
export const requestPermissionForNotifications = async () => {
const hasPermission = await messaging().hasPermission();
if (!hasPermission) {
// checking for ios notification permission;
const isGranted = await requestNotificationPermission();
if (isGranted < 1) return false;
}
// always request permisssion even if it is granted
return messaging()
.requestPermission()
.then(() => console.log('granted'))
.catch(err => console.log('error in granted', err));
};
export const displayNotification = async (
title,
body,
notificationId,
imageUrl = null
) => {
try {
// always check permisssion even if it has access to register the app to
// receive the push notification as stated in documentation
await requestPermissionForNotifications();
const channel = await createChannel();
let notificationObj = {
title,
body,
id: notificationId,
android: {
channelId: channel,
smallIcon: 'ic_launcher',
sound: 'default',
category: AndroidCategory.ALARM,
importance: AndroidImportance.HIGH,
pressAction: {
id: 'default'
},
timestamp: Date.now(),
showTimestamp: true
},
ios: {
sound: 'default',
attachments: [
{
url: imageUrl
}
]
}
};
await notifee.displayNotification(notificationObj);
} catch (error) {
console.log('Error in displaying notification', error);
}
};
export const onMessageReceived = async message => {
const { messageId, notification } = message;
return displayNotification(
notification.title,
notification.body,
messageId,
notification.image
);
};
export const notificationPressActionEventListener = async ({
type,
detail
}) => {
try {
const { navigate } = RootNavigator.navigationRef.current;
switch (type) {
case EventType.PRESS:
navigate('SampleStackScreen');
break;
default:
break;
}
} catch (error) {
console.log('Error in navigation', error);
}
return null;
};
export const getDeviceToken = async () => {
const token = await messaging().getToken();
if (token) return token;
return null;
};
NavigationRef.js
import * as React from 'react';
export const navigationRef = React.createRef();
Listeners in Home.js
componentDidMount() {
this.notificationListener = messaging().onMessage(onMessageReceived);
this.notificationActionEventListener = notifee.onForegroundEvent(
notificationPressActionEventListener
);
}
Listeners in index.js
/** #format */
import messaging from '#react-native-firebase/messaging';
import notifee from '#notifee/react-native';
import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
import App from './src/App';
import {
notificationPressActionEventListener,
onMessageReceived
} from './src/firebase/notification';
notifee.onBackgroundEvent(notificationPressActionEventListener);
messaging().setBackgroundMessageHandler(onMessageReceived);
AppRegistry.registerComponent(appName, () => App);
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'
}
})
Can't solve this problem, using react-native and redux-thunk.
I am trying to post data to firebase.io and repeatedly get the same error: Actions must be plain objects. Use custom middleware for async actions.
reducers/index.js
import { createStore, combineReducers, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
import PlacesReducer from './placesReducer'
const rootReducer = combineReducers({
places : PlacesReducer,
});
let composeEnhancers = compose;
if (__DEV__) {
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
}
const configureStore = () => {
return createStore(rootReducer, applyMiddleware(thunk));
};
export default configureStore;
actions/index.js
export const addPlace = (placeName, location, image) => {
return dispatch => {
const placeData = {
name: placeName,
location: location
};
fetch("https://awesome-places-b592c.firebaseio.com/placesReducer.json", {
method: "POST",
body: JSON.stringify(placeData)
})
.catch(err => console.log(err))
.then(res => res.json())
.then(parsedRes => {
console.log(parsedRes);
});
};
};
reducers/placesReducer.js
const initialState = {
places: []
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_PLACE":
return {
...state,
places: state.places.concat({
key: Math.random(),
name: action.placeName,
image: {
uri: action.image.uri,
location: action.location
}
})
};
default:
return state;
}
};
export default reducer;
All help is appreciated, thank you.
UPDATE
Added initialState argument to the createStore() function in reducers/index.js
const configureStore = () => {
return createStore(rootReducer, {places: []}, applyMiddleware(thunk));
};
Still receiving the same error
You need to dispatch the actions after the async request is completed
.then(parsedRes => {
console.log(parsedRes);
dispatch({
type: YOUR_REDUCER_TYPE,
parsedRes
})
});
Also as mentioned in the createStore docs, you need to add the initialState
(reducer, preloadedState, enhancer)
Try updating the code as follows:
export const addPlace = (placeName, location, image) => {
//return dispatch => {
const placeData = {
name: placeName,
location: location
};
fetch("https://awesome-places-b592c.firebaseio.com/placesReducer.json", {
method: "POST",
body: JSON.stringify(placeData)
})
.catch(err => console.log(err))
.then(res => res.json())
.then(parsedRes => {
console.log(parsedRes);
// you may tweak this
// use ...parsedRes following the reducer code you paste,
// I assume location, placeName and image are inside parsedRes
dispatch({ type:"ADD_PLACE", ...parsedRes })
});
//};
};
Then in the reducer:
const reducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_PLACE":
// check the action content
console.log(action)
return {
...state,
places: state.places.concat({
key: Math.random(),
name: action.placeName,
image: {
uri: action.image.uri,
location: action.location
}
})
};
default:
return state;
}
};