Join Room For Video Call Using Twilio And Vue.js

Here, we will learn how to create the video call room using Twilio in Vue.js

Step 1: Getting your Twilio API Keys:

    • The primary step to practice Twilio Programmable Video chat is to induce Associate in Nursing access token. Twilio will generate a token practice your API Keys. Twilio uses this token to create sure people practice your app are authorized to undertake to therefore.
    • To use your Twilio API keys, login or check in for a free account. Once you are logged in, observe of the ACCOUNT SID on your dashboard.

  • Next, visit All merchandise & Services by clicking the icon on the left as shown inside the image below:
    On the popup that seems, click on Programmable Video. The Programmable Video dashboard are loaded for you.
  • Next, click on Tools, then click on API Keys, then click on the + icon as seen within the image below:
  • Lastly, input a friendly name within the FRIENDLY NAME input field then click on produce API Key.
  • The page that may be loaded contains your API Keys. author your API SID key, SECRET key, and additionally your ACCOUNT SID key. We’ll build use of those keys within the next section.

Step 2: Produce the Vue application and install the required packages:

npm install twilio-video
npm install axios

Step 3: Open event.js file and add the following in it:

import Vue from 'vue'

export const EventBus = new Vue()

Step 4: Open AddRoom.vue file and add the following in it:

<template>
   <div class="row roomForm">
       <form class="form-inline" @submit.prevent="createNewRoom(room_name)">
           <div class="form-group mb-2">
               <input type="text" class="form-control" v-model="room_name" >
           </div>
           <button type="submit" class="btn btn-primary mb-2 createRoomBotton">Create Room</button>
       </form>
   </div>
</template>

<script>
import { EventBus } from '../event'

export default {
 name: "AddRoom",
 data() {
   return {
    room_name: "",
   }
 },
 methods: {
   createNewRoom(name) {
       if(!name) {
         alert("please provide a room name");
         return
       }

       this.room_name = '';
       EventBus.$emit('new_room', name);
   }
 }
}
</script>

<style>
 .roomForm {
     margin-left: auto;
     margin-right: auto;
     margin-top: 30px;
     width: 100%;
 }
 .createRoomBotton {
   color: #fff;
   background-color: #4d555f;
   border-color: #303840;
   padding: 8px;
   font-weight: bolder;
 }
</style>

Step 5: Open Rooms.vue file and add the following in it:

<template>
  <div class="col-md-3 rooms">
    <div
      class="room"
      v-for="room in rooms"
      v-bind:key="room.id"
      @click="showRoom(room.name)"
    >
      {{ room.name }}
    </div>
    <AddRoom />
    <!-- Imported AddRoom component -->
  </div>
</template>

<script>
import { EventBus } from "../event";
import AddRoom from "./AddRoom.vue";

export default {
  name: "Rooms", 
  data() {
    return {
      rooms: [
        { id: 1, name: "PHP Room" },
        { id: 2, name: "Python Room" },
        { id: 3, name: "Daily standup" },
      ],
      roomCount: 3, 
      loading: false, 
    };
  },
  components: {
    AddRoom, 
  },
  created() {
    EventBus.$on("new_room", (data) => {
      this.roomCount++;
      this.rooms.push({ id: this.roomCount, name: data });
    });
  },

  methods: {
    showRoom(room) {
      EventBus.$emit("show_room", room);
    },
  },
};
</script>

<style scoped>

.rooms > .room {
  border: 1px solid rgb(124, 129, 124);
  padding: 13px;
  margin: 3px 0px;
  color: ghostwhite;
}

.rooms {
  border: 1px solid rgb(64, 68, 64);
  cursor: pointer;
}
</style>

Step 6: Open Log.vue file and add the following in it:

<template>
    <div class="col-md-3 box">
        <div class="log"  v-for="log in logs" v-bind:key="log.id">
            {{log.message}}
        </div>
    </div>
</template>

<script>

import { EventBus } from '../event'

export default {
  name: "Logs",
  data() {
    return {
        logs: [],
        logCount: 0
    }
  },
  created() {
    EventBus.$on('new_log', (message) => {
        this.logs.push( {id: this.logCount, message: message} );

        this.logCount += 1; 
    })
  },
}
</script>

<style scoped>
    .log {
        border: 1px solid rgb(124, 129, 124);
        padding: 13px;
        margin: 3px 0px;
        color: ghostwhite;
    }
</style>

Step 7: Open Video.vue file and add the following in it:

<template>
  <div class="col-md-6 box">
    <div class="roomTitle">
      <span v-if="loading"> Loading... {{ roomName }}</span>
      <span v-else-if="!loading && roomName"> Connected to {{ roomName }}</span>
      <span v-else>Select a room to get started</span>
    </div>
    <div class="row remote_video_container">
      <div id="remoteTrack"></div>
    </div>
    <div class="spacing"></div>
    <div class="row">
      <div id="localTrack"></div>
    </div>
  </div>
</template>

<script>
import { EventBus } from "../event";
import Twilio, {
  // connect,
  // createLocalTracks,
  createLocalVideoTrack,
} from "twilio-video";
import axios from "axios";

export default {
  name: "Video",
  data() {
    return {
      loading: false,
      data: {},
      localTrack: false,
      remoteTrack: "",
      activeRoom: "",
      previewTracks: "",
      identity: "",
      roomName: null,
    };
  },
  props: ["username"], 
  created() {
    EventBus.$on("show_room", (room_name) => {
      this.createChat(room_name);
    });
    window.addEventListener("beforeunload", this.leaveRoomIfJoined);
  },
  methods: {
    async getAccessToken() {
      const data = {
        action: "join_group_video_by_ajax",
        device: "browser",
        user_name: this.username,
        product_id: 5489,
        wave_id: 1,
        order_id: "",
        user_id: 200,
      };
      const formData = new FormData();
      Object.keys(data).forEach((key) => {
        formData.append(key, data[key]);
      });
      return axios.post(
        "http://localhost:30000/token",
        formData
      );
    },

    // Trigger log events
    dispatchLog(message) {
      EventBus.$emit("new_log", message);
    },

    // Attach the Tracks to the DOM.
    attachTracks(tracks, container) {
      tracks.forEach(function (track) {
        container.appendChild(track.attach());
      });
    },

    // Attach the Participant's Tracks to the DOM.
    attachParticipantTracks(participant, container) {
      let tracks = Array.from(participant.tracks.values());
      this.attachTracks(tracks, container);
    },

    // Detach the Tracks from the DOM.
    detachTracks(tracks) {
      tracks.forEach((track) => {
        track.detach().forEach((detachedElement) => {
          detachedElement.remove();
        });
      });
    },

    // Detach the Participant's Tracks from the DOM.
    detachParticipantTracks(participant) {
      let tracks = Array.from(participant.tracks.values());
      this.detachTracks(tracks);
    },

    // Leave Room.
    leaveRoomIfJoined() {
      // eslint-disable-next-line no-debugger

      if (this.activeRoom) {
        this.activeRoom.disconnect();
      }
    },

    // Create a new chat
    createChat(room_name) {

      this.loading = true;
      const VueThis = this;

      this.getAccessToken().then((data) => {
      VueThis.roomName = null;
      const token = data.data.token;
      let connectOptions = {
        name: room_name,
        // logLevel: 'debug',
        audio: true,
        video: { width: 400 },
      };
      // before a user enters a new room,
      // disconnect the user from they joined already
      this.leaveRoomIfJoined();

      // remove any remote track when joining a new room
      document.getElementById("remoteTrack").innerHTML = "";

      Twilio.connect(token, connectOptions)
        .then(function (room) {
          console.log(room);
          // console.log('Successfully joined a Room: ', room);
          VueThis.dispatchLog("Successfully joined a Room: " + room_name);

          // set active toom
          VueThis.activeRoom = room;
          VueThis.roomName = room_name;
          VueThis.loading = false;

          // Attach the Tracks of all the remote Participants.

          room.participants.forEach(function (participant) {
            let previewContainer = document.getElementById("remoteTrack");
            VueThis.attachParticipantTracks(participant, previewContainer);
          });

          // When a Participant joins the Room, log the event.

          room.on("participantConnected", function (participant) {
            VueThis.dispatchLog("Joining: '" + participant.identity + "'");
          });

          // When a Participant adds a Track, attach it to the DOM.

          room.on("trackAdded", function (track, participant) {
            VueThis.dispatchLog(
              participant.identity + " added track: " + track.kind
            );
            let previewContainer = document.getElementById("remoteTrack");
            VueThis.attachTracks([track], previewContainer);
          });

          // When a Participant removes a Track, detach it from the DOM.
          room.on("trackRemoved", function (track, participant) {
            VueThis.dispatchLog(
              participant.identity + " removed track: " + track.kind
            );
            VueThis.detachTracks([track]);
          });

          // When a Participant leaves the Room, detach its Tracks.

          room.on("participantDisconnected", function (participant) {
            VueThis.dispatchLog(
              "Participant '" + participant.identity + "' left the room"
            );
            VueThis.detachParticipantTracks(participant);
          });

          // if local preview is not active, create it
          if (!VueThis.localTrack) {
            createLocalVideoTrack().then((track) => {
              let localMediaContainer = document.getElementById("localTrack");

              localMediaContainer.appendChild(track.attach());
              VueThis.localTrack = true;
            });
          }
        })
        .catch((err) => {
          console.log(err);
        });
      //   });
    },
  },
};
</script>

<style >
.remote_video_container {
  left: 0;
  margin: 0;
  border: 1px solid rgb(124, 129, 124);
}
#localTrack video {
  border: 3px solid rgb(124, 129, 124);
  margin: 0px;
  max-width: 50% !important;
  background-repeat: no-repeat;
}
.spacing {
  padding: 20px;
  width: 100%;
}
.roomTitle {
  border: 1px solid rgb(124, 129, 124);
  padding: 4px;
  color: dodgerblue;
}
</style>

Step 8: Open App.vue file and add the following in it:

<template>
  <div class="container-fluid chat_container" id="app">
   <div class="row" v-if="authenticated">
      <Rooms />
      <Video :username="username" />
      <Logs />
    </div>
    <div class="row" v-else>
      <div class="username">
        <form class="form-inline" @submit.prevent="submitUsername(username)">
          <div class="form-group mb-2">
            <input type="text" class="form-control" v-model="username" />
          </div>
          <button type="submit" class="btn btn-primary mb-2 Botton">
            Submit
          </button>
        </form>
      </div>
    </div> 
  </div>
</template>

<script>
 import Rooms from "./components/Rooms.vue";
 import Video from "./components/Video.vue";
 import Logs from "./components/Log.vue";
 import AddRoom from "./components/AddRoom";
export default {
  name: "App",
  data() {
    return {
      username: "",
      authenticated: false,
    };
  },
  components: {
     Rooms,
     Video,
     Logs,
     AddRoom,
  },
  methods: {
    submitUsername(username) {
      if (!username) {
        return alert("please provide a username");
      }

      this.authenticated = true;
    },
  },
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
  background: #2c3e50;
}
.box {
  border: 1px solid gray;
}

.username {
  margin: 12px auto 7px auto;
  color: wheat;
}

.Botton {
  color: #fff;
  background-color: #4d555f;
  border-color: #303840;
  padding: 8px;
  font-weight: bolder;
}
</style>

Code in action:

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories