In this step we are going to implement push notifications using Google's Firebase Cloud Messaging (FCM). Whenever a user will send you a message, if you don't have our application in the foreground you will get a push notification.
First we will have to create google-services.json in our project's root directory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{  "project_info": {    "project_number": "152311690748",    "firebase_url": "https://meteor-c069e.firebaseio.com",    "project_id": "meteor-c069e",    "storage_bucket": "meteor-c069e.appspot.com"  },  "client": [    {      "client_info": {        "mobilesdk_app_id": "1:152311690748:android:25f0ec3806cf1f01",        "android_client_info": {          "package_name": "io.ionic.starter"        }      },      "oauth_client": [        {          "client_id": "152311690748-2ht8fdqhlnv8lsrrvnd7u521j9rcgi3h.apps.googleusercontent.com",          "client_type": 3        }],
      "api_key": [        {          "current_key": "AIzaSyD9CKsY6bC_a4Equ2HpbcrSErgJ2pheDS4"        }],
      "services": {        "analytics_service": {          "status": 1        },        "appinvite_service": {          "status": 1,          "other_platform_oauth_client": []        },        "ads_service": {          "status": 2        }      }    }],
  "configuration_version": "1"}Then we need to install the FCM Cordova plug-in:
$ ionic cordova plugin add cordova-plugin-fcm --save
$ npm install --save @ionic-native/fcm
Then let's add it to app.module.ts:
10
11
12
13
14
15
16
78
79
80
81
82
83
84
85
import { Camera } from '@ionic-native/camera';import { Crop } from '@ionic-native/crop';import { Contacts } from "@ionic-native/contacts";import { FCM } from "@ionic-native/fcm";import { AgmCoreModule } from '@agm/core';import { MomentModule } from 'angular2-moment';import { ChatsPage } from '../pages/chats/chats';...some lines skipped...SmsReceiver,
Camera,
Crop,
Contacts,
FCM
]
})export class AppModule {}Now we can start adding some FCM logic into ChatsPage:
7
8
9
10
11
12
13
21
22
23
24
25
26
27
28
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import { MessagesPage } from '../messages/messages';import { ChatsOptionsComponent } from './chats-options';import { NewChatComponent } from './new-chat';import { FCM } from "@ionic-native/fcm";@Component({  templateUrl: 'chats.html'...some lines skipped...    private popoverCtrl: PopoverController,    private modalCtrl: ModalController,    private alertCtrl: AlertController,    private platform: Platform,    private fcm: FCM) {    this.senderId = Meteor.userId();  }...some lines skipped...        this.chats = this.findChats();      });    });    // Notifications    if (this.platform.is('cordova')) {      //this.fcm.subscribeToTopic('news');      this.fcm.getToken().then(token => {        console.log("Registering FCM token on backend");        MeteorObservable.call('saveFcmToken', token).subscribe({          next: () => console.log("FCM Token saved"),          error: err => console.error('Impossible to save FCM token: ', err)        });      });      this.fcm.onNotification().subscribe(data => {        if (data.wasTapped) {          console.log("Received FCM notification in background");        } else {          console.log("Received FCM notification in foreground");        }      });      this.fcm.onTokenRefresh().subscribe(token => {        console.log("Updating FCM token on backend");        MeteorObservable.call('saveFcmToken', token).subscribe({          next: () => console.log("FCM Token updated"),          error: err => console.error('Impossible to update FCM token: ' + err)        });      });    }  }  findChats(): Observable<Chat[]> {We used the saveFcmToken Meteor method, so we need to create it first:
2
3
4
5
6
7
8
95
96
97
98
99
100
101
102
103
104
105
106
import { Messages } from './collections/messages';import { MessageType, Profile } from './models';import { check, Match } from 'meteor/check';import { Users } from "./collections/users";const nonEmptyString = Match.Where((str) => {  check(str, String);...some lines skipped...  },  countMessages(): number {    return Messages.collection.find().count();  },  saveFcmToken(token: string): void {    if (!this.userId) throw new Meteor.Error('unauthorized', 'User must be logged-in to call this method');check(token, nonEmptyString);
    Users.collection.update({_id: this.userId}, {$set: {"fcmToken": token}});  }});Since we will soon need the node-fetch package, we will need to install it first:
$ npm install --save node-fetch
$ npm install --save-dev @types/node-fetch
Let's implement our server side service which will actually send the notification:
4
5
6
7
8
9
10
11
12
13
    "verificationRetriesWaitTime": 0,    "adminPhoneNumbers": ["+9721234567", "+97212345678", "+97212345679"],    "phoneVerificationMasterCode": "1234"  },  "private": {    "fcm": {      "key": "AIzaSyBnmvN5WNv3rAaLra1RUr9vA5k0pNp0KuY"    }  }}Now we should edit the AddMessage Meteor method to use our just-created service to send the notification:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import fetch from 'node-fetch';export interface FcmNotification {  title: string;  text: string;}export class FcmService {  private key: string = Meteor.settings.private.fcm.key;  sendNotification(notification: FcmNotification, destination: string) {    const body = {notification: notification,
to: destination
    };    const options = {      method: 'POST',      body: JSON.stringify(body),      headers: {        "Content-Type": "application/json",        Authorization: `key=${this.key}`      },    };    return fetch("https://fcm.googleapis.com/fcm/send", options);  }}export const fcmService = new FcmService();Before the Typescript compiler complains, let's update our models:
3
4
5
6
7
8
9
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { MessageType, Profile } from './models';import { check, Match } from 'meteor/check';import { Users } from "./collections/users";import { fcmService } from "./services/fcm";const nonEmptyString = Match.Where((str) => {  check(str, String);...some lines skipped...        'Chat doesn\'t exist');    }    const userId = this.userId;    const senderName = Users.collection.findOne({_id: userId}).profile.name;    const memberIds = Chats.collection.findOne({_id: chatId}).memberIds;    const tokens: string[] = Users.collection.find(      {        _id: {$in: memberIds, $nin: [userId]},        fcmToken: {$exists: true}      }).map((el) => el.fcmToken);
    for (let token of tokens) {      console.log("Sending FCM notification");      fcmService.sendNotification({"title": `New message from ${senderName}`, "text": content}, token);    }    return {      messageId: Messages.collection.insert({chatId: chatId,