Using OneSignal.registerForPushNotifications, then approve not registering - react-native

I am user OneSignal in combination with React-Native to setup the push notifications. In my app, users need to be logged in, in order receive push notifications (or to give permission).
The issue that I am having, is that when the user logs in, they get my custom popup, asking if they would like to receive messages. If they click yes, they iOS popup shows, asking for permission.
When I click yes again, and check the OneSignal users dashboard, to see which users have registered for the push notifications. I see mine, but as opted out, which is not correct.
So I am wondering if there is a bug in the code of OneSignal (already in contact with them, but is going slow) or my code is wrong.
I am using;
React-native 0.56
react-native-onesignal: 3.2.7
The following code is what I currently have:
componentWillMount() {
OneSignal.init("{key}", {kOSSettingsKeyAutoPrompt : false});
AsyncStorage.multiGet(['firstTime', 'subscribed']).then((response) => {
this.setState({
firstTime: JSON.parse(response[0][1]) != null ? JSON.parse(response[0][1]) : true,
subscribed: JSON.parse(response[1][1]) != null ? JSON.parse(response[1][1]) : false
});
setTimeout(() => {
if (Platform.OS === 'android' && !this.state.subscribed && this.state.firstTime) {
OneSignal.setSubscription(false);
}
}, 200);
});
OneSignal.addEventListener('received', this.onReceived);
OneSignal.addEventListener('opened', this.onOpened);
OneSignal.addEventListener('ids', this.onIds);
}
componentDidMount () {
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivityChange);
AsyncStorage.getItem('firstTime').then((result) => {
!this.isCancelled && this.setState({
firstTime: JSON.parse(result) != null ? JSON.parse(result) : true
})
});
firebase.auth().onAuthStateChanged((user) => {
if (user) {
setTimeout(() => {
if (!this.state.subscribed && this.state.firstTime) {
Alert.alert(
I18n.t('notifications title'),
I18n.t('notifications subtitle'),
[
{text: I18n.t('notifications no'), onPress: () => {
setTimeout(() => {
AsyncStorage.multiSet([['firstTime', JSON.stringify(false)], ['subscribed', JSON.stringify(false)]]);
}, 100)
}, style: 'cancel'},
{text: I18n.t('notifications ok'), onPress: () => {
if (Platform.OS === 'android') {
OneSignal.setSubscription(true);
}else if (Platform.OS === 'ios') {
// OneSignal.setLogLevel(6, 0);
OneSignal.registerForPushNotifications();
}
setTimeout(() => {
AsyncStorage.multiSet([['firstTime', JSON.stringify(false)], ['subscribed', JSON.stringify(true)]]);
}, 100)
}}
],
{ cancelable: false }
)
}
}, 500)
}else{
OneSignal.setSubscription(false);
}
SplashScreen.hide();
});
};
Hope you guys can help out.
Cheers,

Ah, I found the cause of the issue. The second setSubscription(false) in the else statement was unsubscribing even-though I didn't.
So removing the else is fixing it for me.

Related

Expo Push Notification Foreground not working

Background Notifications: Working
Killed notificiations: Working
Token generation: Working
Permissions: Verified and working
What should I do to troubleshoot this properly? I have tried other methods of handling, and I believe I tried adding a notification property to app.json but nothing worked to my knowledge.
Thanks for your time!
// imports redacted, but contain expo notification, device etc
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
export default function App() {
const [expoPushToken, setExpoPushToken] = useState<string|undefined>('');
const [notification, setNotification] = useState<any>(false);
const notificationListener = useRef<any>();
const responseListener = useRef<any>();
useEffect(() => {
if(Device.isDevice){
registerForPushNotificationsAsync().then(token => setExpoPushToken(token));
// This listener is fired whenever a notification is received while the app is foregrounded
notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
setNotification(notification);
});
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
console.log(response);
});
return () => {
Notifications.removeNotificationSubscription(notificationListener.current);
Notifications.removeNotificationSubscription(responseListener.current);
};
} else {
//
}
}, []);
return(view stuff)
}
// outside of functional component
async function registerForPushNotificationsAsync() {
let token;
if (Constants.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!');
return;
}
token = (await Notifications.getExpoPushTokenAsync({ experienceId: '#Expo-project-name' })).data; // commented project name for security
} else {
alert('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',
});
}
return token;
}
the fix to this solution is in the experienceId
Make sure your experienceID matches EXACTLY what your expo project name is.
I had mine where the #username/project-name 'project-name' portion was lowercase, but my project was actually named in CAPITAL letters, so #username/PROJECT-NAME
That's the fix!

How to write a JEST test for a useEffect with timers?

I am using #react-native-community/netinfo to detect the app connectivity state and showing a message when connection is lost. How do I write a test for the following code that's in a useEffect to make sure that the message is showing/hiding and the cleanup works?
const { isConnected } = useContext(ConnectionContext);
...
useEffect(() => {
const snack = setTimeout(() => {
if (!isConnected) {
showMessage({
autoHide: false,
message: 'Please try again later',
});
}
}, 10000);
const hideSnack = setTimeout(() => {
if (isConnected) hideMessage();
}, 5000);
return () => {
clearTimeout(snack);
clearTimeout(hideSnack);
};
}, [isConnected]);
I have tried something like this to check if the app is connected
jest.mock('#react-native-community/netinfo', () => ({
...jest.requireActual('#react-native-community/netinfo'),
useNetInfo: () => ({
isConnected: true,
})
}));
You can use jest fake timers to control the time:
at the top use
jest.useFakeTimers();
then when you need to advance time by certain amount use :
jest.advanceTimersByTime(100);

React-Native NetInfo dosen't work after once did work?

I'm new at react-native I have a problem about internet connection check. When I open my app and disconnect for internet(wifi or cellular both) I got a alert message that's what I want. But when I connect again to internet and try again I see in my console state is again false. NO way to see true. Where did I do a fault?
` const [isInternetReachable, setInternetReachable] = useState(true)
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
if (!(state.isInternetReachable === null)) {
setInternetReachable(state.isInternetReachable);
}
});
},[])
useEffect(() => {
chechConnection();
}, [isInternetReachable]);
const chechConnection = () => {
if(isInternetReachable === false) {
Alert.alert(
'Internet fail',
'Try again.',
[
{text: 'Try Again', onPress: chechConnection},
{text: 'Exit', onPress:() => RNExitApp.exitApp()},
],
{ cancelable: false }
)
}
};`
The useEffect which is responsible for setInternetReachable will only run once, no matter what.
Try like this:
const [connected, setConnected] = useState();
useEffect(() => {
const unsubscribe = NetInfo.addEventListener((state) => {
console.log("Connection type", state.type);
console.log("Is Internet Reachable?", state.isInternetReachable);
if (connected !== state.isInternetReachable)
setConnected(state.isInternetReachable);
});
return () => unsubscribe();
}, [connected]);
I found a problem its when I use İf condition or switch my connection state doesn't change. When I don't use switch or if connection state works well.

How to Navigate to Screen after GPS been enabled?

When the User is enabled the GPS I want to navigate it to AuthScreen.js. I'm using react-native-navigation v1 but there is no feature that can navigate just to simple screen, only push and modal but I don't want to use it.
Also using this: react-native-android-location-services-dialog-box
Here are my codes:
componentDidMount() {
this.gpsLocation();
}
gpsLocation = () => {
if (Platform.OS === 'android') {
LocationServicesDialogBox.checkLocationServicesIsEnabled({
message: "<h2>Use Location?</h2> \
This app wants to change your device settings:<br/><br/>\
Use GPS for location<br/><br/>",
ok: "Yes",
cancel: "No",
style: {
backgroundColor: '#4f6d7a',
positiveButtonTextColor: '#000000',
negativeButtonTextColor: '#000000'
},
enableHighAccuracy: true,
showDialog: true,
openLocationServices: true,
preventOutSideTouch: true,
preventBackClick: true,
providerListener: true
}).then(function(success) {
console.log(success)
// return <AuthScreen/>
}).catch((error) => {
console.log(error.message);
});
};
DeviceEventEmitter.addListener('locationProviderStatusChange', function(status) {
console.log(status);
});
};
componentWillUnmount() {
LocationServicesDialogBox.stopListener();
};
render() {
if(!this.state.nextScreen) {
return (
<View style={styles.container}>
</View>
);
} else {
return <AuthScreen/>
}
};
If you really don't want to navigate, you can use the .then() callback in which you're already logging the success parameter. According to the doc, success is an object with the following structure:
{
alreadyEnabled: false,
enabled: true,
status: "enabled"
}
You just need to check if success.enabled is true and if that's the case call setState({ nextScreen : true });
Edit: Here's the code, as requested:
// MyView.js
componentDidMount()
{
this.checkOrRequestLocationPermission();
}
checkOrRequestLocationPermission()
{
if (Platform.OS === 'android')
{
LocationServicesDialogBox.checkLocationServicesIsEnabled({
// Your config
// ...
})
.then(result => {
if (result.enabled)
{
this.setState({
nextScreen : true
});
}
// The above could be replaced by this
// this.setState({
// nextScreen : result.enabled
// });
})
.catch(error => {
console.warn('Error requesting location permission ' + error);
});
}
}
render() {
if(!this.state.nextScreen) {
return (
<View style={styles.container} />
);
}
else {
return <AuthScreen/>
}
};

React Native Share Method Multiple Windows

I’m using react native share, to share text content. The issue is multiple windows are opened over each other on clicking share button multiple times. Even I have disabled button on share click but it is of no use as well.
The standard way is if share window is already open, on clicking the share button again the window get closed. How can it be done?
<Button
transparent
disabled={this.state.isShareDisabled}
onPress={() => this.onShare()}>
onShare() {
if(!this.state.isShareDisabled)
{
this.setState(
{
isShareDisabled:true
}
)
Share.share({
message: “Message test”,
url: ”www.google.com”,
title: “Title test”
}, {
// Android only:
dialogTitle: 'Share',
// iOS only:
excludedActivityTypes: [
'com.apple.UIKit.activity.PostToTwitter'
]
}) .then((result) => {
this.setState(
{
isShareDisabled: false,
}
)
})
}
}
Use dismissed action. Share event support it on both android and iOS.
I resolved it by doing the following, just required to check dismissedAction.
onShare() {
if(!this.state.isShareDisabled)
{
this.setState(
{
isShareDisabled:true
}
)
Share.share({ message: 'test',
url: 'test url',
title: 'test title'
},
{
// Android only:
dialogTitle: 'Share',
// iOS only:
excludedActivityTypes: [
'com.apple.UIKit.activity.PostToTwitter'
]
}).then(({action, activityType}) => {
if(action === Share.dismissedAction) {
this.setState(
{
isShareDisabled: false,
}
)
}
else {
this.setState(
{
isShareDisabled: false,
}
)
}
});
}
}