Handling incoming calls

In the previous section we added the capability of initiating the call. This section describes how to get notified of and handle incoming calls.

If you haven't yet initiated a call, go back and follow the steps in that section.

Listening for incoming calls

  1. In call-container in index.html add the following HTML code:
    Copy
    Copied
    <button id="answer" style="display: none">Answer</button>
    <button id="hangup" style="display: none">Hang up</button>
  2. Inside the SinchClientWrapper #sinchClientListener onClientStarted -callback function, add the following code to add a listener for incoming calls:
    Copy
    Copied
    class SinchClientWrapper {
     // ...
     #sinchClientListener() {
       return {
         // ...
         onClientStarted: (sinchClient) => {
           console.log("Sinch - Start client succeded");
           const { callClient } = sinchClient;
           callClient.addListener({
             onIncomingCall: (client, call) => {
               this.ui.onIncomingCall(call);
               this.#callListeners(call);
             },
           });
         },
       };
     }
    }
  3. Inside the UI -class, add the following functions for handling callback for incoming calls from SinchClientWrapper and showing/hiding Answer and Hangup buttons:
    Copy
    Copied
    class UI {
     // ...
     onIncomingCall(call) {
       console.log("Incoming call", call);
       this.audio.srcObject = call.incomingStream;
       this.onAnswer = this.#handleCall("answer", call, this.onAnswer);
       this.onHangup = this.#handleCall("hangup", call, this.onHangup);
     }
    
     onCallEstablished(call) {
       console.log("Call established", call);
       this.#hideElement("answer");
       this.#showElement("hangup");
     }
    
     onCallEnded(call) {
       console.log("Call ended", call);
       this.#hideElement("answer");
       this.#hideElement("hangup");
     }
    
     #handleCall(id, call, oldOnClickFunction) {
       const element = document.getElementById(id);
       element.removeEventListener("click", oldOnClickFunction);
       element.style = "display: block";
       const onClickFunction = () => call[id]();
       element.addEventListener("click", onClickFunction);
       return onClickFunction;
     }
     // ...
    }
  4. If you followed all the steps, the files should look like this:

    index.html

    Copy
    Copied
    <html>
     <head>
       <script src="https://cdn.sinch.com/latest/sinch-rtc-min.js"></script>
     </head>
     <body>
       <div id="login-container">
         <input id="userid" placeholder="Enter user id" type="text" />
         <button id="login">Login</button>
       </div>
       <div id="call-container" style="display: none">
         <input id="callee" placeholder="Enter callee" type="text" />
         <button id="call">Call</button>
         <button id="answer" style="display: none">Answer</button>
         <button id="hangup" style="display: none">Hang up</button>
       </div>
       <script type="module" src="index.js"></script>
     </body>
    </html>

    index.js

    Copy
    Copied
    const APP_KEY = "enter-application-key";
    const APP_SECRET = "enter-application-secret";
    const ENVIRONMENT_HOST = "ocra.api.sinch.com";
    
    class SinchClientWrapper {
     constructor(userId, ui) {
       this.userId = userId;
       this.ui = ui;
    
       const sinchClient = Sinch.getSinchClientBuilder()
         .applicationKey(APP_KEY)
         .userId(userId)
         .environmentHost(ENVIRONMENT_HOST)
         .build();
    
       sinchClient.addListener(this.#sinchClientListener());
       sinchClient.setSupportManagedPush();
       sinchClient.start();
    
       this.sinchClient = sinchClient;
     }
    
     async makeCall(callee) {
       const call = await this.sinchClient.callClient.callUser(callee);
       this.#callListeners(call);
       return call;
     }
    
     #callListeners(currentCall) {
       currentCall.addListener({
         onCallProgressing: (call) => {
           this.ui.onCallProgressing(call);
         },
         onCallEstablished: (call) => {
           this.ui.onCallEstablished(call);
         },
         onCallEnded: (call) => {
           this.ui.onCallEnded(call);
         },
       });
     }
    
     #sinchClientListener() {
       return {
         onClientStarted: (sinchClient) => {
           console.log("Sinch - Start client succeded");
           const { callClient } = sinchClient;
           callClient.addListener({
             onIncomingCall: (client, call) => {
               this.ui.onIncomingCall(call);
               this.#callListeners(call);
             },
           });
         },
    
         onClientFailed: (sinchClient, error) => {
           console.log("Sinch - Start client failed");
           console.error(error);
         },
    
         /**
          * The recommended way to implement this authentication scheme
          * is that the Application Secret should be kept securely on your
          * server-side backend, the signed token should be created and
          * signed on your server, then passed via a secure channel to
          * the application instance and Sinch client running on a device.
          */
         onCredentialsRequired: (sinchClient, clientRegistration) => {
           new JWT(APP_KEY, APP_SECRET, this.userId)
             .toJwt()
             .then(clientRegistration.register)
             .catch((error) => {
               clientRegistration.registerFailed();
               console.error(error);
             });
         },
       };
     }
    }
    
    class UI {
     constructor() {
       this.#handleLogin();
       this.audio = new Audio();
       this.audio.autoplay = true;
       console.log("UI started");
     }
    
     onIncomingCall(call) {
       console.log("Incoming call", call);
       this.audio.srcObject = call.incomingStream;
       this.onAnswer = this.#handleCall("answer", call, this.onAnswer);
       this.onHangup = this.#handleCall("hangup", call, this.onHangup);
     }
    
     onCallProgressing(call) {
       console.log("Call progressing", call);
     }
    
     onCallEstablished(call) {
       console.log("Call established", call);
       this.#hideElement("answer");
       this.#showElement("hangup");
     }
    
     onCallEnded(call) {
       console.log("Call ended", call);
       this.#hideElement("answer");
       this.#hideElement("hangup");
     }
    
     #handleCall(id, call, oldOnClickFunction) {
       const element = document.getElementById(id);
       element.removeEventListener("click", oldOnClickFunction);
       element.style = "display: block";
       const onClickFunction = () => call[id]();
       element.addEventListener("click", onClickFunction);
       return onClickFunction;
     }
    
     #hideElement(id) {
       const element = document.getElementById(id);
       element.style = "display: none";
     }
    
     #showElement(id) {
       const element = document.getElementById(id);
       element.style = "display: block";
     }
    
     #handleLogin() {
       document.getElementById("login").addEventListener("click", () => {
         const userId = document.getElementById("userid").value;
         this.#hideElement("login-container");
         this.sinchClientWrapper = new SinchClientWrapper(userId, this);
         this.#handleMakeCallClick();
         this.#showElement("call-container");
       });
     }
    
     #handleMakeCallClick() {
       document.getElementById("call").addEventListener("click", async () => {
         const callee = document.getElementById("callee").value;
         const call = await this.sinchClientWrapper.makeCall(callee);
         this.audio.srcObject = call.incomingStream;
         this.onHangup = this.#handleCall("hangup", call, this.onHangup);
       });
     }
    }
    
    new UI();
  5. Open two tabs of the app and login as two different users. Enter the second user ID as callee in the first tab and press the "Call" button. Click the "Answer" button and a you should see Call established in the console and hear audio confirmation that a call has been established. You should also be able to press the "Hang Up" button if you want to cancel a current call or not answer.

Next steps

Now that you've built a simple app to make and receive calls, learn more about the JavaScript SDK.

We'd love to hear from you!
Rate this content:
Still have a question?
 
Ask the community.