Fork me on GitHub

WhatsApp Clone with Meteor and Ionic 2 CLI

Socially Merge Version (Last Update: 2017-02-01)

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 src/services/phone.ts
2
3
4
5
6
7
8
 
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Accounts } from 'meteor/accounts-base';
import { Meteor } from 'meteor/meteor';
import { Platform } from 'ionic-angular';
import { Sim } from 'ionic-native';
 
@Injectable()
export class PhoneService {
...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 src/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 '@angular/core';
import { Alert, AlertController, NavController } from 'ionic-angular';
import { PhoneService } from '../../services/phone';
import { VerificationPage } from '../verification/verification';
...some lines skipped...
  selector: 'login',
  templateUrl: 'login.html'
})
export class LoginPage implements AfterContentInit {
  private 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:

$ ionic plugin add cordova-plugin-sim

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:

$ ionic plugin add cordova-plugin-camera

We will bind the click event in the MessagesAttachmentsComponent:

13.5 Added click event for takePicture src/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:

13.6 Implement takePicture src/pages/messages/messages-attachments.ts
3
4
5
6
7
8
9
 
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import { NewLocationMessageComponent } from './location-message';
import { MessageType } from 'api/models';
import { PictureService } from '../../services/picture';
import { Camera } from 'ionic-native';
 
@Component({
  selector: 'messages-attachments',
...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.