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
-
In
call-container
inindex.html
add the following HTML code:<button id="answer" style="display: none">Answer</button> <button id="hangup" style="display: none">Hang up</button>
-
Inside the
SinchClientWrapper
#sinchClientListener
onClientStarted
-callback function, add the following code to add a listener for incoming calls: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); }, }); }, }; } }
-
Inside the
UI
-class, add the following functions for handling callback for incoming calls fromSinchClientWrapper
and showing/hiding Answer and Hangup buttons: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; } // ... }
-
If you followed all the steps, the files should look like this:
index.html
<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
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();
-
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.