73

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

}
Mahdi Zarei
  • 5,644
  • 7
  • 24
  • 54
dennismuijs
  • 1,215
  • 1
  • 11
  • 24
  • 2
    can you share the full error message, are you running this on a mobile device? – cjensen Mar 10 '17 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. – dennismuijs Mar 10 '17 at 20:10
  • So what's in your call ended code? – cjensen Mar 12 '17 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 – dennismuijs Mar 12 '17 at 19:16
  • What backend? The JS sdk is client only javascript (uses our serviers of course) – cjensen Mar 13 '17 at 21:44
  • Sorry I meant the minified sinch sdk js file – dennismuijs Mar 13 '17 at 21:47
  • @cjensen Do you have the required information to assist me with this? – dennismuijs Mar 15 '17 at 16:13
  • Does work if you dont rewrite it to angular? not sure what that message means (no angular developer) but it feels like our library dont find the function, or there is an error within the funciton, do you see in your log a error in the function, or is the function never called? Do you have an URL where i can try it? – cjensen Mar 16 '17 at 17:47
  • yes it does work when I leave it as jquery but obviously that is not an option. That is what I thought aswell, could you tell me how that function is called from your library or where I can find a not minified version of the library? There is no more output in the log than I have allready added to the question. I will try to see if I can make an example available online, but in the meantime I could really use the not-minified version of your library – dennismuijs Mar 16 '17 at 18:07
  • can you please check this package [sinch-rtc](https://www.npmjs.com/package/sinch-rtc) – Savaj Patel Feb 13 '20 at 09:58
  • Sorry @SavajPatel I no longer work on this project, maybe some other user can check your solution? – dennismuijs Feb 13 '20 at 10:00

1 Answers1

1

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!');
    })
Sreeram Nair
  • 2,369
  • 12
  • 27