Expo push notifications stopped working in production - push-notification

I'm using Expo to develop both Android and iOS at same time. Notifications were working fine for several weeks, and then out of no where stopped working in production, even though I did not update the app during this time.
Server-side, everything is still fine, and notifications are being pushed. In dev, notifications are still being received and handled properly, but in production, it's crickets.
Has anyone else experienced this / what could be causing this?
Here is my code:
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
notificationsSet: false,
}
}
componentDidMount() {
this.registerForPushNotificationsAsync(this.props.currentUser.currentUser.id, this.props.currentUser.authToken)
savePushToken = (userId, pushToken, token) => {
//API call to save push token to database
apiHelper
.savePushToken(userId, pushToken, token)
.then(res => {
return
})
.catch(err => console.log("err saving", err));
};
handleNotification = notification => {
this.props.setNotification({ notification })
}
registerForPushNotificationsAsync = async (userId, token) =>{
//requesting if user would like to turn on notifications
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
//this checks if notifications is turned on for the app --- "granted"
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
if (finalStatus !== "granted") {
return;
} //if "granted" then get push notifications and calls this.savepushtoken to save into the API
let pushToken = await Notifications.getExpoPushTokenAsync();
this.subscription = Notifications.addListener(this.handleNotification);
this.savePushToken(userId, pushToken, token);
};
render() {
return(...)
}
}

Related

Teams Tab App not working on mobile client

I have a teams app which serves as another front end for our application (I will call it App 1) and we just use the client id for App 1 to authenticate. This works fine on Teams desktop and web, but on teams mobile (Android) I can successfully obtain a bearer token, but the rest of the api calls return status cancelled.
My login work flows look like so:
LoginForm.js
const { teamsUserCredential } = useContext(TeamsFxContext);
const { loading, error, data, reload } = useGraphWithCredential(
async (graph, teamsUserCredential, scope) => {
const provider = new TeamsFxProvider(teamsUserCredential, scope);
Providers.globalProvider = provider;
Providers.globalProvider.setState(ProviderState.SignedIn);
if (teamsUserCredential.ssoToken === null) {
setTeamsLoginError(true); // flag to switch to web auth flow
}
if (error) {
setTeamsLoginError(true);
}
},
{ scope: ["User.Read"], credential: teamsUserCredential }
);
// for web auth flow
const onClick = async () = {
await login(instance)
}
App.js
const { theme, themeString, teamsUserCredential, error, loading } =
useTeamsUserCredential({
initiateLoginEndpoint:
"https://{teams app site name}.web.core.windows.net/auth-start.html",
clientId: "{client id for App 1}", // not the teams app client id
});
const {
instance,
accounts,
} = useMsal();
// SSO auth flow
if (!teamsLoginError) {
let newAccessToken;
try {
newAccessToken = await teamsUserCredential.getToken(
"api://{client id for App 1}/access_as_user"
);
} catch (error) {
console.log("error getting token");
}
const token = `Bearer ${newAccessToken.token}`;
}
// auth flow for web clients
else {
const accessTokenRequest = {
scopes: ["api://{client id for App 1}/access_as_user"],
account: accounts[0],
};
instance
.acquireTokenSilent(accessTokenRequest)
.then(async (accessTokenResponse) => {
let newAccessToken = accessTokenResponse.accessToken;
const token = `Bearer ${newAccessToken}`;
}

messaging().getToken() generates same device token for different devices

I got an issue with fcm tokens, they are identical for some devices (as you can see from screenshot). On internet it is said that they should be unique for each device, but it seems that in our case they are not. This is the way how I get fcm tokens from messaging library (react native firebase):
export const AppMaintainer = () => {
const fullname = useAppSelector(getMyFullName);
const photoUrl = useAppSelector(getPhotoUrl);
const userDocId: string = useAppSelector(getCurrentUserDocId);
const token: TokenOrProvider = useAppSelector(getCurrentStreamToken);
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(actions.authStateChangeUser());
}, []);
const requestUserPermission = async () => {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('Authorization status:', authStatus);
const deviceToken = await getFcmToken();
try {
await firestore()
.collection('usersDescriptiveData')
.doc(userDocId)
.update({
deviceToken,
});
} catch (error: any) {
console.log('error in deviceToken update');
dispatch(
globalActions.setIsGlobalSnackbarVisible({message: error.message}),
);
}
}
};
const getFcmToken = async () => {
try {
const fcmToken = await messaging().getToken();
return fcmToken;
} catch (error) {
console.log('error in fcm', error);
}
};
useEffect(() => {
if (userDocId && photoUrl && token && fullname) {
requestUserPermission();
}
}, [userDocId, photoUrl, token, fullname]);
return (
<>
<NavigationContainer ref={navigationContainerRef}>
<RootNavigator />
</NavigationContainer>
<NetGlobalSnackbar />
</>
);
};
Could you please say what i am doing wrong?
Package.json:
"react-native": "0.69.6",
"#react-native-firebase/messaging": "12.9.3".
Additionally, I assume that these duplicated tokens are the reason why some users get notifications more then two times (but this is another story).
I tried calling the getFsmToken function again when deviceToken was already in use by another user, but it didnt help. Additionally, tried deleting and generating the deviceToken again, but it didnt help too. I expected this token to be unique for each device, but it is not, which means i am doing something wrong. FYI: i dont do it with browser, the app is available on stores and some users get the same token for their devices
Could anyone guide me with this?

Firebase V9 does not give error in catch when offline

I want to set state in catch even if user offline but firebase V9 setDoc does not give anything in catch when user offline
For Example: in Authentication, if the user offline firebase gives (network error) in catch but in firestore "add document" no message from catch...
This is by design thanks to Firestore's Offline Behaviour (queued up to the right spot, but I do recommend watching in full).
The promise will resolve once the server has acknowledged the request. If the server is currently unavailable, that request is cached within the SDK and attempted as soon as a connection is restored. During this window, the Promise will be kept in its pending state because that's the state its actually in - pending. While the promise may not resolve, all your local realtime listeners and such will still fire off and your app will function as normal - just offline.
Dealing with this behaviour is an exercise for the developer. One way to approach this would be to use Promise.race() to implement your own offline-handling logic.
As a quick & dirty example, here's a setDocWithTimeout implementation:
const setDocWithTimeout = (ref, data, options) => {
const timeoutMS = options && options.timeout || 10000;
const setDocPromise = setDoc(ref, data);
return Promise.race([
setDocPromise.then(() => ({ timeout: false })),
new Promise((resolve, reject) => setTimeout(resolve, timeoutMS, { timeout: true, promise: setDocPromise }));
]);
}
which you can apply using:
try {
const result = await setDocWithTimeout(doc(db, "cities", "new-city-2"), data);
if (result.timeout) {
// offline? (could be poor connection too)
console.log("Document added to Firestore queue");
// dismiss any blocking UIs/loading bubbles
// tell user will be written to server later
await result.promise; // wait for write to complete as before
}
// online! (or back online)
console.log("Document written successfully!");
} catch (err) {
console.error(`error found! ${err}`);
}
Alternatively where an error is thrown:
const setDocWithTimeoutError = (ref, data, options) => {
const timeoutMS = options && options.timeout || 10000;
const setDocPromise = setDoc(ref, data);
return Promise.race([
setDocPromise,
new Promise((_, reject) => setTimeout(reject, timeoutMS, new Error("timeout"));
]);
}
which you can apply using:
try {
await setDocWithTimeoutError(doc(db, "cities", "new-city-2"), data);
console.log("Document written successfully!");
} catch (err) {
console.error(`error found! ${err}`);
}
works on web v9, see
docs from v8.
import { onLog } from 'firebase/app';
onLog((e) => {
const { level, message } = e;
if (level === 'warn') {
console.log('connection interruption after intial load was success:', message);
}
if (level === 'error') {
console.log('no connection on inital load:', message);
}
});

RNFirebase v6 Push Notifications are not coming both iOS&Android

I am trying to send notifications from firebase console to my react-native app
I followed the poor documentation here as much as I understand: https://invertase.io/oss/react-native-firebase/v6/messaging/quick-start
I installed #react-native-firebase/app and /messaging and here is my code in component:
componentDidMount() {
this.reqNotifications()
this.checkNotificationPermission()
}
reqNotifications() {
requestNotifications(['alert', 'badge', 'sound']).then(({status, settings}) => {
console.log('NOTIFICATION STATUS' + status)
});
}
async checkNotificationPermission() {
const enabled = await messaging().hasPermission();
if (enabled) {
console.log('APPROVED');
await messaging().registerForRemoteNotifications()
messaging().getToken().then(token => console.log('token: >> ' + token))
} else {
console.log('NOT APPROVED');
}
}
I am requesting permission via react-native-permissions and permission request is
working.
My Apple APNs are OK on Apple and Firebase console
And I am getting my token by getToken() method on the code
succesfully.
But I cant send anything to device from firebase; nothing happening on neither foreground nor background . I tried with-token test and also tried normal but no, nothing happens.
I added this code to componentDidMount:
messaging().onMessage(async remoteMessage => {
console.log('FCM Message Data:', remoteMessage.data);
});
As I understand this subscribes for cloud messages and when I send some cloud message notification from firebase-console, I should get console output; but nothing happens.
I dont know what am I missing but I think there is a big update on this package and most of docs are for previous version and I really stuck here thanks for assist
for rnfirebase.io V6
componentDidMount = async () => {
this.checkNotificationPermission();
await messaging().requestPermission({provisional: true});
await messaging().registerDeviceForRemoteMessages();
await this.getFCMToken();
if (Platform.OS === 'android') {
this.createAndroidNotificationChannel();
}
this.backgroundState();
this.foregroundState();
};
checkNotificationPermission = () => {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (!enabled) {
this.promptForNotificationPermission();
}
});
};
promptForNotificationPermission = () => {
firebase
.messaging()
.requestPermission({provisional: true})
.then(() => {
console.log('Permission granted.');
})
.catch(() => {
console.log('Permission rejected.');
});
};
createAndroidNotificationChannel() {
const channel = new firebase.notifications.Android.Channel(
'channelId',
'Push Notification',
firebase.notifications.Android.Importance.Max,
).setDescription('Turn on to receive push notification');
firebase.notifications().android.createChannel(channel);
}
foregroundState = () => {
const unsubscribe = messaging().onMessage(async notification => {
console.log('Message handled in the foregroundState!', notification);
});
return unsubscribe;
};
// Register background handler
backgroundState = () => {
messaging().setBackgroundMessageHandler(async notification => {
console.log('Message handled in the background!', notification);
});
};

push event not triggered in service worker

Following this tutorial until "Handle push event" section to setup a desktop notification system in my application, I face a problem:
When I click "push" to push a notification artificially with Chrome, no notification appear. No message in the console.
I allowed the notification from the website and the service-worker is well installed in my browser.
My service worker looks like this:
self.addEventListener('push', function (event) {
console.log('[Service Worker] Push Received.')
console.log(`[Service Worker] Push had this data: "${event.data.text()}"`)
const title = 'My App Name'
const options = {
body: event.data.text(),
icon: 'pwa/icon.png',
badge: 'pwa/badge.png'
}
const notificationPromise = self.registration.showNotification(title, options)
event.waitUntil(notificationPromise)
})
and my service worker registration (using register-service-worker npm package) looks like this:
import { register } from 'register-service-worker'
const applicationServerPublicKey = 'BI5qCj0NdNvjDcBYTIXiNccdcP74Egtb3WxuaXrHIVCLdM-MwqPkLplHozlMsM3ioINQ6S_HAexCM0UqKMvaYmg'
function urlB64ToUint8Array (base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4)
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/')
const rawData = window.atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}
async function manageNotificationSubscription (registration) {
const subscription = await registration.pushManager.getSubscription()
let isSubscribed: boolean = !(subscription === null)
if (isSubscribed) {
console.log('User IS subscribed.')
} else {
console.log('User is NOT subscribed.')
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey)
try {
await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerKey
})
console.log('User just subscribed.')
} catch (e) {
console.error('Failed to subscribe the user: ', e)
}
}
}
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready () {
console.log(
'App is being served from cache by a service worker.'
)
},
async registered (registration) {
console.log('Service worker has been registered.')
await manageNotificationSubscription(registration)
},
cached () {
console.log('Content has been cached for offline use.')
},
updated () {
console.log('New content is available; please refresh.')
},
offline () {
console.log('No internet connection found. App is running in offline mode.')
},
error (error) {
console.error('Error during service worker registration:', error)
}
})
}
It looks like the push event in the service-worker is not even triggered...
Did I do something wrong?

Resources