Fork me on GitHub

WhatsApp Clone with Ionic 2 and Meteor CLI

Socially Merge Version (Last Update: 14.02.2017)

Native Mobile

Note: If you skipped ahead to this section, click here to download a zip of the tutorial at this point.

In this step, we will be implementing additional native features, to enhance the user experience.

Automatic phone number detection

Ionic 2 is provided by default with a Cordova plug-in called cordova-plugin-sim, which allows us to retrieve some data from the current device's SIM card, if even exists. We will use the SIM card to automatically detect the current device's phone number, so this way the user won't need to manually fill-in his phone number whenever he tries to login. We will start by adding the appropriate handler in the PhoneService:

13.1 Implement getNumber with native ionic client/imports/services/phone.ts
1
2
3
4
5
6
 
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Injectable } from [email protected]/core';
import { Platform } from 'ionic-angular';
import { Sim } from 'ionic-native';
import { Accounts } from 'meteor/accounts-base';
import { Meteor } from 'meteor/meteor';
 
...some lines skipped...
 
  }
 
  getNumber(): Promise<string> {
    if (!this.platform.is('cordova') ||
      !this.platform.is('mobile')) {
      return Promise.resolve('');
    }
 
    return Sim.getSimInfo().then((info) => {
      return '+' + info.phoneNumber;
    });
  }
 
  verify(phoneNumber: string): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      Accounts.requestPhoneVerification(phoneNumber, (e: Error) => {

And we will use it inside the LoginPage:

13.2 Use getNumber native method client/imports/pages/login/login.ts
1
2
3
4
 
7
8
9
10
11
12
13
 
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { Component, AfterContentInit } from [email protected]/core';
import { Alert, AlertController, NavController } from 'ionic-angular';
import { PhoneService } from '../../services/phone';
import { VerificationPage } from '../verification/verification';
...some lines skipped...
@Component({
  template
})
export class LoginPage implements AfterContentInit {
  phone = '';
 
  constructor(
...some lines skipped...
    private navCtrl: NavController
  ) {}
 
  ngAfterContentInit() {
    this.phoneService.getNumber().then((phone) => {
      if (phone) {
        this.login(phone);
      }
    });
  }
 
  onInputKeypress({keyCode}: KeyboardEvent): void {
    if (keyCode === 13) {
      this.login();

In-order for it to work, be sure to install the following Cordova plug-in:

$ meteor add cordova:[email protected]

Camera

Next - we will grant access to the device's camera so we can send pictures which are yet to exist in the gallery.

We will start by adding the appropriate Cordova plug-in:

$ meteor add cordova:[email protected]

We will bind the click event in the MessagesAttachmentsComponent:

13.5 Added click event for takePicture client/imports/pages/messages/messages-attachments.html
5
6
7
8
9
10
11
      <div class="attachment-name">Gallery</div>
    </button>
 
    <button ion-item class="attachment attachment-camera" (click)="takePicture()">
      <ion-icon name="camera" class="attachment-icon"></ion-icon>
      <div class="attachment-name">Camera</div>
    </button>

And we will use the recently installed Cordova plug-in in the event handler to take some pictures:

1
2
3
4
5
6
 
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import { Component } from [email protected]/core';
import { AlertController, Platform, ModalController, ViewController } from 'ionic-angular';
import { Camera } from 'ionic-native';
import { PictureService } from '../../services/picture';
import { MessageType } from '../../../../imports/models';
import { NewLocationMessageComponent } from './location-message';
...some lines skipped...
    });
  }
 
  takePicture(): void {
    if (!this.platform.is('cordova')) {
      return console.warn('Device must run cordova in order to take pictures');
    }
 
    Camera.getPicture().then((dataURI) => {
      const blob = this.pictureService.convertDataURIToBlob(dataURI);
 
      this.viewCtrl.dismiss({
        messageType: MessageType.PICTURE,
        selectedPicture: blob
      });
    });
  }
 
  sendLocation(): void {
    const locationModal = this.modelCtrl.create(NewLocationMessageComponent);
    locationModal.onDidDismiss((location) => {

Note that take pictures are retrieved as relative paths in the device, but we use some existing methods in the PictureService to convert these paths into the desired format.