69

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));
  }

}
11
  • 1
    can you share the full error message, are you running this on a mobile device?
    – cjensen
    Mar 10, 2017 at 19:45
  • @cjensen I added the msg, I am running this on a website. the problem happens when I initiate a call from the website to the sinch mobile phone demo app on ios. Mar 10, 2017 at 20:10
  • So what's in your call ended code?
    – cjensen
    Mar 12, 2017 at 19:06
  • I added the component to the question, i expect that the backend is looking for the jquery function that I rewrote to angular Mar 12, 2017 at 19:16
  • What backend? The JS sdk is client only javascript (uses our serviers of course)
    – cjensen
    Mar 13, 2017 at 21:44

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.