Sinch API in an Angular 2 project times out on onCallProgressing

JqueryJsonAngularVoipSinch

Jquery Problem Overview


We implemented Sinch in an angular 2 web application.

Everything works fine, except when I try to call a user using the sinch phone demo.

When the app is running in the foreground it will ring and the connection is made.

When the app is running in the background nothing happens.

In an angular application the onCallProgressing eventlistener is not getting triggered, the page just waits until it spits out this error message:

enter image description here

It seems like the problem lies in the missing jquery functions since I rewrote the project to angular.

I like to know what I can do to fix this issue with the sinch API.

Full stack trace of the error is as follows:

Error: Error executing listener: onCallEnded
at SinchError (http://cdn.sinch.com/latest/sinch.min.js:4:4093) [<root>]
at g.<anonymous> (http://cdn.sinch.com/latest/sinch.min.js:4:18715) [<root>]
at Array.forEach (native) [<root>]
at g.execListener (http://cdn.sinch.com/latest/sinch.min.js:4:18650) [<root>]
at g.mxpHangup (http://cdn.sinch.com/latest/sinch.min.js:4:30318) [<root>]
at g.hangup (http://cdn.sinch.com/latest/sinch.min.js:5:12013) [<root>]
at g.<anonymous> (http://cdn.sinch.com/latest/sinch.min.js:5:4195) [<root>]
at Zone.runTask (http://localhost:4200/polyfills.bundle.js:6135:47) [<root> => <root>]
at ZoneTask.invoke (http://localhost:4200/polyfills.bundle.js:6329:33) [<root>]
at data.args.(anonymous function) (http://localhost:4200/polyfills.bundle.js:7360:25) [<root>]

This is the angular component that manages the calling using sinch:

import {Component, AfterViewInit, ViewChild, OnInit, OnDestroy} from '@angular/core';
import {Router, ActivatedRoute} from "@angular/router";
import {HttpService} from "../http.service";
import 'rxjs/add/operator/map';
import {Response} from "@angular/http";
import {Employee} from "../employee";
import {Subscription} from "rxjs/Rx";
import {TimeOutService} from "../../time-out.service";

declare var SinchClient: any;

@Component({
  selector: 'mp-callpage',
  templateUrl: './callpage.component.html',
  styleUrls: ['./callpage.component.scss']
})
export class CallpageComponent implements OnInit, OnDestroy {
  public contact: Employee;
  public _video;
  public _audio;
  public volume: number = 0.2;
  public vol: number = 20;
  public volumechange: boolean = false;
  private volumefade = [];
  id: number;
  private sub: Subscription;

  sinchClient: any;
  public callUserName = "";
  public incomingVideoSource = "";
  private callClient;
  private call;

  constructor(private router: Router, private route: ActivatedRoute, private http: HttpService, private timeOutService: TimeOutService) {
  }

  ngOnInit() {

    this.sub = this.route.params.subscribe(params => {
      this.id = +params['id'];
      if (this.id != 0) {
        //noinspection TypeScriptValidateTypes
        this.http.getData('employees', 'orderBy="id"&equalTo=' + this.id)
          .map((response:Response) => response.json())
          .subscribe(
            (data:Employee) => {
              for (var property in data) {
                if (data.hasOwnProperty(property)) {
                  this.contact = data[property];
                }
              }
            }
          );
      } else {
        this.contact = new Employee({
          name: "the reception",
          id: 0
        }, "the receptionist", "the receptionist", 0, false, "0000")
      }
    });


    var _self = this;

    this.sinchClient = new SinchClient({
      applicationKey: 'xxx',
      capabilities: {calling: true, video: true}
    });

    /*** Name of session, can be anything. ***/
    var sessionName = 'sinchSessionVIDEO-' + this.sinchClient.applicationKey;


    /*** Check for valid session. NOTE: Deactivated by default to allow multiple browser-tabs with different users. ***/
    var sessionObj = JSON.parse(localStorage[sessionName] || '{}');
    console.log(sessionObj);
    if(sessionObj.userId) {
      this.sinchClient.start(sessionObj)
        .then(function() {
          localStorage[sessionName] = JSON.stringify(_self.sinchClient.getSession());
          console.log("a valid session was found")
        }).fail(function(response) {
        console.log(response);
      });
    }else {
      console.log("no user id");
    }


    /*** Set up callClient and define how to handle incoming calls ***/
    this.callClient = this.sinchClient.getCallClient();
    this.callClient.initStream().then(function() { // Directly init streams, in order to force user to accept use of media sources at a time we choose
      // $('div.frame').not('#chromeFileWarning').show();
    });

  }

  /*** Define listener for managing calls ***/
  private callListeners = {
    onCallProgressing: (call) => {
      this._audio.play();
    },
    onCallEstablished: (call) => {
      this._video.src = call.incomingStreamURL;
      this._audio.pause();
      this._audio.currentTime=0;
    },
    onCallEnded: (call) => {
      this._video.src = '';
      this.onCallEnd('/');
      if(call.error) {
        console.log("there was an error" + call.error.message);
      }
    }
  };

  /*** Make a new data call ***/
  onCall() {
    this.call = this.callClient.callUser(this.callUserName);

    this.timeOutService.inCall = true;
    this.timeOutService.interacted();

    this.call.addEventListener(this.callListeners);
  }

  @ViewChild('video') video:any;
  @ViewChild('ringback') audio:any;

  ngAfterViewInit () {
    this._audio = this.audio.nativeElement;
    this._video = this.video.nativeElement;
    if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({ video: true, audio: true })
        .then(stream => {
          this._video.src = '';
          this._video.play();
          this._video.volume = 0.2;
        })
    }

  }
  onVolumeChange(direction: string) {
    for (var i = 0; i < this.volumefade.length; i++) {
      clearTimeout(this.volumefade[i]);
    }
    this.volumefade = [];
    this.volumechange = true;
    if (direction == 'down' && this._video.volume > 0.15) {
      this._video.volume -= 0.1;
      this.volume -= 0.1;
    }else if (direction == 'up' && this._video.volume <= 0.9) {
      this._video.volume += 0.1;
      this.volume += 0.1;
    }
    this.volume = Math.round( this.volume * 10 ) / 10;
    this.vol = this.volume * 100;
    this.volumefade.push(setTimeout(() => { this.volumechange = false; }, 2000));
  }

  /*** Hang up a call ***/
  onCallEnd(btn) {
    if(this.call.getEndCause() != 'HUNG_UP'){
      this.call && this.call.hangup();
    }
    this.navHome(btn);
  }

  navHome(url) {
    setTimeout(() => { this.router.navigate([url]); }, 0);
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
    this.timeOutService.inCall = false;
    this.timeOutService.resetTimer.push(setTimeout(() => { this.router.navigate(['/']); window.location.reload() }, 100));
  }

}

Jquery Solutions


Solution 1 - Jquery

It worked for me by using the sinch-rtc as Savaj Patel mentioned.

Example:

sinchClient = new SinchClient({
	applicationKey: 'YOUR KEY',
	capabilities: {messaging: true},
	onLogMessage: function(message) {
		console.log(message);
	}
	});

sinchClient.start(CREDENTIALS).then(function() {
		console.log('Success!');
	})

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondennismuijsView Question on Stackoverflow
Solution 1 - JquerySreeram NairView Answer on Stackoverflow