In the previous section you added the ability to initiate a call. This section describes how to get notified of and handle incoming calls.
If you have not yet initiated a call, follow the steps in Make an audio call.
In the
call-containerofindex.html, add the following HTML:<button id="answer" style="display: none">Answer</button> <button id="hangup" style="display: none">Hang up</button>Inside the
SinchClientWrapper#sinchClientListeneronClientStartedcallback, 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
UIclass, add functions to handle incoming calls and show/hide Answer and Hang Up 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.jsconst 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 and log in as two different users. Enter the second user ID as the callee in the first tab and press Call. Click Answer and you should see
Call establishedin the console and hear audio confirmation. Press Hang Up to end or decline a call.
Now that you've built a simple app to make and receive calls, learn more about the JavaScript SDK.