Calls / JavaScript
Home
/
Calls
/
JavaScript

Calls integration to chat

You can integrate Sendbird Calls to Sendbird Chat to provide users with a seamless experience in using Calls and Chat services by allowing them to make a call within a chat channel. With the implementation of Calls integration to Chat, the call screen will appear when the call starts and when the call ends users will return to the chat view.

Note: To turn on Calls integration to Chat on the Sendbird Dashboard, go to Settings > Chat > Messages.


Benefits

Calls integration to Chat provides the following benefits:

Natively integrated service

Sendbird Calls and Sendbird Chat are provided from the same app to offer an advanced in-app chat experience for users.

Call within channel

Users can directly make a call to anyone in a channel without leaving the app.

Immersive user experience

Smooth transition between Calls and Chat within a chat channel will make the user experience more engaging.


How it works

Since Calls integration to Chat is a way to add call capabilities to Sendbird Chat, it requires an app that uses Chat. If you already have one, you are ready to move to the next step. If you don’t have an app, learn how to set up Sendbird Chat from our About Chat SDK page.


Prerequisites

To use Calls integration to Chat, an environment setup is first needed for both Sendbird Calls and Sendbird Chat using the SDKs. To learn more about each, refer to Sendbird Calls for Javascript Quickstart and Sendbird Chat samples.


Requirements

The minimum requirements for Calls integration to Chat are:

  • Node
  • npm or yarn
  • WebRTC API supported browsers
  • Sendbird Chat sample
  • Sendbird Chat SDK
  • Sendbird Calls SDK

Note: See here to find out if your browser supports WebRTC API. However, the Calls SDK doesn't support legacy WebRTC libraries in some versions of web browsers.


Install the SDKs

To install the Calls SDK and the Chat SDK, do the following steps:

Step 1: Configuration

Sendbird Chat SDK and Sendbird Calls SDK both use singleton interfaces. Since these two SDKs work independently, create appropriate functions that can handle them together.

Step 2: Initialize the Calls SDK and the Chat SDK

Find your application ID from the Sendbird Dashboard to use in the Calls SDK and the Chat SDK.

Light Color Skin
Copy
// SendBirdAction.js
const sb = new SendBirdAction();

this.sb = new SendBird({
    appId
});

// main.js
const sbc = new SendBirdCallAction();

sbc.init();
sbc.onRinging = (call) => {
    const callView = Call.getInstance();
    callView.setCall(call);
    callView.render();
    callView.setRinging();
};

// SendBirdCallAction.js
init() {
    SendBirdCall.init(appId);
    SendBirdCall.addListener(1, {
        onRinging: (call) => {
            if (this.isBusy()) {
                call.end();
            } else if (!call.endResult) {
                if (this.onRinging) {
                    this.onRinging(call);
                }
            }
        }
    });
}

Step 3: Log in to the Calls SDK and the Chat SDK

To log in to the Calls and Chat SDKs, create a function that allows you to authenticate the Calls SDK and the Chat SDK together.

Light Color Skin
Copy
// main.js
sb
    .connect(userid, nickname)
    .then(user => {
        chatLeft.updateUserInfo(user);
        createConnectionHandler();
        createChannelEvent();
        updateGroupChannelTime();
        chatLeft.getGroupChannelList(true);
    })
    .catch(() => {
        redirectToIndex('Sendbird connection failed.');
    });
...
sbc.login(userid)
    .catch(() => {
    redirectToIndex('SendbirdCall login failed.');
    });

// SendBirdAction.js
connect(userId, nickname) {
    return new Promise((resolve, reject) => {
        const sb = SendBird.getInstance();
        sb.connect(
            userId,
            (user, error) => {
                if (error) {
                    reject(error);
                } else {
                    sb.updateCurrentUserInfo(decodeURIComponent(nickname), null, (user, error) => {
                        error ? reject(error) : resolve(user);
                    });
                }
            }
        );
    });
}

// SendBirdCallAction.js
login(userId, accessToken) {
  return SendBirdCall.authenticate({ userId: userId, accessToken: accessToken })
    .then(() => {
      SendBirdCall.connectWebSocket();
    })
    .then(() => {
      //if (this.onLoginSuccess) this.onLoginSuccess();
    })
    .catch(e => {
      throw e;
    });
}

As written above, the Chat SDK is authenticated by using the SendBird.connect() method and the Calls SDK is authenticated by using the SendBirdCall.authenticate() method.

Step 4: Log out from the Calls SDK and the Chat SDK

To log out from the Calls SDK and the Chat SDK, create a function that handles the two SDKs together like it was to log in.

Light Color Skin
Copy
// SendBirdAction.js
disconnect() {
    return new Promise((resolve, reject) => {
        this.sb.disconnect((response, error) => {
            error ? reject(error) : resolve();
        });
    });
}

// SendBirdCallAction.js
logout() {
    SendBirdCall.deauthenticate();
}

Make a call

For integration between the Calls and Chat services, the Calls SDK provides a specific option when dialing. You can provide the group channel URL to a DialParams object of Sendbird Calls as shown below:

Light Color Skin
Copy
// SendBirdCallAction.js
dial(peerId, isVideoCall = false, callOption, channelUrl, callback) {
...
    const call = SendBirdCall.dial({
        userId: peerId,
        isVideoCall: isVideoCall,
        callOption: callOption,
        sendBirdChatOptions: {
            channelUrl
        },
    });
...

});

When a group channel URL is provided to DialParams as shown above, messages containing call information such as calling statuses and duration will be automatically sent to the channel when the call starts and ends.

The messages will contain call information in the plugins field of the BaseMessage instance which can be used to properly show information about the calls.


Create custom UI components

The sample app for Calls integration to Chat is built based on Sendbird Chat. In the chat sample app, every chat message gets rendered by a corresponding method in the Message class. Different types of messages such as user message, file message, and admin message are shown with a corresponding HTMLElement, which is returned by the Message._createElement() method, offering a more intuitive user experience.

For UI components for Calls integration to Chat, add _createCallElement() method to the Message class with a stylesheet. If the _createCallElement() method is called, the returned HTMLElement will show the following when users make or receive a call.

An example of UI components you can create is demonstrated in the following files in the sample app: the _createCallElement() method of the Message.js file and the .call-message class of the message.scss file.


Build UI components

Add the _createCallElement() method to return the necessary HTMLElement for your custom UI component.

Light Color Skin
Copy
_createCallElement() {
    const detail = this.message.plugins.find((plugin) => plugin.vendor === 'sendbird' && plugin.type === 'call').detail;

    const sendbirdAction = SendBirdAction.getInstance();
    const isCurrentUser = sendbirdAction.isCurrentUser(this.message.sender);
    const root = createDivEl({ className: styles['call-message'], id: this.message.messageId });
    setDataInElement(root, MESSAGE_REQ_ID, this.message.reqId);

    const messageContent = createDivEl({ className: styles['message-content'] });
    const nickname = createDivEl({
        className: isCurrentUser ? [styles['message-nickname'], styles['is-user']] : styles['message-nickname'],
        content: `${protectFromXSS(this.message.sender.nickname)} : `
    });
    nickname.addEventListener('mouseover', () => {
        this._hoverOnNickname(nickname, true);
    });
    nickname.addEventListener('mouseleave', () => {
        this._hoverOnNickname(nickname, false);
    });
    nickname.addEventListener('click', () => {
        if (!isCurrentUser) {
            const userBlockModal = new UserBlockModal({ user: this.message.sender, isBlock: true });
            userBlockModal.render();
        }
    });
    messageContent.appendChild(nickname);

    const bubble = createDivEl({ className: styles['bubble-content'] });
    const icon = createDivEl({ className: (detail.is_video_call) ? styles['video-icon'] : styles['audio-icon'] });
    let msgContent;
    if (detail.end_result) {
        msgContent = detail.end_result;
    } else {
        msgContent = (detail.is_video_call) ? 'Video calling...' : 'Voice calling...';
    }
    const msg = createDivEl({ className: styles['message-content'], content: msgContent });
    bubble.appendChild(icon);
    bubble.appendChild(msg);

    messageContent.appendChild(bubble);

    root.appendChild(messageContent);

    return root;
}

Show UI components

To show the registered UI components, refer to the steps below to identify which messages are associated with the Calls SDK.

  1. The BaseMessage class from Sendbird Chat SDK has a field called plugins where you can store additional information to default values. The key-value plugins are delivered as [string: any] dictionary.
  2. When a call is made from the Calls SDK, the plugin field of a message associated with the call will contain the following information: vendor: sendbird, type : call.
  3. Then, for the messages that have these fields, show the UI component created.
  4. In the _createElement() method of the Message.js file from the chat sample, add the following:
Light Color Skin
Copy
if (this.message.isUserMessage()) {
    if (this.message.plugins.some((plugin) => plugin.vendor === 'sendbird' && plugin.type === 'call')) {
        return this._createCallElement();
    } else {
        return this._createUserElement();
    }
}
...

Extract call data from plugins

The below demonstrates a way to extract specific information from the plugin about Sendbird Calls.

Light Color Skin
Copy
const detail = this.message.plugins.find((plugin) => plugin.vendor === 'sendbird' && plugin.type === 'call').detail;

if (detail.end_result) {
    if (detail.end_result === 'completed') {
      const durationInSec = Math.floor(detail.duration / 1000);
      const minutes = `0${Math.floor(durationInSec / 60)}`.slice(-2);
      const seconds = `0${durationInSec % 60}`.slice(-2);
      msgContent = `${minutes}:${seconds}`;
    } else {
      msgContent = detail.end_result;
    }
  } else {
    msgContent = (detail.is_video_call) ? 'Video calling...' : 'Voice calling...';
  }
  const msg = createDivEl({ className: styles['message-content'], content: msgContent });
...

Create call view

The call view provides events for users such as ending a call, muting or unmuting the microphone, or offers local and remote video views on a user’s screen.

To create a call view, refer to CallView.js on our Sendbird Calls for JavaScript Quickstart.


Use message bubble to call

Calls integration to Chat can provide a seamless user experience by allowing users to initiate a new call directly from a channel by tapping the messages that contain call information.

When you create an element for a message bubble, add an onclick event handler to the element which would call the dial() method and allow users to make a call by tapping the bubble.

Light Color Skin
Copy
const bubble = createDivEl({ className: styles['bubble-content'] });
...
bubble.onclick = () => {
    SendBirdCallAction
        .getInstance()
        .dial(peer.userId, detail.is_video_call, {
            localMediaView: null,
            remoteMediaView: null,
            audioEnabled: true,
            videoEnabled: true,
        },
        this.channel.url
    );
};

Other requirements

To enhance the user experience for Calls integration to Chat, refer to Calls integration to Chat for Quickstart and learn more about these essential features.