Prerequisites
Basic knowledge of JavaScript and Vue.js will help you get the best out of this tutorial. To make it easy for everyone to follow along, I will endeavor to break down any complex implementation. In addition, you will also need to ensure that you have Node runtime and npm installed on your computer. Click here to install Node and follow this link to install npm if otherwise.
Introduction
Irrespective of the size of your web application, a voice, and video chat feature is an addon that will not only allow your users to communicate in real time, have a face to face interaction or meeting without necessarily being in the same location or region but also improves the engagement and interactivity of your application. While the implementation of voice and video chat might sound so cool, trust me, you don’t want to build this from scratch. This is where an awesome tool like CometChat really shines.
So rather than build a backend for your chat application from scratch, you can easily build the entire functionalities using CometChat API which will enable you to build communication features like voice and video chat in real-time.
Together in this tutorial, we will build a voice and video chat application by leveraging on some of the awesome API made available by CometChat. You will be able to run this application on two separate windows(browser) locally and have the ability to make, receive and reject a call amongst other things after a successful implementation. Once we are done, you would have built an application similar to:
This application will be built with Vue.js and CometChat Pro SDK. The complete source code for this tutorial can be found here on GitHubif you will prefer to head straight into the code.
Getting started
To begin, we will create and install a new Vue.js application using an awesome tool named Vue CLI. This is a standard tool created by the team at Vue.js to help and allow developers to quickly scaffold a new project without hassle. Run the following command from the terminal to install it globally on your computer:
npm install -g @vue/cli
Once the installation is complete, proceed to use the vue command to create a new Vue.js project as shown here:
vue create comet-voice-video
Choose the “manually select features” options by pressing Enter on your keyboard and check the features you will be needing for this project by pressing space on your computer to select one. As shown below you should select Babel, Router, and Linter / Formatter:
For other instructions, type y to use history mode for the router. Ideally, the default mode for Vue-Router is hash(#) mode as it uses the URL hash to simulate a full URL so that page won’t be reloaded when the URL changes. Choosing history mode here will help to get rid of the hash mode in order to achieve URL navigation without a page reload and add this configuration to the router file that will be generated automatically for this project. In addition, select ESLint with error prevention only in order to pick a linter / formatter config. Next, select Lint on save for additional lint features and save your configuration in a dedicated config file for future projects. Type a name for your preset, I named mine vuecomet:
Immediately after the configuration, Vue CLI will start the installation of the application and install all its required dependencies in a new folder named comet-voice-video.
Start the application
Now that the installation of the new application is completed, move into the new project and start the development server with:
// move into the app
cd comet-voice-video
// start the server
npm run serve
View the welcome page of the application on http://localhost:8080:
In addition, since we will be depending on CometChat Pro to easily build our application, let’s install the SDK before proceeding with the video chat implementation. Stop the development server from running by hitting CTRL + C on your machine and run the following command from the project directory:
npm install @cometchat-pro/chat --save
Now we can easily import CometChat object wherever we want to use CometChat within our application like this:
import { CometChat } from '@cometchat-pro/chat';
Create the CometChat Pro account, APP ID and API Key
Since we will be leveraging on the hosted service of CometChat to successfully build our voice and video chat application, head over to the website here and create a free CometChat Pro account. Fill in all the required information to set up a trial account.
Log in to view your CometChat dashboard and let’s create a new project. This will give us access to a unique APP ID and an API Key
In the ‘Add New App’ dialog, enter a name and click the plus sign to create a new application. Once you are done, click on the Explore button for the new app created. You will be redirected to a new page as shown below:
Next, from the left side menu, go to “API Keys” tab and you will see a page similar to this:
Immediately after you created a new application from your dashboard, CometChat automatically generated an API Key for the new demo application for you. You don’t need to create a new one as this will suffice and give you full access to the functionality offered by CometChat. Don’t forget to note or better still, copy the automatically-generated Full access API Key and application ID as we will need these shortly.
Now that we are done setting up all the necessary tools and credentials needed to successfully create our application, we will start building properly in a bit.
What we want to achieve
Before we start building the application properly, let’s quickly discuss the application structure and how we intend to structure the flow.
Basically, we want users to log in from different locations and be able to chat using voice and video once we host our application on a live server, but for the sake of this tutorial, we will use two different windows locally. Once the user logs in:
we will redirect to a different page where he or she can input the UID of another user and start a video chat. Each user of CometChat is uniquely identified using his or her UID, you can consider this or relate it with a typical unique primary ID of the user from your database, which gives an opportunity to identify such user:
Initialize CometChat
To begin, the typical workflow when using CometChat requires that the settings for CometChat must be initialized by calling the init() method before any other method from CometChat. To start, create a new file named .env within the root directory of the application and paste the following code in it:
// .env
VUE_APP_COMMETCHAT_API_KEY=YOUR_API_KEY
VUE_APP_COMMETCHAT_APP_ID=YOUR_APP_ID
This will make it very easy to reference and use our application credentials within our project. Don’t forget to replace YOUR_API_KEY
and YOUR_APP_ID
placeholder with the appropriate credentials as obtained from your CometChat dashboard.
Next, navigate to ./src/App.vue file which is the root component for Vue.js applications and replace its content with:
// ./src/App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
import { CometChat } from "@cometchat-pro/chat";
import "./App.css";
export default {
data() {
return {};
},
created() {
this.initializeApp();
},
methods: {
initializeApp() {
var appID = process.env.VUE_APP_COMMETCHAT_APP_ID;
CometChat.init(appID).then(
() => {
console.log("Initialization completed successfully");
},
error => {
console.log("Initialization failed with error:", error);
}
);
}
}
};
</script>
What we have done here is to include the functional component that will render any matched component for a given path fromVue Router. We will configure the router later in this tutorial. Next, within the section, we imported the CometChat object and a CSS file that we will create next. Lastly, we initialize CometChat by passing the application ID as a parameter.</p> <p>Now create a new file named <code>App.css</code> within <code>./src/App.css</code> and paste the following content in it:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/App.css @import 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css'; @import 'https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'; #app { font-family: "Avenir", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } #nav { padding: 30px; } #nav a { font-weight: bold; color: #2c3e50; } #nav a.router-link-exact-active { color: #42b983; } #auth { width: 600px; margin: 0 auto; } #callScreen { width: 500px; height: 500px; } .home { width: 600px; margin: 0 auto; } </code></pre></div> <p></p> <p>We imported the CDN files for <a href="https://getbootstrap.com/">Bootstrap</a> and <a href="https://fontawesome.com/">Font awesome</a> and then proceeded to add some default style for the application. Feel free to modify this content as you deem fit.</p> <h3> <a name="login-component" href="#login-component" class="anchor"> </a> Login component </h3> <p>One of the key concept when building chat applications with CometChat is to ensure that users are authenticated before they can have access to use CometChat and start a chat. To ensure this, we will create a Login component that will handle the logic for authenticating a user and redirecting such user to the appropriate page for a chat.</p> <p>To begin, create a new folder named auth within the views folder and within the newly created folder, create a new file and call it <code>Login.vue</code>. Open this new file and paste the following contents:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/views/auth/Login.vue <template> <div id="auth"> <div id="nav"> <router-link to="/login">Login</router-link> </div> <p> Enter your username to start video chat </p> <p>Create an account through your CometChat dashboard or login with one of our test users (superhero1, superhero2)</p> <form v-on:submit.prevent="authLoginUser"> <div class="form-group"> <input name="username" id="username" class="form-control" placeholder="Enter your username" v-model="username"/> </div> <div class="form-group"> <button type="submit" class="btn btn-success"> Login <span v-if="showSpinner" class="fa fa-spin fa-spinner"></span></button> </div> </form> </div> </template> <script> import { CometChat } from "[@cometchat](http://twitter.com/cometchat)-pro/chat"; export default { data() { return { username: "", showSpinner: false }; }, methods: { authLoginUser() { var apiKey = process.env.VUE_APP_COMMETCHAT_API_KEY; this.showSpinner = true; CometChat.login(this.username, apiKey).then( () => { this.showSpinner = false; this.$router.push({ name: "home" }); }, error => { this.showSpinner = false; console.log("Login failed with error:", error.code); } ); } } }; </script> </code></pre></div> <p></p> <p>What we have done here is pretty simple. First, we included an HTML form and added an input field that will accept the username of a user during the authentication process. Once the form is submitted, it will be processed using a method named authLoginUser().</p> <p>Next within the <script> tag, we imported the CometChat object and created the authLoginUser() method attached to the form. Within the authLoginUser() method, we called the login() method from CometChat and passed our application API and the username to uniquely identify the user.</p> <p>Once the user logs into the application, we used the <a href="https://router.vuejs.org/">Vue Router</a> to redirect the user to the home page which will be handled by a different component name HomeComponent. We will create that in the next section.</p> <h3> <a name="home-component" href="#home-component" class="anchor"> </a> Home component </h3> <p>In chronological order, what we want to achieve with this component is:</p> <ul> <li>Display the <strong>UID</strong> of the currently logged in user</li> <li>Initiate a video chat</li> <li>Accept or reject a call</li> <li>And finally, be able to log out of the application.</li> </ul> <p>Let’s start adding these functionalities. To begin, open <code>./src/views/Home.vue</code> and replace its content with:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/views/Home.vue <template> <div class="home"> <div id="nav"> <button class="btn btn-success" [@click](http://twitter.com/click)="logoutUser">Logout</button> </div> <div class="form-group"> <div class="form-group"> <p>Welcome <b>{{ this.username}}</b>, your UID is <b>{{ this.uid }}</b> <br> Enter the receiver Id to start a chat </p> <p v-if="error"> <b class="text-danger"> Receiver ID is required </b> </p> <input type="text" class="form-control" placeholder="Enter receiver UID" v-model="receiver_id"> </div> <div v-if="incomingCall"> <button class="btn btn-success" [@click](http://twitter.com/click)="acceptCall">Accept Call</button> <button class="btn btn-success" [@click](http://twitter.com/click)="rejectCall">Reject Call</button> </div> <div v-else-if="ongoingCall"> <button class="btn btn-secondary"> Ongoing Call ... </button> </div> <div v-else> <button [@click](http://twitter.com/click)="startVideoChat" class="btn btn-secondary"> Start Call <span v-if="showSpinner" class="fa fa-spin fa-spinner"></span> </button> </div> </div> <div id="callScreen"></div> </div> </template> </code></pre></div> <p></p> <p>Here, we added contents to display the username and the UID of the logged in user. Next, we included an input field that will accept the UID of the receiver in order to uniquely identify such user and be able to initiate or send a call request.</p> <p>Once a call request has been received, the receiver has an option of either rejecting or accepting the call. We have included click events for this process and will define the methods next.</p> <p><strong>Get the logged in user</strong></p> <p>First, let’s retrieve the details of the currently logged in user by adding this <script> section to our code. Place the contents below within the Home.vue immediately after the closing tag of the <template> section:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/views/Home.vue <script> import { CometChat } from "[@cometchat](http://twitter.com/cometchat)-pro/chat"; export default { name: "home", data() { return { username: "", uid: "", session_id: "", receiver_id: null, error: false, showSpinner: false, incomingCall: false, ongoingCall: false }; }, created() { this.getLoggedInUser(); }, methods: { getLoggedInUser() { var user = CometChat.getLoggedinUser().then( user => { this.username = user.name; this.uid = user.uid; }, error => { this.$router.push({ name: "homepage" }); } ); }, } }; </script> </code></pre></div> <p></p> <p>Basically, we imported the CometChat method, defined some properties and corresponding initial values within the data option. And finally, once the component is created, we called a method named <code>getLoggedInUser()</code> to automatically retrieve the details of a logged in user and update the view with it.</p> <p><strong>Start video chat</strong></p> <p>To initiate a chat from the app, we will add this method to the function associated with the Vue instance:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/views/Home.vue <script> import { CometChat } from "[@cometchat](http://twitter.com/cometchat)-pro/chat"; export default { ... methods: { ... startVideoChat() { if (!this.receiver_id) this.error = true; this.showSpinner = true; var receiverID = this.receiver_id; var callType = CometChat.CALL_TYPE.VIDEO; var receiverType = CometChat.RECEIVER_TYPE.USER; var call = new CometChat.Call(receiverID, callType, receiverType); CometChat.initiateCall(call).then( outGoingCall => { this.showSpinner = false; console.log("Call initiated successfully:", outGoingCall); // perform action on success. Like show your calling screen. }, error => { console.log("Call initialization failed with exception:", error); } ); } } }; </script> </code></pre></div> <p></p> <p>This method will be used to specify the type of call and that of a receiver. For our application, this will be a <code>CALL_TYPE.VIDEO</code> and <code>RECEIVER_TYPE.USER</code> respectively. Finally, we passed these details through an object of call into the <code>initiateCall()</code> method from the CometChat API to send a call request.</p> <p><strong>Listen and receive calls</strong></p> <p>Once a call has been initiated, we need to listen to the call events within our application and based on the actions that will be carried out by the user to either receive or reject the call, we will call the appropriate method. To do this successfully, we need to register the CallListener listener using the <code>addCallListener()</code> method from CometChat. Add the following contents within the created() method:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/views/Home.vue <script> import { CometChat } from "[@cometchat](http://twitter.com/cometchat)-pro/chat"; export default { ... created() { ... let globalContext = this; var listnerID = "UNIQUE_LISTENER_ID"; CometChat.addCallListener( listnerID, new CometChat.CallListener({ onIncomingCallReceived(call) { console.log("Incoming call:", call); globalContext.incomingCall = true; globalContext.session_id = call.sessionId; }, onOutgoingCallAccepted(call) { console.log("Outgoing call accepted:", call); globalContext.ongoingCall = true; CometChat.startCall( call.sessionId, document.getElementById("callScreen"), new CometChat.OngoingCallListener({ onUserJoined: user => { /* Notification received here if another user joins the call.*/ console.log("User joined call:", user); /* this method can be use to display message or perform any actions if someone joining the call */ }, onUserLeft: user => { /* Notification received here if another user left the call.*/ console.log("User left call:", user); /* this method can be use to display message or perform any actions if someone leaving the call*/ }, onCallEnded: call => { globalContext.ongoingCall = false; globalContext.incomingCall = false; /* Notification received here if current ongoing call is ended.*/ console.log("Call ended:", call); /* hiding/closing the call screen can be done here.*/ } }) ); // Outgoing Call Accepted }, onOutgoingCallRejected(call) { console.log("Outgoing call rejected:", call); this.incomingCall = false; this.ongoingCall = false; this.receiver_id = ""; // Outgoing Call Rejected }, onIncomingCallCancelled(call) { console.log("Incoming call calcelled:", call); } }) ); }, methods: { ... } }; </script> </code></pre></div> <p></p> <p>Once the recipient accepts an outgoing call in the <code>onOutgoingCallAccepted()</code> callback of the CallListener, we called another method <code>startCall()</code> which takes the</p> <ul> <li>sessionId: The unique session ID available in the CometChat.Call object,</li> <li>callScreen: DOM element where you want to show the call user interface and</li> <li>OngoingCallListener: a CometChat.OngoingCallListener where the real-time events will be received</li> </ul> <p>as parameters.</p> <p><strong>Accept and reject calls</strong></p> <p>Once an incoming call event is received, we will call a method named <code>acceptCall()</code>. This method also takes the unique session ID available in the CometChat.Call object as a parameter. Add this method immediately after the <code>startVideoChat()</code> method:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/views/Home.vue <script> import { CometChat } from "[@cometchat](http://twitter.com/cometchat)-pro/chat"; export default { ... methods: { ... acceptCall() { let globalContext = this; this.ongoingCall = true; this.incomingCall = false; var sessionID = this.session_id; CometChat.acceptCall(sessionID).then( call => { console.log("Call accepted successfully:", call); console.log("call accepted now...."); // start the call using the startCall() method console.log(globalContext.ongoingCall); CometChat.startCall( call.sessionId, document.getElementById("callScreen"), new CometChat.OngoingCallListener({ onUserJoined: user => { /* Notification received here if another user joins the call.*/ console.log("User joined call:", user); /* this method can be use to display message or perform any actions if someone joining the call*/ }, onUserLeft: user => { /* Notification received here if another user left the call.*/ console.log("User left call:", user); /* this method can be use to display message or perform any actions if someone leaving the call*/ }, onCallEnded: call => { /* Notification received here if current ongoing call is ended.*/ console.log("Call ended:", call); globalContext.ongoingCall = false; globalContext.incomingCall = false; /* hiding/closing the call screen can be done here.*/ } }) ); }, error => { console.log("Call acceptance failed with error", error); // handle exception } ); } } }; </script> </code></pre></div> <p></p> <p>Here, once an incoming call has been accepted, we then called the startCall() method. With this in place, the sender and the recipient can start a chat. Next, add another method to reject calls:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/views/Home.vue <script> import { CometChat } from "[@cometchat](http://twitter.com/cometchat)-pro/chat"; export default { ... methods: { ... rejectCall() { var sessionID = this.session_id; var globalContext = this; var status = CometChat.CALL_STATUS.REJECTED; CometChat.rejectCall(sessionID, status).then( call => { console.log("Call rejected successfully", call); globalContext.incomingCall = false; globalContext.ongoingCall = false; globalContext.receiver_id = ""; }, error => { console.log("Call rejection failed with error:", error); } ); } } }; </script> </code></pre></div> <p></p> <p>And lastly, add another method to Log out users from the application and redirect back to the login page:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/views/Home.vue <script> import { CometChat } from "[@cometchat](http://twitter.com/cometchat)-pro/chat"; export default { ... methods: { ... logoutUser() { CometChat.logout().then( success => { console.log("Logout completed successfully"); this.$router.push({ name: "homepage" }); console.log(success); }, error => { //Logout failed with exception console.log("Logout failed with exception:", { error }); } ); } } }; </script> </code></pre></div> <p></p> <p>The <code>CometChat.logout()</code> will log the user out of CometChat and also from our application.</p> <p>In case you miss anything, the complete contents of the Home.vue file can be <a href="https://github.com/yemiwebby/comet-voice-video/blob/master/src/views/Home.vue">found here</a>.</p> <h3> <a name="update-router" href="#update-router" class="anchor"> </a> Update router </h3> <p>Earlier, when we installed Vue.js, we also selected an option of installing Vue Router with it. So open <code>./src/router.js</code> and replace the default content in it with the following:</p> <p></p> <div class="highlight"><pre class="highlight plaintext"><code>// ./src/router.js import Vue from 'vue' import Router from 'vue-router' import Home from './views/Home.vue' import Login from './views/auth/Login.vue' Vue.use(Router) export default new Router({ mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', redirect: 'login', name: 'homepage' }, { path: '/login', name: 'login', component: Login, }, { path: '/home', name: 'home', component: Home } ] }) </code></pre></div> <p></p> <p>What we have done here is to map each route within our application to the respective component that will handle the logic. This is a basic format of configuring Vue Router for a Vue.js application. Click <a href="https://router.vuejs.org/">here</a> to find out more about Vue Router.</p> <h3> <a name="test-the-application" href="#test-the-application" class="anchor"> </a> Test the application </h3> <p>Now that you are done adding all the required logic and components, you can now run the application and test it out. Restart the application with npm run serve and navigate to <a href="http://localhost:8080">http://localhost:8080</a> to view the app.</p> <p><img src="https://cdn-images-1.medium.com/max/688/0*qLJzgX9dJ-ZnIB5U.png" alt=""></p> <p>Open the application in two separate windows and login with any two of the test users: superhero1, superhero2, superhero3, superhero4 or superhero5</p> <p><img src="https://cdn-images-1.medium.com/max/600/0*h2sDQ5lxWHDxWN6p.gif" alt=""></p> <p>Once you are able to log in from both window, enter the UID of one user and click on <strong>Start Call</strong> and once the call request is received by the other user, two new action buttons will be displayed. Click <strong>Accept Call</strong> to accept a call</p> <p><img src="https://cdn-images-1.medium.com/max/600/0*JgKRdftJdW8sg0CR.gif" alt=""></p> <p>or <strong>Reject Call</strong> to reject a call:</p> <p>If you have followed the tutorial diligently up until now, then you should have a functional voice and video chat application running on your computer.</p> <h3> <a name="conclusion" href="#conclusion" class="anchor"> </a> Conclusion </h3> <p>As seen in this tutorial, you will agree with me that it is so easy to quickly implement a video chat feature in your project. This can be easily integrated into an existing Vue.js application or a new one entirely.</p> <p>Feel free to explore the source code as linked at the beginning of this tutorial and add more features as you deem fit.</p> <p>With CometChat, you can easily extend the functionalities of this application by adding more awesome features. <a href="https://prodocs.cometchat.com/">Check the official documentation of CometChat Pro</a> to read more about what its got to offer.</p> <p>I do hope you found this tutorial quite helpful? Can’t wait to see what you will build with the knowledge that you have acquired here.</p> <p>Originally published on <a href="https://www.cometchat.com">CometChat</a></p>
Top comments (1)
Great post! Do you know how can record voice calling in cometchat?