Angular 7 cannot receive firebase notification in the foreground - firebase-cloud-messaging

I am setting my website to receive firebase notifications. I can receive it when in the background. But could not when in the foreground. I have followed this tutorial link to set up. https://medium.com/#a.adendrata/push-notifications-with-angular-6-firebase-cloud-massaging-dbfb5fbc0eeb.
I have initialize it in app.module.ts.
I have tried others similar stackoverflow solutions. But none of them is working so far.
I have tried to use AngularFireMessaging, and FirebaseApp. But both of them could not receive notification after sent.
import { FirebaseApp } from '#angular/fire';
import '#firebase/messaging';
import { AngularFireMessaging } from '#angular/fire/messaging';
setUpMessage() {
this.messaging = this.firebaseApp.messaging();
}
setUpFCM() {
this.afMessaging.messaging.subscribe(_messaging => {
_messaging.onMessage = _messaging.onMessage.bind(_messaging);
_messaging.onTokenRefresh = _messaging.onTokenRefresh.bind(_messaging);
});
}
requestPermission() {
this.afMessaging.requestToken.subscribe(token => {
console.log(token);
}, error => {
console.error(error);
});
}
listenToNotifications() {
return this.afMessaging.messages;
}
listenNotifications() {
return this.messaging;
}
In my component.ts file, I initialized them and got the token from firebase. but cannot receive notification in the foreground.
ngOnInit() {
this.fcmTokenService.setUpMessage();
this.fcmTokenService.setUpFCM();
this.fcmTokenService.requestPermission();
this.validation_messages = this.printFormService.printValidationMessage();
this.listenNotification();
this.listenNotification2();
}
private listenNotification() {
this.fcmTokenService.listenToNotifications().subscribe(msg => {
// msg.content = JSON.parse(msg.data.content);
console.log(msg);
});
}
private listenNotification2() {
this.fcmTokenService.listenNotifications().onMessage(msg => {
console.log(msg);
});
}
I expect to receive the notification and console log it, but no result after many hours or retrying with different approaches.

On your firebase-messaging-sw.js or your service worker file?
importScripts('https://www.gstatic.com/firebasejs/7.6.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.6.0/firebase-messaging.js');
should have the same version with your firebase on package.json
"dependencies": {
...
"firebase": "^7.6.0",
...
}
More details here: https://github.com/angular/angularfire/issues/1904#issuecomment-543506629

I found that importScripts version in service Worker & version of library in package.json has to be same.

The import scripts version and the library version in node_modules must be same for foreground messaging subscription to get the data!

This is a Version compatibility issue as far as I figured. just try to use
"firebase": "^5.0.0",
"#angular/fire": "^5.0.0",
override your package.json with these two, run npm install, you will be fine

Related

react native Android Cannot access realm that has been closed

im using Realm inside my React native app, in IOS everything work fine, but with Android I always got this error: Cannot access realm that has been closed
here is my Realm:
RealmContext.js
import { ContactInfo, Room, RoomBackground, RoomDetail, RoomMessage, Summary } from "../database/RealmSchemas"
import { Realm, createRealmContext } from '#realm/react'
const config = {
schema: [Room.schema,
ContactInfo.schema,
RoomDetail.schema,
RoomBackground.schema,
Summary.schema,
RoomMessage.schema],
}
export default createRealmContext(config)
Other class
import RealmContext from '../../context/RealmContext'
const { useRealm, useQuery } = RealmContext
export class....{
const realm = useRealm()
const getRoomDetailFromDb = () => {
try {
const roomDetailDb = realm.objectForPrimaryKey('RoomDetail', room.RoomId)
if (roomDetailDb != null) {
roomDetail = JSON.parse(roomDetailDb.value)
}
} catch (error) {
console.log(error)
}
}
here is my version of realm:
"realm": "^10.18.0",
"#realm/react": "^0.3.0",
i did following the site: https://www.mongodb.com/docs/realm/sdk/react-native/use-realm-react/
Seems like react-native-reanimated lib is not compatible with Realm v10 on Android.
In case you use that please try realm#11.0.0-rc.0. It works fine after I upgrade realm version to v11. Even though it's not a stable version.

Ionic 4 Not support Events?

Im using Event fucntion to publish some data in app. But its not working in ionic 4. I need to know ionic 4 support Events or not?
import { Events } from '#ionic-angular';
// Module not found: Error: Can't resolve '#ionic-angular'
You can use #angular/Events
//MyEvents Service Page
import { Injectable } from '#angular/core';
import { Subject, Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class EventsService {
constructor() { }
private subject = new Subject<any>();
sendMessage(text){
this.subject.next(text);
}
getMessage():Observable<any>{
return this.subject.asObservable();
}
}
//Page for sendMessage
constructor(private events: EventsService) {
this.events.sendMessage({'created':1}); //send message key-value format
}
//Page for getMessage
subscription: Subscription;
constructor(private events: EventsService) {
this.subscription = this.events.getMessage().subscribe(text => {
console.log(text.created);
})
}
Below solution is working in ionic v4
import { Events } from '#ionic/angular';
constructor(private events: Events) {
events.subscribe('notificationLength', notilen => {
//TO DO`enter code here`
})
}
// Publish the events where ever you want
this.events.publish('notificationLength', this.NotificationList.length)
The problem was in the version. When I updated to the latest patch of version 4, it was working.
npm i #ionic/angular#4.11.10

Ionic 3's PWA & Firebase Cloud Messaging registration

I was following this article here (which is not complete unfortunately) in attempt to learn how to friend Ionic 3 based PWA and Firebase Cloud Messaging: Push Notifications with FCM
What I did:
as advised in the article added FCM libraries into service-worker.js:
'use strict';
importScripts('./build/sw-toolbox.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': '47286327412'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler((payload) => {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the service sending messages
const notificationOptions = {
icon: '/assets/img/appicon.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.toolbox.options.cache = {
name: 'ionic-cache'
};
// pre-cache our key assets
self.toolbox.precache(
[
'./build/main.js',
'./build/vendor.js',
'./build/main.css',
'./build/polyfills.js',
'index.html',
'manifest.json'
]
);
// dynamically cache any other local assets
self.toolbox.router.any('/*', self.toolbox.cacheFirst);
// for any other requests go to the network, cache,
// and then only use that cached resource if your user goes offline
self.toolbox.router.default = self.toolbox.networkFirst;
Then created Firebase Messaging based provider here:
import { Injectable } from "#angular/core";
import * as firebase from 'firebase';
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging: firebase.messaging.Messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage
) {
this.messaging = firebase.messaging();
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
And now during app initialization I call enableNotifications() and get error that says that default service worker is not found (404):
A bad HTTP response code (404) was received when fetching the script.
:8100/firebase-messaging-sw.js Failed to load resource: net::ERR_INVALID_RESPONSE
If I move service-worker.js firebase related stuff into default service worker in WWW folder - I get general error from Firebase (Error, failed to register service worker).
QUESTIONS:
- is there a fresh guide on Ionic 3's PWA & FCM?
- at high level what is the difference in registering service workers in Ionic 3 vs Angular? I did watch the tutorial about Angular but can't figure how to do the same in Ionic 3.
UPDATE: the below is valid as of today (02/12/2018) and most likely will be less relevant once AngularFire2 supports messaging module. So take the below with that assumption...
OK I researched and finally made it work on my Ionic 3 PWA, so I am posting solution here:
Prerequisites:
I created ionic blank app (just a home page)
installed angularfire2 and firebase ("angularfire2": "5.0.0-rc.4","firebase": "4.9.1") using npm install, I used specifically 5.0.0-rc.4" cause I had stability issues with latest one;(
created config (filename environment.ts in src folder):
export const firebaseConfig = {
apiKey: "Your Stuff Here from FB",
authDomain: "YOURAPPNAME.firebaseapp.com",
databaseURL: "https://YOURAPPNAME.firebaseio.com",
projectId: "YOURAPPNAME",
storageBucket: "YOURAPPNAME.appspot.com",
messagingSenderId: "FROMFIREBASECONEOLE"
};
I modified app.module.ts to add firebase and angularfire2 this way:
...
import { AngularFireModule } from 'angularfire2';
import 'firebase/messaging'; // only import firebase messaging or as needed;
import { firebaseConfig } from '../environment';
import { FirebaseMessagingProvider } from '../providers/firebase-messaging';
...
#NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
AngularFireModule.initializeApp(firebaseConfig),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [
FirebaseMessagingProvider,
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
Here we also import our provider whose code is below:
in providers folder I created firebase-messaging.ts like this:
import { Injectable } from "#angular/core";
import { FirebaseApp } from 'angularfire2';
// I am importing simple ionic storage (local one), in prod this should be remote storage of some sort.
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage,
private app: FirebaseApp
) {
this.messaging = app.messaging();
navigator.serviceWorker.register('service-worker.js').then((registration) => {
this.messaging.useServiceWorker(registration);
//this.disableNotifications()
this.enableNotifications();
});
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
console.log(currentToken)
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
Please note I init the firebase app and then in constructor we register ionic's default service worker (service-worker.js) that contains the following right after whatever is there by default:
service-worker.js:
// firebase messaging part:
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging.js');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': 'YOURIDFROMYOURFIREBASECONSOLE'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the Webtask
const notificationOptions = {
icon: '/assets/images/logo-128.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
At this point you also need to make sure you enabled your app as PWA, there is a good guide from Josh Morony and today there was a video stream on youtube that covers it. In TLDR you need to uncomment this in your index.html:
index.html in src uncomment:
<!-- un-comment this code to enable service worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>
OK almost the last thing - your manifest.json (in src) should have exact line:
"gcm_sender_id": "103953800507"
This concludes initial stuff on the client. Please note I didn't implement yet anything to handle notifications while user is in app itself, think for now it just handles when a message is sent from a server while your tab is not in focus (that is what I tested).
Now you want to go to your firebase console and obtain server key (click setting gear icon, then see cloud messaging section there). Copy server key. Also run the client (ionic serve and capture your local token (i just console.logged it). Now try sending yourself the message using a POST method. ( I did it with Postman)
// method: "POST",
//url: "https://fcm.googleapis.com/fcm/send",
// get the key from Firebase console
headers: { Authorization: `key=${fcmServerKey}` },
json: {
"notification": {
"title": "Message title",
"body": "Message body",
"click_action": "URL to your app?"
},
// userData is where your client stored the FCM token for the given user
// it should be read from the database
"to": userData.fcmRegistrationKey
}
So by doing all this I was able to reliable send myself a message WHILE the app was in background. I am yet to handle foreground but this SO question is about how to init default service worker and marry it with FCM.
I hope this will help some learners in future.
I have successfully implemented the process and got success response on API calls. But no notification popup coming on my browser. Any idea?
api: https://fcm.googleapis.com/fcm/send
response got:
{"multicast_id":6904414188195222649,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1545375125056264%e609af1cf9fd7ecd"}]}
cheth the attached url of my console:

IOS Expo Push Notifications when app is in foreground

reading from the expo docs:
For iOS, you would be wise to handle push notifications that are
received while the app is foregrounded, because otherwise the user
will never see them. Notifications that arrive while the app are
foregrounded on iOS do not show up in the system notification list. A
common solution is to just show the notification manually. For
example, if you get a message on Messenger for iOS, have the app
foregrounded, but do not have that conversation open, you will see the
notification slide down from the top of the screen with a custom
notification UI.
What I don't understand is what is the best approach for that? is there an Expo API for showing such messages? or should I create an alert component of my own? It is not really clear from the docs.
Thanks.
This answer is outdated as of February 20, 2020. Please see https://stackoverflow.com/a/60344280/2441420 for how to show iOS Notification when your application is in the Foreground
There isn't an Expo API for showing those messages. You can use any 'toast' library of your choosing and display the notification message, but that should be all your code.
For example, this is how we are doing right now:
export default class HomeScreen extends React.Component {
componentDidMount() {
this.notificationSubscription = Notifications.addListener(
(notification) => this.handlePushNotification(notification),
);
}
handlePushNotification(notification) {
const { navigation } = this.props;
PushNotificationsService.handleNotification(notification, navigation);
}
(...)
import Toast from 'react-native-root-toast';
export default class PushNotificationsService {
static handleNotification(notification, navigation) {
if (notification.data.screen && notification.origin === 'selected') {
navigation.navigate(notification.data.screen);
}
Toast.show(notification.data.message);
}
}
Toast libraries include:
react-native-root-toast
react-native-easy-toast
react-native-simple-toast
Now you can just add that in one of your app entry point. The shouldShowAlert is what you want here
import * as Notifications from 'expo-notifications';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
App.json :
{
"expo": {
"notification": {
"iosDisplayInForeground": true
}
}
DEMO
I'm not sure exactly when this was added to Expo, but as of Expo version 36 is easily doable.
To show Expo Push Notifications on iOS when your app is in the foreground, please do the following:
import { Vibration } from "react-native";
import { Notifications } from "expo";
import * as Permissions from "expo-permissions";
import Constants from "expo-constants";
registerForPushNotificationsAsync = async () => {
if (Constants.isDevice) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Permissions.askAsync(
Permissions.NOTIFICATIONS
);
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
let token = await Notifications.getExpoPushTokenAsync();
console.log("Go to https://expo.io/notifications and copy the token below to easily send yourself a notification.");
console.warn("Notifications on iOS (and I believe Android) ONLY WORK ON A PHYSICAL DEVICE, not a simulator or emulator!!!")
console.log(token);
this.setState({ expoPushToken: token });
} else {
alert("Must use physical device for Push Notifications");
}
};
componentDidMount() {
this.registerForPushNotificationsAsync();
this._notificationSubscription = Notifications.addListener(
this._handleNotification
);
}
_handleNotification = async notification => {
if (notification.remote) {
Vibration.vibrate();
const notificationId = Notifications.presentLocalNotificationAsync({
title: "Follow #technoplato",
body: "To learn yourself goodly (also follow PewDiePie)",
ios: { _displayInForeground: true } // <-- HERE'S WHERE THE MAGIC HAPPENS
});
}
};
Quick and Easy Sanity Check
1) Go here: https://expo.io/notifications
2) Copy the token that is output to the terminal when your application is run.
3) Open your application on iOS.
4) Send a notification to yourself from https://expo.io/notifications and observe that it shows up even when your app is foregrounded.
Notes
Notifications WILL NOT BE RECEIVED ON AN IOS SIMULATOR
Expo makes Notifications ridiculously easy. I honestly can't believe it.
No idea why displayInForeground is false by default and not more prominent in the documentation. I'll submit a PR for it if I can.
Code originally found at this Snack: https://snack.expo.io/#documentation/pushnotifications?platform=ios
LocalNotification.ios._displayInForeground found here: https://docs.expo.io/versions/v36.0.0/sdk/notifications/#localnotification

Unhandled Network Error 401 When using Apollo in react native & empty graphql response

I am building a react-native app using create-react-native-app cli
Here are the packages I am using. All of the latest stable except fiber
"dependencies": {
"apollo-cache-inmemory": "^1.0.0",
"apollo-client": "^2.0.1",
"apollo-link": "^1.0.0",
"apollo-link-error": "^1.0.0",
"apollo-link-http": "^1.0.0",
"expo": "^21.0.0",
"graphql": "^0.11.7",
"graphql-tag": "^2.5.0",
"react": "16.0.0-alpha.12",
"react-apollo": "^2.0.0",
"react-native": "^0.48.4",
"react-navigation": "^1.0.0-beta.15"
}
The app runs and routing navigation works fine. I have two routes, App and FeedList.
the app talks to github graph api( I dont know if github access token is required, if so, i haved added it but I don't know if that's the correct way)
Feedlist is wrapped in a graphql HoC component and its props has a data which is already populated with results of the graphql request(even if the request fails)
You can refer to The structure of the data prop
App Component
import React, { Component } from 'react'
import ApolloClient from 'apollo-client'
import { ApolloLink } from 'apollo-link'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloProvider } from 'react-apollo';
import { onError } from 'apollo-link-error';
import Router from './router'
class App extends Component {
createClient() {
const httpLink = createHttpLink({ uri: 'https://api.github.com/graphql'})
// handle network error
const errorLink = onError(({ networkError }) => {
if (networkError.statusCode === 401) {
console.log(networkError)
}
// let errorMessage = networkError.statusCode === 401 ? 'Network error 104, handled' : 'link sucess'
// console.log(errorMessage, networkError)
})
// apply widdleware to add access token to request
let middlewareLink = new ApolloLink((operation, forward) => {
operation.setContext({
headers: {
authorization: 'GITHUB_ACCESS_TOKEN'
}
})
return forward(operation)
})
const link = middlewareLink.concat(httpLink)
// Initialize Apollo Client with URL to our server
return new ApolloClient({
link: link,
cache: new InMemoryCache(),
})
}
render () {
return (
<ApolloProvider client={this.createClient()}>
<Router />
</ApolloProvider>
)
}
}
export default App
FeedList Component
import React, { Component } from 'react';
import { Text } from 'react-native';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
// The data prop, which is provided by the wrapper below contains,
// a `loading` key while the query is in flight and posts when ready
const FeedList = ({ data }) => {
console.log(data) // returns default from HoC injected props
console.log('FeedList Eroor!!',data.error) // returns undefined
return data.error ? <Text> Error Fetching posts</Text>:<Text>fetching posts... </Text>
}
// The `graphql` wrapper executes a GraphQL query and makes the results
// available on the `data` prop of the wrapped component (PostList here)
export default graphql(gql`{
search(type: REPOSITORY, query: "react", first: 20) {
edges {
node {
... on Repository {
nameWithOwner
owner {
login
}
}
}
}
}
}`, { options: { notifyOnNetworkStatusChange: true } })(FetchPost);
The important parts are the the Wrapped FeedList component which does log the data from props and the createClient() method inside App component.
I followed this to try and catch the error that may happen during the data request.
The HoC wrapper component does its job and FeedList's data prop has has everything but the response from the api.
Note that the error does not crash app
The above image shows also the same error message as on the expo simulator on android
So I guess the question i have is
How do I catch the error? Here is a similar issue
If its the way I structured my query thats the problem. How do I correctly get the github api to return data correctl?
Here is the repo you can run and reproduce the error
Note that Issue#1 in that repo is similar to this SO question and I have already fixed it by handling data.error as per the documentation
Also note that most of the documentation is for the older versions of apollo-client > v2.x.x
Issue #2 is exactly what I am asking here. Thank you for your help
A 401 status means invalid authentication. As you've guessed, you need a valid OAuth token to send with your request. For development purposes, you can just generate one. Make sure you include the scopes outlines as outlined here:
user
public_repo
repo
repo_deployment
repo:status
read:repo_hook
read:org
read:public_key
read:gpg_key
You don't want users accessing the API through your credentials, so eventually you'll need to implement an OAuth2 authentication flow in your app to generate a token for each end user.