I'm currently working on a Android mobile App.
It's a kitchen recipes app. The app will send notification to the user during the day.
In the settings of the app, the user can choose how many and at what time he will receive the notifications (11 am to 7 pm for example)
This is where the problem begins;
I use the react-native-push-notification library with the following code:
static LocalNotif(string)
{
PushNotification.localNotification({
vibrate: true, // (optional) default: true
vibration: 300, // vibration length in milliseconds, ignored if vibrate=false, default: 1000
title: "VĂ©rifier vos produit", // (optional)
message: string, // (required)
largeIcon: "ic_launcher",
smallIcon: "ic_notification",
});
}
Next, I use the react-native-background-fetch to send a notification, even if the app is not running
static async backFetch(delay_to_next_notif)
{
BackgroundFetch.configure({
minimumFetchInterval: 3600
}, async (taskId) => {
// This is the fetch-event callback.
console.log("[BackgroundFetch] taskId: ", taskId);
// Use a switch statement to route task-handling.
switch (taskId) {
case 'com.foo.customtask':
this.LocalNotif("test")
break;
default:
console.log("Default fetch task");
}
// Finish, providing received taskId.
BackgroundFetch.finish(taskId);
});
// Step 2: Schedule a custom "oneshot" task "com.foo.customtask" to execute 5000ms from now.
BackgroundFetch.scheduleTask({
taskId: "com.foo.customtask",
forceAlarmManager: true,
delay: delay_to_next_notif// <-- milliseconds
});
}
The use of react-native-background-fetch is very strange. Sometime I never receive the notification.
Is it possible to use a push notification library and create a routine so that the user receives notifications at specific times during the day, even if the app is not running?
You can use Pushnptification.configure method and set your state if your app is in forground or background something like this
async componentDidMount() {
await this.requestUserPermission();
PushNotification.configure({
onNotification: (notification) => {
console.log('NOTIFICATION', notification);
if (notification.foreground === false) {
console.log('app is in background')
}
this.setState({
msg: notification.message.body
? notification.message.body
: notification.message,
forground: notification.foreground,
});
},
});
}
and in your return u can do something like this
{this.state.forground === true
? showMessage({
message: this.state.msg,
backgroundColor: '#1191cf',
type: 'default',
duration: 10000,
icon: 'success',
onPress: () => {
console.log('app is in forground')
},
})
: null}
Related
In my React Native app, I'm using react-native-geolocation-service to track location changes. In iOS, the background location tracking works perfectly just by following these instructions. The problem arises in Android which causes the tracking to stop or work randomly when the app goes into background.
Let me emphasize that I don't want the location to be tracked when the app is fully closed. I ONLY want the tracking to work when the app is in the foreground (active) and background states.
I've followed the instructions given in the package's own example project to configure and start the tracking service and just like them I use react-native-foreground-service.
This is the function responsible for tracking the user location with the watchPosition method of Geolocation:
// Track location updates
export const getLocationUpdates = async (watchId, dispatch) => {
// Check if app has permissed
const hasPermission = await hasLocationPermission();
// Show no location modal and return if it hasn't
if (!hasPermission) {
dispatch(setAvailability(false));
return;
}
// Start the location foreground service if platform is Android
if (Platform.OS === 'android') {
await startForegroundService();
}
// Track and update the location refernce value without re-rendering
watchId.current = Geolocation.watchPosition(
position => {
// Hide no location modal
dispatch(setAvailability(true));
// Set coordinates
dispatch(
setCoordinates({
latitude: position?.coords.latitude,
longitude: position?.coords.longitude,
heading: position?.coords?.heading,
}),
);
},
error => {
// Show no location modal
dispatch(setAvailability(false));
},
{
accuracy: {
android: 'high',
ios: 'best',
},
distanceFilter: 100,
interval: 5000,
fastestInterval: 2000,
enableHighAccuracy: true,
forceRequestLocation: true,
showLocationDialog: true,
},
);
};
And this is how the foreground service of react-native-foreground-service is initialized:
// Start the foreground service and display a notification with the defined configuration
export const startForegroundService = async () => {
// Create a notification channel for the foreground service
// For Android 8+ the notification channel should be created before starting the foreground service
if (Platform.Version >= 26) {
await VIForegroundService.getInstance().createNotificationChannel({
id: 'locationChannel',
name: 'Location Tracking Channel',
description: 'Tracks location of user',
enableVibration: false,
});
}
// Start service
return VIForegroundService.getInstance().startService({
channelId: 'locationChannel',
id: 420,
title: 'Sample',
text: 'Tracking location updates',
icon: 'ic_launcher',
});
};
And this is how it's supposed to stop:
// Stop the foreground service
export const stopLocationUpdates = watchId => {
if (Platform.OS === 'android') {
VIForegroundService.getInstance()
.stopService()
.catch(err => {
Toast.show({
type: 'error',
text1: err,
});
});
}
// Stop watching for location updates
if (watchId.current !== null) {
Geolocation.clearWatch(watchId.current);
watchId.current = null;
}
};
The way I start the tracking is just when the Map screen mounts:
const watchId = useRef(null); // Location tracking reference value
const dispatch = useDispatch();
// Start the location foreground service and track user location upon screen mount
useMemo(() => {
getLocationUpdates(watchId, dispatch);
// Stop the service upon unmount
return () => stopLocationUpdates(watchId);
}, []);
I still haven't found a way to keep tracking the location when the app goes into background and have become frustrated with react-native-foreground-service since its service won't stop even after the app is fully closed (The problem is that the cleanup function of useMemo never gets called upon closing the app).
I have heard about react-native-background-geolocation (The free one) but don't know if it will still keep tracking after closing the app (A feature I DON'T want) and am trying my best not to use two different packages to handle the location service (react-native-background-geolocation and react-native-geolocation-service).
Another option would be Headless JS but even with that I'm not quite sure if it would stop tracking after the app is closed.
I welcome and appreciate any help that might guide me to a solution for this frustrating issue.
Hey guys so I have been trying to implement push notifications to my react-native project for almost 2 weeks now. The idea is if the person is not in the chat room(chat room is present in the app) then the other user's message Is sent via push notification and stored to local storage on the receivers device.
I implemented the push notification service through firebase since literally everyone said its super easy etc. My problem comes when I want to dispatch the notification to my reducer etc using React-Redux when the notification comes in a quit state. I am able to save the message to local storage thanks to redux and persisting storage but when the app is not open im not sure how to achieve this.
Any guides and help would be appreciated!
*PS I even shifted my whole provider, reducer etc to my index.js file so that
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);
dispatch({
type: 'save_message',
data: JSON.parse(remoteMessage.data.message)
})
});
can have access to the provider to save the message but that only works when the app is in background and not when its in a quit state. Also I am using #react-native-firebase/messaging v7.8.3 and #react-native-firebase/app v8.4.1
USING REDUX IN REACT NATIVE PUSH NOTIFICATION
-->App.js
import { Configure } from './config/NotificationHandler'
const App = () => {
return (
<SafeAreaProvider>
<StatusBar barStyle="dark-content" hidden={false} backgroundColor="#1A1A1A" translucent={true} />
<Provider store={store} >
<Configure />
<View style={{ flex: 1 }} >
<AuthLoading />
</View>
</Provider>
</SafeAreaProvider>
)
}
export default App;
-->Notificationhandler.js
import React, { useEffect } from 'react';
import PushNotificationIOS from "#react-native-community/push-notification-ios";
import PushNotification from 'react-native-push-notification';
import AsyncStorage from '#react-native-community/async-storage';
import NavigationService from '../routing/NavigationService'
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
const Configure = () => {
const { activeProject } = useSelector(state => ({
activeProject: state.homeReducer.activeProject,
}), shallowEqual);
const dispatch = useDispatch();
// Must be outside of any component LifeCycle (such as `componentDidMount`).
PushNotification.configure({
// (optional) Called when Token is generated (iOS and Android)
onRegister: function (token) {
console.log("RNPNonRegistertoken:", token);
AsyncStorage.setItem('fcmToken', token.token);
},
// (required) Called when a remote is received or opened, or local notification is opened
onNotification: function (notification) {
console.log("NOTIFICATION:", notification, activeProject);
// process the notification
if (notification?.data?.url) {
NavigationService.navigate('PDFScreen', { Key: 'URL', urlForPDF: notification.data.url })
} else if (notification.id > 0 && notification.id < 7 && global.notifNavVar) {
global.localPushID = notification.id
NavigationService.navigate('AllTimersButton')
} else if (notification.id == 7 && global.notifNavVarP) {
NavigationService.navigate('ProjectDetail')
}
// (required) Called when a remote is received or opened, or local notification is opened
notification.finish(PushNotificationIOS.FetchResult.NoData);
},
// (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
onAction: function (notification) {
console.log("ACTION:", notification.action);
console.log("NOTIFICATION:", notification);
// process the action
},
// (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
// onRegistrationError: function(err) {
// console.error(err.message, err);
// },
// IOS ONLY (optional): default: all - Permissions to register.
permissions: {
alert: true,
badge: true,
sound: true,
},
largeIcon: "ic_launcher",
smallIcon: "ic_launcher",
// 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'
*/
});
return null
};
const LocalNotificationSchedule = (id, afterSec, message = '', title = '') => {
PushNotification.localNotificationSchedule({
//... You can use all the options from localNotifications
id: id + '',
title,
message, // (required)
date: new Date(Date.now() + afterSec * 1000), // in n secs
playSound: true,
// soundName: 'local_notification_custom_tone.mp3',
vibrate: true,
vibration: 180000,
allowWhileIdle: true,
visibility: "public",
// soundName: 'default',
showWhen: true,
usesChronometer: true,
ignoreInForeground: false,
priority: "max",
})
}
const CancelLocalNotifications = (id) => {
PushNotification.cancelLocalNotifications({ id: id + '' })
}
// const LocalNotification = () => {
// PushNotification.localNotification({
// id: 0, // (optional) Valid unique 32 bit integer specified as string. default: Autogenerated Unique ID
// autoCancel: true,
// bigText: 'This is local notification demo in React Native app. Only shown, when expanded.',
// subText: 'Local Notification Demo',
// title: 'Local Notification Title',
// message: 'Expand me to see more',
// vibrate: true,
// vibration: 300,
// playSound: true,
// soundName:'default',
// actions: '["Yes", "No"]'
// })
// }
export {
Configure,
LocalNotificationSchedule,
CancelLocalNotifications,
// LocalNotification
};
In my React Native app I dispatch to the Redux store when the app is killed by using the vanilla Redux API as setBackgroundMessageHandler is not a React component or hook which don't have access to the Redux provider:
setBackgroundMessageHandler.js:
import store from '../../redux/store';
const setBackgroundMessageHandler = async (remoteMessage) => {
store.dispatch({
type: 'save_message',
data: JSON.parse(remoteMessage.data.message)
})
}
Your data should be safely dispatched from the notification message to the store ready to use once the app loads.
Tried react-native-background-fetch and react-native-background-job both libraries does not run the code when app is closed, followed the documentation clearly
react-native-background-fetch job runs when custom event created using
when app is in foreground
adb shell cmd jobscheduler run -f com.bg_fetch 999
, also periodic event is not triggered after 15 min(as given in config)
For react-native-background-job job does not run even in background
Tested on android-9
componentDidMount() {
// Configure it.
BackgroundFetch.configure({
minimumFetchInterval: 15, // <-- minutes (15 is minimum allowed)
// Android options
stopOnTerminate: false,
startOnBoot: true,
requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY, // Default
requiresCharging: false, // Default
requiresDeviceIdle: false, // Default
requiresBatteryNotLow: false, // Default
requiresStorageNotLow: false, // Default
enableHeadless:true,
}, async() => {
console.log('[BackgroundFetch HeadlessTask] start');
let response = await fetch('http://431c21b5.ngrok.io/');
let responseJson = await response.json();
console.log('[BackgroundFetch HeadlessTask] response: ', responseJson);
BackgroundFetch.finish(BackgroundFetch.FETCH_RESULT_NEW_DATA);
}, (error) => {
console.log("[js] RNBackgroundFetch failed to start");
});
// Optional: Query the authorization status.
BackgroundFetch.status((status) => {
switch(status) {
case BackgroundFetch.STATUS_RESTRICTED:
console.log("BackgroundFetch restricted");
break;
case BackgroundFetch.STATUS_DENIED:
console.log("BackgroundFetch denied");
break;
case BackgroundFetch.STATUS_AVAILABLE:
console.log("BackgroundFetch is enabled");
break;
}
});
}
Job is supposed to run when app is in background or closed,
Headless job is getting killed after i close the app,
you need to put inside your index.js file your headlessTask
https://github.com/transistorsoft/react-native-background-fetch#config-boolean-enableheadless-false
I am using ReactJs to build a chat application with pubnub. The issue is my first message is sent 1 time, second is send twice and third is sent 3 times. What could be wrong, Here is my code. Do I need to unsubscribe at some point? The message is not just shown multiple times, it is actually sent.
class App extends Component {
constructor(props) {
super(props);
this.pubnub = new PubNub({
publishKey: 'demo',
subscribeKey: 'demo',
});
this.sendMessage = this.sendMessage.bind(this);
}
sendMessage() {
this.pubnub.publish({
channel: "simple-chat",
message: {
text: this.state.currentMesage,
sender: this.pubnub.getUUID()
}
});
this.setState({
currentMesage: ''
});
this.pubnub.subscribe({
channels: ['simple-chat'],
withPresence: true
});
this.pubnub.addListener({
message: (evt) => {
console.log('you have got a message:' + JSON.stringify(evt));
this.state.messages.push({
text: evt.message.text
})
this.setState({
messages: this.state.messages
});
}
});
}
export default App;
I believe that it's not a duplication of publish.
I think that you are 're-subscribing' to the same channel over and over again... while publishing 'again and again', so with every new subscribe you are receiving all of the messages that were published to that channel (like History)
So,
1st publish->subscribe-> 1 message
2nd publish->subscribe-> 2 messages
3rd publish->subscribe-> 3 messages
And so on...
Your PubNub init should be global and/or better management of the Subscribe/Publish calls.
Please use the UUID and set it with a static value
Best,
Avi.
I am using react-native with socket.io to send and receive contact requests but my code is only emitting to the emitter and to no one else.
This is the server side:
users = []; // Each time a new user joins the server they are saved in this array
socket.on('create connection', function(data, callback) {
if(data.receiverId in users) { // If the user you want to add is online then callback is true else callback is false
// The underneath line is the one that I will be using but nothing happend
//io.sockets.in(data.receiverId).emit('save room', data);
// So I created this one to see if I actually was emitting something
socket.emit('save room', data); // I found out that the emitting was working but only with the emitter
callback(true);
}else
callback(false);
});
So my conclusion is that my server side is correct and the problem lies on my client side
This is my client side:
constructor() {
this.socket = SocketIOClient('http://192.168.15.4:8000');
this.socket.on('save room', function (data) { // This is where the server calls the emit. It was at first inside the connect function but I moved it to the constructor to see if that way all clients could get it, results are the same
Alert.alert(
'Accept Connection?',
'User: '+data.emitterId+' sent you a connection request',
[
{
text: 'Accept',
onPress: () => {},
},
{
text: 'Refuse',
onPress: () => {},
},
],
{cancellable: false}
);
})
}
#action connect(data, callback1) { // this access the function we had previously in the server
this.socket.emit('create connection', data, function (callback2) {
if(!callback2)
Alert.alert(
'User Offline',
'This user is currently offline try again later',
[
{
text: 'OK',
onPress: () => {callback1(false)},
},
],
{cancellable: false}
);
else {
callback1(true)
}
});
}
I think the problem lies on where I place the this.socket.on('save room') function, but in react-native I really do not know where to put it.
Thanks to #H. Tugkan kibar and #azium I realized that the problem was on my server side the correct way to emit to a specific user is like this:
socket.broadcast.to().emit();