DEV Community

Cover image for Let’s Build a WhatsApp Clone Using Vue.js and Firebase
Nil Madhab
Nil Madhab

Posted on • Originally published at simplecoding.dev

4 1

Let’s Build a WhatsApp Clone Using Vue.js and Firebase

Let’s Build a WhatsApp Clone Using Vue.js and Firebase

How to build a clone of WhatsApp Web Using Vue.js, Firebase, and Spring Boot.

Whatspp Clone for realtime chat

Introduction

We use a lot of social media platforms every day, like Facebook, LinkedIn, Twitter, Whatsapp, Instagram. One of the most used features every day is real time chat. In this tutorial, we are going to build one, using Vue.js and Firebase.

This is part of a series of tutorials, where we will be building a fully functional social networking app, where users can log in, post, add friends, and chat.

Previously, we used Firebase to create a social login using Spring Boot in the backend and Vue.js in the frontend.
Let’s Build a Robust Social Login Using Spring Boot, Vue.js and Firebase

Live Demo:
simplecoding-social

Github code for this tutorial:
webtutsplus/social-network-frontend

Project Structure

Relevant files for this tutorial

Let’s Build the Chat page

Let's build the view for the entire page, it has two components:

  1. ChatSidebar

  2. ChatView

ChatSidebar and ChatView components

<template>
<div v-if="fetched" class="app">
<div class='app__body'>
<ChatSidebar :rooms="rooms"/>
<ChatView :room="firstRoom" />
</div>
</div>
<div v-else>
<md-progress-spinner md-mode="indeterminate"></md-progress-spinner>
</div>
</template>
view raw Chat.vue hosted with ❤ by GitHub

For each friend, we have a room name, which we store in the backend. And we retrieve it via this API.
getRooms(){
axios.get(`${API_BASE_URL}private/rooms`,{'headers' :{
'Authorization': 'Bearer '+localStorage.getItem('idToken'),
}}).then(resp => {
this.fetched =true
if(resp.status === 200){
this.rooms = resp.data;
console.log("rooms", this.rooms);
if (this.rooms.length > 0) {
//update chatview by first user
console.log("update chatview by first room", this.rooms[0]);
this.firstRoom = this.rooms[0];
}
}
}).catch((err) => {
console.log("coming in error", err);
this.$router.push('/login');
});
}
view raw Chat.js hosted with ❤ by GitHub

Complete code for the above can be found here:
webtutsplus/social-network-frontend

Response of the API

[
{
"user": {
"id": 2,
"uid": "SwNxVd6k3vTXMNNW1uZDaK6KsUr1",
"name": null,
"email": "nilmadhab1995@gmail.com",
"role": null,
"picture": "https://avatars.githubusercontent.com/u/75689408?v=4"
},
"meetingRoom": "51285a8f-fe7f-40db-899d-3ad0ff591aaf"
},
{
"user": {
"id": 3,
"uid": "VeBelmJoAkV6YJulO55Y7qAKWh02",
"name": null,
"email": "test@gmail.com",
"role": null,
"picture": null
},
"meetingRoom": "73130e31-4e55-4603-8c45-a607d26da18e"
},
{
"user": {
"id": 4,
"uid": "fD5IEeAC5qNgkx9GVxsqLrsAOlQ2",
"name": "Smit Tailor",
"email": "sjtailor27@gmail.com",
"role": null,
"picture": "https://avatars.githubusercontent.com/u/32813584?v=4"
},
"meetingRoom": "655cd014-1863-4fc6-b8d2-8e0ea6dc1bde"
},
{
"user": {
"id": 5,
"uid": "au1kRnYRw5TrOwNKMk4hIzC4z4n1",
"name": null,
"email": "siuli@gmail.com",
"role": null,
"picture": null
},
"meetingRoom": "0271146c-46d8-41d2-8b76-e0a3390e924c"
}
]
view raw rooms.json hosted with ❤ by GitHub

Once we have the rooms information, we send it to the child components. Let's look at the ChatSidebar component

ChatSidebar Component

It gets the rooms prop from the parent Chat and display a list of SidebarChatUserRow components for each user from the rooms list prop passed from parent Chat.vue.

It also has 3 icons in the top right, which look really good!


<template>
<div class='sidebar'>
<div class='sidebar__header'>
<md-avatar/>
<div class='sidebar__headerRight'>
<md-button class="md-icon-button">
<md-icon>donut_large</md-icon>
</md-button>
<md-button class="md-icon-button">
<md-icon>chat</md-icon>
</md-button>
<md-button class="md-icon-button">
<md-icon>more_vert</md-icon>
</md-button>
</div>
</div>
<div class='sidebar__chat'>
<div v-for="(room, id) in rooms" :key="id">
<SidebarChatUserRow :room="room" />
</div>
</div>
</div>
</template>
<script>
import SidebarChatUserRow from "@/components/Chat/SidebarChatUserRow";
export default {
name: "ChatSidebar",
components : { SidebarChatUserRow },
props : ['rooms'],
}
</script>
<style scoped>
.sidebar{
display: flex;
flex-direction: column;
flex: 0.30;
// more styles
</style>
view raw ChatSidebar.vue hosted with ❤ by GitHub

SidebarChatUserRow component

This component is very simple — it’s just to display the email and avatar for each user. We can add the last message later, if we want.


<template>
<div class='sidebarChat' v-if="!addNewChat" @click="updateChatView()">
<md-avatar>
<img :src="room.user.picture">
</md-avatar>
<div class='sidebarChat__info'>
<h2>{{room.user.email}}</h2>
<p>Last message...</p>
</div>
</div>
<div class='sidebarChat' v-else onClick={createChat()} >
<h2>Add new chat</h2>
</div>
</template>
<script>
export default {
name: "SidebarChatUserRow",
props : ['addNewChat', "room"],
methods : {
updateChatView() {
this.$root.$emit('updateChatViewEvent', this.room);
}
}
}

ChatView component

This is the right side part of the chat, where we display the actual chats. First, we will discuss how we are displaying the chats and then we will see, when we click a different user, how to update the chats.

chat main

By default, we pass the first user’s room, so it has a prop room, which also has the user information (avatar, email) which we display in the header.

From the roomname, we find the associated roomId in Firebase and get all the associated chats and display those chats.

Firebase chats store

getPreviousChats (roomId) {
firebase.database().ref('chatrooms/'+roomId+'/chats').on('value', (snapshot) => {
this.chats = [];
snapshot.forEach((doc) => {
let item = doc.val()
item.key = doc.key
this.chats.push(item)
});
console.log("chats",this.chats);
});
},
getRoomName(room) {
console.log("room name is ", room);
this.ref.orderByChild('roomName').equalTo(room).once('value', snapshot => {
if (snapshot.exists()) {
console.log('Room Exists');
snapshot.forEach((doc) => {
console.log("roomId", doc.key);
this.roomid = doc.key;
this.getPreviousChats(doc.key)
})
} else {
// create a new doc
let newData = this.ref.push()
newData.set({
roomName: room
});
// after creating the room, get the chats again by recursive function
this.getRoomName(room);
}
})
},

Creating a new chat

There is a form, where we hide the send button and onclick action. We prevent the default submit and refresh action of form by v-on:submit.prevent directive.

We get the data for inputMsg div and add a new entry in firebase by adding the chat in room name.

submit form

<template>
// header and body
<div class="chat__footer">
<md-icon>insert_emoticon</md-icon>
<form v-on:submit.prevent="onSubmit">
<input type="text" id="inputMsg"/>
<button type="submit" >send a message</button>
</form>
<md-icon>mic</md-icon>
</div>
</div>
</template>
<script>
import firebase from 'firebase';
export default {
name: "Chat",
props : ['room'],
data () {
return {
chats: [],
ref: firebase.database().ref('chatrooms/'),
roomid: null,
avatar: null,
friend: {}
}
},
methods : {
onSubmit(){
const msg = document.getElementById("inputMsg").value;
console.log("msg on submit", msg);
let newData = firebase.database().ref('chatrooms/'+this.roomid+'/chats').push();
newData.set({
type: 'newmsg',
user: localStorage.getItem("username"),
message: msg,
sendDate: Date()
});
document.getElementById("inputMsg").value = '';
},

Now we have one important function left, which is about selecting a different friend from the Sidechatbar and how to update the chatview.

As they are not a parent-child type, we can’t directly pass prop or update the props. The answer is emitting the event and listening for it.

Emit an event in **SidebarChatUserRow** component

Retrieving the event in Chatview and updating everything

The complete code for the chatview.view component:
webtutsplus/social-network-frontend

In this tutorial, we have covered some really interesting modern JavaScript topics like:

  1. How to pass props.

  2. How to render components.

  3. How to emit events and use them.

  4. How to integrate Firebase.

That's it. You can run the frontend code on your local computer and comment here if anything goes wrong. Thanks for reading!

References

Let’s Build a Robust Social Login Using Spring Boot, Vue.js and Firebase

More content at plainenglish.io

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay