[SendBird Calls] React.js Single Component - Audio Only

Published: 2020-02-02
Prepared By: Solutions Engineering @Supertopoz

Background
This document contains a guide to a single component React.JS implementation for a SendBird Calls 1:1 audio-only implementation.

Notes
A sample of the code can be found here.

  • Download
    • npm install
    • npm start - uses Parcel.JS
    • npm build if you want to deploy
  • For details on SendBirds JavaScript SDK then take a look here.
  • For a vanilla JavaScript implementation sample take a look here.
  • To use SendBird Calls you will also need a SendBird Calls enabled
    Dashboard.
    • With the Dashboard use the built in Call Studio and Phonebooth for testing.

The Code
Build your component and add in state options to hold the SendBird call object, the UI state, and your app_id.

Setup

Index.js

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      appId: "APP_ID",
      info: "Waiting...",
      call: "",
      displayPickup: false,
      displayEnd: false,
      displayCall: true
    };

Connect to SendBird
The first step is to get connected SendBird when your component mounts.

index.js

...
  componentDidMount() {
    SendBirdCall.init(this.state.appId);
    this.authenticate()
      .then(() => this.connect())
      .then(() => this.addListener())
      .catch(err => {
        console.log(err)
      });
  }

  connect() {
    return new Promise((resolve, reject) => {
      SendBirdCall.connectWebSocket()
        .then(() => {
          resolve("Connected");
        })
        .catch(() => {
          reject("Websocket Failed");
        });
    });
  }

  authenticate() {
    return new Promise((resolve, reject) => {
      SendBirdCall.authenticate({
        userId: "User3",
        accessToken: undefined
      }, (result, error) => {
        !!error ? reject(error) : resolve(result);
      });
    });
  }

...

Add an Audio tag

Once connected it is possible to receive calls. Although to make an audio call an HTML audio tag is required in the DOM.

Therefore, the component is rendered with an auto-playing audio tag. SendBird’s JavaScript SDK takes care of connecting to the tag and only a reference is needed. The code below also renders a minimal UI with a single button for making accepting and ending calls.

index.js

render() {
  let button;
  if (this.state.displayPickup) button = <button onClick = {() => this.acceptCall()}> Pick Up! </button>
  if (this.state.displayEnd) button = <button onClick = {() => this.endCall()}> Hang Up! </button>
  if (this.state.displayCall) button = <button onClick = {() => this.makeCall()}>Call User2</button>
  return ( < div >
    <div > { this.state.info } </div> 
     < audio id = "remote_audio_tag" autoPlay /> 
     { button} 
    </div>
    );
  }

Receive Calls
The fastest way to check everything is working and hooked up is to use SendBird’s Dashboard Phonebooth tool. With the tool open dial to the user logged into your simple sample, and answer the call.

In order to be able to accept a call then the call needs to be listened for. Currently SendBird’s JavasScript Calls SDK will only signal an incoming call via websocket. Therefore, apply a listener right after you have connected to SendBird. As seen above in getting connected.

This is the listener does all the following tasks:

  • Listens for an incoming call.
  • Sets the audio tag.
  • Passes the incoming call to the components state.
  • Applies three further event listeners which change the ui state depending on events that occur in the call.

index.js

  addIncomingListener() {
    console.log("Initialized & ready...");
    SendBirdCall.addListener(1, {
      onRinging: (call) => {
        call.setRemoteMediaView(document.getElementById('remote_audio_tag'))
        this.setState({call, ...callStates.ringing});
        call.onEstablished = (call) => this.setState(callStates.established);
        call.onConnected = (call) => this.setState(callStates.connected);
        call.onEnded = (call) => this.setState(callStates.ended);
      }
 
    });
  }

In this example once an incoming call is heard then a button appears asking for the call to be answered. Clicking the button accepts the call, and SendBird’s SDK works to connect the two parties. In doing so the call will fire events that the above code is listening for and changing the UI states where needed.

index.js

  acceptCall() {
    this.state.call.accept({callOption:{ audioEnabled: true } });
  }

End Calls
In this example call.end() is called via a button which in turns fires the call ended event and changes the UI state accordingly. 

Index.js
  endCall(){
    this.state.call.end();
  }

Make a Call
For making a call three things need to happen.

  • First initialize the outgoing call including settings, the target audio tag and the person you are calling.
  • Then dial out
  • In the callback of the dial out apply event listeners for the call you are making out with.

Index.js

  makeCall() {
    const dialParams = {
      userId: "User2",
      isVideoCall: false,
      callOption: {
        localMediaView: document.getElementById('remote_audio_tag'),
        audioEnabled: true,
      }
    };
    const call = SendBirdCall.dial(dialParams, (call, error) => {
      if (error) {}
    });
    this.addOutGoingListener(call)
  }

In this case in the callback from dialing the returned call object is passed to a listener function that adds the call to the components state and adds UI state changes depending on the listener for events of the calls.

Index.js

  addDialOutListener(call){
    call.onEstablished = (call) => this.setState({ call,...callStates.established });
    call.onConnected = (call) => this.setState(callStates.connected);
    call.onEnded = (call) => {
      let _this = this;
      this.setState(callStates.ended);
      setTimeout(() => _this.setState({ info: "Waiting..."}), 1000);
    };

}