Twilio Conversation In Vue.js

Twilio Conversation:

In this article, we will learn about how to use twilio conversation API in Vue,js

Twilio conversation API is used to make conversation between whatsapp, chats, messages, etc. It is the upgraded version of the Chats API provided by Twilio earlier.

Step 1: Open index.html file and add the following in it:

<script src="https://media.twiliocdn.com/sdk/js/conversations/releases/2.0.1/twilio-conversations.min.js"></script>

Step 2: Add to chat grand in token:

const AccessToken = require('twilio').jwt.AccessToken;
const ChatGrant = AccessToken.ChatGrant;

// Used when generating any kind of tokens
const twilioAccountSid = 'ACxxxxxxxxxx';
const twilioApiKey = 'SKxxxxxxxxxx';
const twilioApiSecret = 'xxxxxxxxxxxx';

// Used specifically for creating Chat tokens
const serviceSid = 'ISxxxxxxxxxxxxx';
const identity = 'user@example.com';

// Create a "grant" which enables a client to use Chat as a given user,
// on a given device
const chatGrant = new ChatGrant({
  serviceSid: serviceSid,
});

// Create an access token which we will sign and return to the client,
// containing the grant we just created
const token = new AccessToken(
  twilioAccountSid,
  twilioApiKey,
  twilioApiSecret,
  {identity: identity}
);

token.addGrant(chatGrant);

Step 3: Create Conversation.vue file and add the following in it:

<template>
  <div class="col-md-12 col-xl-12 conversation-chat">
    <div class="conversation-chat card">
      <div class="card-body">
        <section class="msger">
          <main class="msger-chat" id="msger-chat">
            <div
              class="msg"
              :class="[
                message && getUserName(message.author) === userData
                  ? 'right-msg'
                  : 'left-msg',
              ]"
              v-for="message in messages"
              :key="message.id"
            >
              <div class="msg-bubble" :id="message.index">
                <div class="msg-info">
                  <div class="msg-info-name">
                    {{ getUserName(message.author) }}
                  </div>
                  <div class="options" @click="removeMessage(message)">
                    <span
                      ><b-button
                        variant="danger"
                        v-if="userRole == 'host' || userRole == 'moderator'"
                        ><ph-trash :size="16" /></b-button
                    ></span>
                  </div>
                </div>

                <div class="msg-text">
                  {{ message.body }}
                </div>
              </div>
            </div>
          </main>
        </section>
      </div>
      <div class="card-footer">
        <div class="input-group">
          <textarea
            name=""
            class="form-control type_msg"
            placeholder="Type your message..."
            v-model="message"
            v-on:keyup.enter="addMessage"
          ></textarea>
          <div class="input-group-append">
            <span class="msger-send-btn" @click="addMessage">
              <ph-paper-plane-tilt :size="30"
            /></span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
<script>
/* eslint-disable no-debugger */

import { BButton } from "bootstrap-vue";
import { PhPaperPlaneTilt, PhTrash } from "phosphor-vue";
import EventBus from "./../event";
import axios from "axios";
import config from "../config";
export default {
  components: {
    PhPaperPlaneTilt,
    PhTrash,
    BButton,
  },
  data() {
    return {
      conversations: null,
      client: null,
      conversationSid: "",
      chatConversation: null,
      uniqueName: "",
      message: "",
      messages: [],
      userData: "",
      userRole: "",
    };
  },
  methods: {
    getUserName(authorName) {
      try {
        const canParsed = JSON.parse(authorName);
        if (canParsed && canParsed.u) return canParsed.u;
        return authorName;
      } catch (error) {
        return authorName;
      }
    },
    async initializeChat(token, userData, roomName, roleName, conversationSid) {
      this.userData = userData;
      this.userRole = roleName;
      this.uniqueName = roomName.split(" ").join("");
      const self = this;
      await window.Twilio.Conversations.Client.create(token)
        .then((client) => {
          this.conversationSid = conversationSid;
          if (this.conversationSid) {
            client
              .getConversationBySid(this.conversationSid)
              .then((res) => res)
              .catch((error) => {
                console.log(error);
              })
              .then((conversation) => {
                this.chatConversation = conversation;
                return this.chatConversation.join().catch((error) => {
                  console.log(error);
                });
              })
              .then(() => {
                this.chatConversation
                  .getMessages()
                  .then((messages) => {
                  this.messages = messages.items;
                    setTimeout(() => {
                      const element = document.getElementById("msger-chat");
                      if (element) {
                        element.scrollTop = element.scrollHeight;
                      }
                    }, 0);
                  })
                  .catch((error) => {
                    console.log(error);
                  });
                this.chatConversation.on("messageAdded", (message) => {
                  if (
                    this.isMessageEntered(message)) {
                    this.messages.push(message.state);
                  } else if (
                    this.isMessageEntered(message)) {
                    EventBus.$emit("control-panel-action", {
                      payload: message.state.body,
                      message: message,
                    });
                  }
                  setTimeout(() => {
                    const element = document.getElementById("msger-chat");
                    if (element) {
                      element.scrollTop = element.scrollHeight;
                    }
                  }, 0);
                });
                this.chatConversation.on("messageRemoved", (message) => {
                  this.messages = this.messages.filter(
                    (x) => x.index != message.index
                  );
                });
              });
          }
        })
        .catch((error) => {
          console.log(error);
        });
    },
    isMessageEntered(message) {
      return message.state && message.state.body;
    },
    addMessage(defaultMessage = "") {
      if (this.chatConversation) {
        this.chatConversation.sendMessage(
          typeof defaultMessage === "string" && defaultMessage
            ? defaultMessage
            : this.message
        );
        if (!defaultMessage || typeof defaultMessage === "object") {
          this.message = "";
        }
      }
    },
  },
};
</script>
<style>
.input-group-append {
  background-color: rgba(0, 0, 0, 0.3) !important;
  border-radius: 0 15px 15px 0 !important;
  padding-top: 10px;
  align-items: center;
}
.conversation-chat {
  margin-top: auto;
  margin-bottom: auto;
}
.conversation-chat .card {
  height: 75vh;
  max-height: -webkit-fill-available;
  border-radius: 15px !important;
  background-color: rgba(0, 0, 0, 0.4) !important;
}

.conversation-chat .card-body {
  height: calc(78% - -15px);
  max-height: 70vh;
  /* height: calc(87% - -15px);
  max-height: 80vh;
  overflow: auto; */
}

.card-footer {
  border-radius: 0 0 15px 15px !important;
  border-top: 0 !important;
}
.type_msg {
  background-color: rgba(0, 0, 0, 0.3) !important;
  border: 0 !important;
  color: white !important;
  overflow-y: auto;
  border-radius: 15px 0 0 15px !important;
}
.type_msg:focus {
  box-shadow: none !important;
  outline: 0px !important;
}

:root {
  --body-bg: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
  --msger-bg: #fff;
  --border: 2px solid #ddd;
  --left-msg-bg: #ececec;
  --right-msg-bg: #579ffb;
}

html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  margin: 0;
  padding: 0;
  box-sizing: inherit;
}

.msger {
  display: flex;
  flex-flow: column wrap;
  justify-content: space-between;
  width: 100%;
  max-width: 867px;
  height: calc(100% - -15px);
  border: var(--border);
  border-radius: 5px;
  background: var(--msger-bg);
  box-shadow: 0 15px 15px -5px rgba(0, 0, 0, 0.2);
}

.msger-header {
  display: flex;
  justify-content: space-between;
  padding: 10px;
  border-bottom: var(--border);
  background: #eee;
  color: #666;
}

.msger-chat {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
}
.msger-chat::-webkit-scrollbar {
  width: 6px;
}
.msger-chat::-webkit-scrollbar-track {
  background: #ddd;
}
.msger-chat::-webkit-scrollbar-thumb {
  background: #bdbdbd;
}
.msg {
  display: flex;
  align-items: flex-end;
  margin-bottom: 10px;
}
.msg:last-of-type {
  overflow-wrap: anywhere;
  margin: 0;
}
.msg-img {
  width: 50px;
  height: 50px;
  margin-right: 10px;
  background: #ddd;
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  border-radius: 50%;
}
.msg-bubble {
  max-width: 85%;
  padding: 15px;
  border-radius: 15px;
  background: var(--left-msg-bg);
}
.msg-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}
.msg-info-name {
  margin-right: 10px;
  font-weight: bold;
  font-size: 10px;
  overflow: overlay;
}
.msg-info-time {
  font-size: 0.85em;
}

.left-msg .msg-bubble {
  border-bottom-left-radius: 0;
}

.right-msg {
  flex-direction: row-reverse;
}
.right-msg .msg-bubble {
  background: var(--right-msg-bg);
  color: #fff;
  border-bottom-right-radius: 0;
}
.right-msg .msg-img {
  margin: 0 0 0 10px;
}

.msger-inputarea {
  display: flex;
  padding: 10px;
  border-top: var(--border);
  background: #eee;
}
.msger-inputarea * {
  padding: 10px;
  border: none;
  border-radius: 3px;
  font-size: 1em;
}
.msger-input {
  flex: 1;
}
.msger-send-btn {
  margin-left: 10px;
  color: #fff;
  font-weight: bold;
  cursor: pointer;
  transition: background 0.23s;
  padding: 0.375rem 0.75rem;
}

.msg-text {
  font-size: 12px;
  text-align: left;
}

.msger-chat {
  width: 100%;
  background-color: #fcfcfe;
}

@media (max-width: 576px) {
  .contacts_card {
    margin-bottom: 15px !important;
  }
  .conversation-chat .card {
    height: 40vh !important;
  }
  .card-body {
    height: calc(47% - -15px) !important;
  }
  .msg-bubble {
    padding: 10px !important;
  }
}
@media (max-width: 320px) {
  .conversation-chat .card {
    height: 30vh !important;
  }
}
@media (device-width: 375px) {
  .conversation-chat .card {
    height: 40vh !important;
  }
  .card-body {
    height: calc(62% - -15px) !important;
  }
  textarea::placeholder {
    font-size: 16px;
  }
}
@media (device-width: 768px) {
  .conversation-chat .card {
    height: 50vh !important;
  }
}
</style>

Step 4: Create Videos.vue file and add the following in it:

<template>
  <div class="container-fluid">
    <button class="btn btn-danger" v-if="roomConnected" @click="leaveRoom">
      Leave Meeting
    </button>
    <span class="icon" v-if="roomConnected">
      <ph-microphone :size="30" v-if="isAudioMuted" style="margin-right: 5px" />
      <ph-microphone-slash
        :size="30"
        color="red"
        v-else
        style="margin-right: 5px" />
      <toggle-button
        id="audioToggle"
        @change="onMuteUnmuteClick(TrackType.Audio)"
        v-model="isAudioMuted"
        :labels="true"
    /></span>
    <span class="icon" v-if="roomConnected && !isAudioOnly">
      <ph-video-camera
        :size="30"
        v-if="isVideoMuted"
        style="margin-right: 5px" />
      <ph-video-camera-slash
        :size="30"
        color="red"
        v-else
        style="margin-right: 5px" />
      <toggle-button
        id="videoToggle"
        @change="onMuteUnmuteClick(TrackType.Video)"
        v-model="isVideoMuted"
        :labels="true"
    /></span>
    <span class="icon" v-if="roomConnected">
      <b-dropdown id="dropdown-1" text="Options" class="m-md-2">
        <b-dropdown-item @click="openSettingModal"
          ><ph-gear :size="25" /> Settings</b-dropdown-item
        >
        <b-dropdown-item
          @click="screeenSharing"
          :class="{ 'd-none': !isHost && !isModerator }"
          ><ph-export :size="25" /> Present
        </b-dropdown-item>
        <b-dropdown-item
          v-if="!stopScreenRecording"
          @click="StartRecording"
          :class="{ 'd-none': !isHost && !isModerator }"
          ><ph-record :size="25" color="red" />
          {{ screenRecordingText }}</b-dropdown-item
        >
        <b-dropdown-item v-else @click="stopRecording"
          ><ph-stop-circle :size="25" />
          {{ screenRecordingText }}</b-dropdown-item
        >
        <b-dropdown-item>
          <b-button
            v-if="!streaming"
            variant="primary"
            @click="startStream"
            :class="{ 'd-none': !isHost && !isModerator }"
            >Start LiveStream</b-button
          >
          <b-button v-else variant="danger" @click="endStream"
            >Leave LiveStream</b-button
          >
        </b-dropdown-item>
      </b-dropdown>
    </span>
    <b-row id="room">
      <b-col
        cols="12"
        sm="12"
        :md="isGuest || isChatDisabled ? 12 : 8"
        :lg="isGuest || isChatDisabled ? 12 : 8"
        :xl="isGuest || isChatDisabled ? 12 : 8"
      >
        <div
          id="active-participant"
          class="col-xs-12 col-sm-12 col-md-12"
          style="text-align: center"
          :class="{
            'd-none': streaming && false,
          }"
        >
          <SkeletonBox
            v-if="!roomConnected && startJoin && true"
            class="Skeleton-box mt-2 Skeloton-container participant-icon"
            :height="'720px'"
          />
          <div
            class="participant main"
            :class="{
              'd-none':!roomConnected && startJoin && true}"
          >
            <video autoplay playsinline muted></video>
          </div>
        </div>
        <div
          id="participants"
          class="col-xs-12 col-sm-12 col-md-12 w-100"
          style="text-align: center"
        ></div>
      </b-col>
      <b-col
        v-if="!isChatDisabled"
        cols="12"
        sm="12"
        md="4"
        lg="4"
        xl="4"
        :class="{ 'd-none': isGuest }"
      >
        <Conversation ref="chat" v-if="roomConnected" />
      </b-col>
    </b-row>
    <b-modal
      ref="username-modal"
      v-model="modalShow"
      title="Enter your information"
      hide-footer
      cancel-disabled
    >
      <div class="d-block text-center">
        <b-form>
          <b-row>
            <b-col cols="12">
              <b-form-input
                type="text"
                v-model="userName"
                placeholder="Enter your name"
                v-if="!roomConnected && disable"
                disabled
              />
              <b-form-input
                type="text"
                v-model="userName"
                :state="validation"
                placeholder="Enter your name"
                v-if="!roomConnected && !disable"
              />
              <b-form-invalid-feedback :state="validation">
                Please enter your name.
              </b-form-invalid-feedback>
            </b-col>
          </b-row>
          <b-row>
            <b-col cols="12" sm="12" md="4" lg="4" xl="4">
              <b-button
                class="mt-3 mr-3"
                variant="outline-success"
                @click="audioVideoSelection"
                >Join Meeting</b-button
              >
            </b-col>
            <b-col cols="12" sm="12" md="8" lg="8" xl="8">
              <b-button
                class="mt-3 mr-3"
                variant="outline-primary"
                @click="testAudioVideoModal"
                >Check your Audio and Video</b-button
              >
            </b-col>
          </b-row>
        </b-form>
      </div>
    </b-modal>
    <b-modal
      ref="testAudioVideo-modal"
      v-model="testModalShow"
      title="Get Ready"
      id="modal-xl"
      size="xl"
      no-close-on-backdrop
    >
      <div class="d-block text-center">
        <b-row>
          <b-col cols="12" sm="12" md="8" lg="8" xl="8" v-if="!isAudioOnly">
            <div class="video-div" v-if="!isCamera"></div>
            <video id="localVideo" muted autoplay playsinline />
          </b-col>
          <b-col cols="12" sm="12" md="4" lg="4" xl="4">
            <b-button
              variant="outline-primary"
              v-if="!recordAudio"
              @click="captureAudio"
              >Check Audio</b-button
            >
            <b-button
              variant="outline-primary"
              v-if="recordAudio"
              @click="releaseAudio"
              >Play Audio</b-button
            >
          </b-col>
        </b-row>
      </div>
    </b-modal>
    <b-modal
      ref="AudioVideoSelection-modal"
      v-model="audioVideoSelectionModal"
      title="Media Device Selection"
      id="modal-xl"
      size="xl"
      no-close-on-backdrop
    >
      <b-row>
        <b-col cols="12" xl="12" sm="12" md="12" lg="12" v-if="!isAudioOnly">
          <div class="card">
            <div class="card-block text-center">
              <h4 class="card-title">Preview Media</h4>
              <div id="audioinputwaveform"></div>
              <audio id="audioinputpreview" autoplay></audio>
              <video id="videoinputpreview" autoplay></video>
            </div>
          </div>
        </b-col>
        <b-col cols="12" xl="12" sm="12" md="12" lg="12">
          <div class="card">
            <div class="card-block">
              <h4 class="card-title text-center">Select Media Devices</h4>
              <b-form inline>
                <label class="mr-sm-2" for="inline-form-custom-select-pref"
                  >Audio Input</label
                >
                <b-form-select
                  id="audioinput"
                  name="audioinput"
                  class="mb-2 mr-sm-2 mb-sm-0 form-control"
                  @change="applyAudioInputDeviceChange"
                ></b-form-select>
              </b-form>
              <b-form v-if="!isAudioOnly" inline>
                <label class="mr-sm-2" for="inline-form-custom-select-pref"
                  >Video Input</label
                >
                <b-form-select
                  id="videoinput"
                  name="videoinput"
                  class="mb-2 mr-sm-2 mb-sm-0 form-control"
                  @change="applyVideoInputDeviceChange"
                >
                </b-form-select>
              </b-form>
              <b-form inline>
                <label class="mr-sm-2" for="inline-form-custom-select-pref"
                  >Audio Output</label
                >
                <b-form-select
                  id="audiooutput"
                  name="audiooutput"
                  class="mb-2 mr-sm-2 mb-sm-0 form-control"
                  @change="applyAudioOutputDeviceChange"
                ></b-form-select>
              </b-form>
            </div>
          </div>
        </b-col>
      </b-row>
      <template #modal-footer>
        <div class="w-100 text-center">
          <b-button
            variant="outline-success"
            size="md"
            class="float-left mx-2"
            @click="hideModal"
          >
            {{ buttonText }}
          </b-button>
          <b-button
            variant="outline-danger"
            v-if="showCancel"
            class="mr-2"
            size="md"
            @click="cancelModal"
          >
            Cancel
          </b-button>
        </div>
      </template>
    </b-modal>
  </div>
</template>
<script>
/* eslint-disable no-debugger */
import {
  connect,
  createLocalVideoTrack,
  createLocalAudioTrack,
  LocalDataTrack,
  LocalVideoTrack,
} from "twilio-video";
import axios from "axios";
import SkeletonBox from "./SkeletonBox.vue";
import Conversation from "./Conversation.vue";
import EventBus from "./../event";
import { ToggleButton } from "vue-js-toggle-button";
import {
  BModal,
  BButton,
  BRow,
  BCol,
  BFormInput,
  BFormInvalidFeedback,
  BForm,
  BFormSelect,
  BDropdown,
  BDropdownItem,
} from "bootstrap-vue";
import config from "../config.js";
import {
  PhMicrophone,
  PhMicrophoneSlash,
  PhVideoCamera,
  PhVideoCameraSlash,
  PhGear,
  PhExport,
  PhRecord,
  PhStopCircle,
} from "phosphor-vue";
export default {
  components: {
    SkeletonBox,
    ToggleButton,
    // Messages,
    BModal,
    BButton,
    BRow,
    BCol,
    PhMicrophone,
    PhMicrophoneSlash,
    PhVideoCamera,
    PhVideoCameraSlash,
    BFormInput,
    BFormInvalidFeedback,
    BForm,
    BFormSelect,
    BDropdown,
    BDropdownItem,
    PhGear,
    PhExport,
    PhRecord,
    PhStopCircle,
    Conversation,
  },
  data() {
    return {
      streaming: false,
      streamDetails: null,
      room: null,
      isCamera: false,
      loading: false,
      recordAudio: false,
      data: {},
      localTrack: false,
      startJoin: false,
      roomConnected: false,
      remoteTrack: "",
      activeRoom: "",
      previewTracks: "",
      identity: "",
      roomName: null,
      buttonShow: true,
      leaveButtonShow: false,
      isAudioMuted: true,
      isVideoMuted: true,
      userName: "",
      userData: "",
      screenRecordingText: "Screen Recording",
      stopScreenRecording: false,
      mediaRecorder: null,
      TrackType: {
        Audio: "Audio",
        Video: "Video",
      },
      screenTrack: null,
      userRole: [],
      selectedAudioDevice: "",
      selectedVideoDevice: "",
      modalShow: false,
      testModalShow: false,
      disable: false,
      audioVideoSelectionModal: false,
      localAudioTrack: null,
      localVideoTrack: null,
      deviceSelections: null,
      track: null,
      buttonText: "Join Meeting",
      chatDisable: false,
      showCancel: false,
      isPinned: false,
      audioOnly: false,
      selectedDevice: [],
      conversationSid: "",
      actionSVG: {
        unmute:
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="20" height="20" fill="currentColor"> <g><path d="M127.99991,24H128a40,40,0,0,1,40,40v64a40,40,0,0,1-40,40h-.00008A39.99991,39.99991,0,0,1,88,128.00009V63.99991A39.99991,39.99991,0,0,1,127.99991,24Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path> <line x1="128" y1="200" x2="128" y2="232" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <path d="M199.56055,136.00017a72.00715,72.00715,0,0,1-143.12111-.00011" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path></g></svg>',
        mute: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="20" height="20" fill="white"> <g><path d="M176.42693,181.28138A72.00558,72.00558,0,0,1,56.43944,136.00006" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path> <path d="M154.90249,157.60205A39.85421,39.85421,0,0,1,128.00009,168H128a40,40,0,0,1-40-40V84.00214" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path> <line x1="128" y1="200" x2="128" y2="232" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <line x1="48" y1="40" x2="208" y2="216" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <path d="M93.95906,42.98434A39.97457,39.97457,0,0,1,128,24h.00008A39.99991,39.99991,0,0,1,168,63.99991V124.4291" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path> <path d="M199.56055,136.00017a71.54827,71.54827,0,0,1-4.48193,18.2151" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path></g></svg>',
        remove:
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="20" height="20" fill="currentColor" style="color: white;"> <g><line x1="215.99609" y1="56" x2="39.99609" y2="56.00005" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <line x1="104" y1="104" x2="104" y2="168" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <line x1="152" y1="104" x2="152" y2="168" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <path d="M200,56V208a8,8,0,0,1-8,8H64a8,8,0,0,1-8-8V56" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path> <path d="M168,56V40a16,16,0,0,0-16-16H104A16,16,0,0,0,88,40V56" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path></g></svg>',
        chat: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="20" height="20" fill="#f7f7f7"> <g><path d="M34.0714,140.74858a71.96979,71.96979,0,1,1,25.18031,25.1802l.00017-.00075-24.86742,7.105a6,6,0,0,1-7.41747-7.41747l7.105-24.86742Z" fill="none" stroke="#f7f7f7" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path> <path d="M92.059,175.89247a72.04349,72.04349,0,0,0,104.68926,38.03631l-.00017-.00075,24.86742,7.105a6,6,0,0,0,7.41747-7.41747l-7.105-24.86742.00057.00046A72.01951,72.01951,0,0,0,163.93781,80.10585" fill="none" stroke="#f7f7f7" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path></g></svg>',
        unmuteVideo:
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="20" height="20" fill="currentColor" style="margin-right: 5px;"> <g><path d="M24,60H152a32,32,0,0,1,32,32v96a8,8,0,0,1-8,8H48a32,32,0,0,1-32-32V68A8,8,0,0,1,24,60Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path> <polyline points="184 112 240 80 240 176 184 144" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></polyline></g></svg>',
        muteVideo:
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="20" height="20" fill="white" style="margin-right: 5px;"> <g><polygon points="240 176 184 144 184 112 240 80 240 176" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></polygon> <line x1="34.9091" y1="24" x2="224" y2="232" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <path d="M110.8827,60H152a32,32,0,0,1,32,32v52" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path> <path d="M184,188a8,8,0,0,1-8,8H48a32,32,0,0,1-32-32V68a8,8,0,0,1,8-8H67.63637" fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path></g></svg>',
        pin: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="20" height="20" fill="currentColor"> <g><line x1="88" y1="168" x2="48" y2="208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <path d="M154.34315,29.65685,92,92S64.256,78.128,34.60125,102.05044a8.01406,8.01406,0,0,0-.64838,11.90243L141.84933,221.84933a7.99324,7.99324,0,0,0,12.04036-.83167C162.28278,209.86184,175.46546,186.93092,164,164l62.34315-62.34315a8,8,0,0,0,0-11.3137l-60.6863-60.6863A8,8,0,0,0,154.34315,29.65685Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path></g></svg>',
        unPin:
          '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="20" height="20" fill="#currentColor"> <g><line x1="88" y1="168" x2="48" y2="208" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <line x1="48" y1="40" x2="208" y2="216" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></line> <path d="M93.71428,90.28572,92,92S64.256,78.128,34.60125,102.05044a8.01406,8.01406,0,0,0-.64838,11.90243L141.84933,221.84933a7.99324,7.99324,0,0,0,12.04036-.83167c7.60268-10.10523,19.13532-29.87219,12.71834-50.549" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path> <path d="M184.9386,143.0614l41.40455-41.40455a8,8,0,0,0,0-11.3137l-60.6863-60.6863a8,8,0,0,0-11.3137,0l-37.976,37.976" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"></path></g></svg>',
      },
    };
  },
  computed: {
    isDevelopment() {
      return process.env.NODE_ENV === "development";
    },
    validation() {
      return this.userName.length > 0;
    },
    isViewer() {
      return this.userRole.includes("viewer");
    },
    isChatDisabled() {
      return this.chatDisable;
    },
    isGuest() {
      return this.userRole.includes("guest");
    },
    isModerator() {
      return this.userRole.includes("moderator");
    },
    isHost() {
      return this.userRole.includes("host");
    },
    isAudioOnly() {
      return window.$(".room-type").val() === "audio";
    },
  },
  mounted() {
    this.modalShow = true;
    if (window.$(".video-group-username").val()) {
      this.userName = window.$(".video-group-username").val();
      this.disable = true;
    }
    if (
      typeof navigator === "undefined" ||
      typeof navigator.userAgent !== "string"
    ) {
      window.isMobile = false;
    }
    if (window.$(".room-recording").val() === 1) {
      this.$toastr.success("Meeting is recorded");
    }
    if (!window.isMobile) {
      window.isMobile = /Mobile/.test(navigator.userAgent);
    }
    window.isMobile = window.addEventListener(
      "beforeunload",
      this.leaveRoomIfJoined
    );
    window.videoThat = this;
    this.$nextTick(() => {
      window.$room = window.$("#room");
      window.$activeParticipant = window.$(
        "div#active-participant > div.participant.main",
        window.$room
      );
      window.$activeVideo = window.$("video", window.$activeParticipant);
      window.$participants = window.$("div#participants", window.$room);
      window.activeParticipant = null;
      window.isActiveParticipantPinned = false;
      window.room = null;
      window.muteRemoteUser = (sid) => {
        if (this.$refs.chat) {
          //|| this.$refs.conversation
          var message = JSON.stringify({
            message: "MUTE USER",
            command: { name: "#MUTE USER#", user: sid },
          });
          this.$refs.chat.addMessage(message);
          // this.$refs.conversation.addMessage(message);
        }
        window.$(`span#userActionSVG-${sid} svg`).remove();
        window.$(`span#userActionSVG-${sid}`).append(this.actionSVG.mute);
      };
      window.muteRemoteUserVideo = (sid) => {
        if (this.$refs.chat) {
          //|| this.$refs.conversation
          var message = JSON.stringify({
            message: "MUTE USER VIDEO",
            command: { name: "#MUTE USER VIDEO#", user: sid },
          });
          this.$refs.chat.addMessage(message);
        }
        window.$(`span#videoUserActionSVG-${sid} svg`).remove();
        window
          .$(`span#videoUserActionSVG-${sid}`)
          .append(this.actionSVG.muteVideo);
      };
      window.kickUser = (sid) => {
        if (this.$refs.chat) {
          //|| this.$refs.conversation
          var message = JSON.stringify({
            message: "KICK USER",
            command: { name: "#KICK USER#", user: sid },
          });
          this.$refs.chat.addMessage(message);
          // this.$refs.conversation.addMessage(message);
          window.$(`#remove-${sid}`).remove();
        }
      };
      window.disableChat = (sid) => {
        if (this.$refs.chat) {
          //|| this.$refs.conversation
          var message = JSON.stringify({
            message: "DISABLE CHAT",
            command: { name: "#DISABLE CHAT#", user: sid },
          });
          this.$refs.chat.addMessage(message);
          // this.$refs.conversation.addMessage(message);
        }
      };
      window.pinUser = (sid) => {
        if (this.$refs.chat) {
          //|| this.$refs.conversation
          var message = JSON.stringify({
            message: "PIN USER",
            command: { name: "#PIN USER#", user: sid },
          });
          this.$refs.chat.addMessage(message);
          // this.$refs.conversation.addMessage(message);
        }
        if (this.streaming) {
          var streamMessage = JSON.stringify({
            message: "ADD LIVE STREAM USER",
            command: { name: "#ADD LIVE STREAM USER#", user: sid },
          });
          this.$refs.chat.addMessage(streamMessage);
        }
        window.$(`span#pinuserActionSVG-${sid} svg`).remove();
        window.$(`span#pinuserActionSVG-${sid}`).append(this.actionSVG.unPin);
      };
    });
    EventBus.$on("control-panel-action", this.externalActionForUsers);
  },
  destroyed() {
    EventBus.$off("control-panel-action", this.externalActionForUsers);
  },
  methods: {
    externalActionForUsers(dataObj) {
      try {
        const payload = dataObj.payload;
        const message = dataObj.message;
        if (
          window.room &&
          window.room.localParticipant &&
          window.room.localParticipant.sid
        ) {
          if (payload.includes("#MUTE USER#")) {
            const data = JSON.parse(payload);
            const userSid = data.command.user;
            if (userSid && userSid === window.room.localParticipant.sid) {
              const bubbleTag = window.$("#audioToggle");
              if (
                bubbleTag &&
                window.$(bubbleTag).find("input").prop("checked")
              ) {
                window.$(bubbleTag).trigger("click");
              }
              this.isAudioMuted = false;
              this.onMuteUnmuteClick(this.TrackType.Audio);
              if (message) {
                this.$refs.chat.removeMessage(message);
              }
            }
          } else if (payload.includes("#KICK USER#")) {
            const data = JSON.parse(payload);
            const userSid = data.command.user;
            if (userSid && userSid === window.room.localParticipant.sid) {
              // TODO: uncomment the api code when api
              const parameter = {
                action: "kick_user",
                identity: window.room.localParticipant.identity,
                room_sid: window.room.sid,
              };
              const formData = new FormData();
              Object.keys(parameter).forEach((key) => {
                formData.append(key, parameter[key]);
              });
              axios
                .post(`${config.url}/wp-admin/admin-ajax.php`, formData)
                .then((res) => {
                  if (res.data.stats === "success") {
                    this.leaveRoom();
                    window.$(`#remove-${userSid}`).remove();
                  }
                })
                .catch((error) => {
                  console.log(error);
                });
              if (message) {
                this.$refs.chat.removeMessage(message);
              }
            }
          } else if (payload.includes("#DISABLE CHAT#")) {
            const data = JSON.parse(payload);
            const userSid = data.command.user;
            if (userSid && userSid === window.room.localParticipant.sid) {
              this.chatDisable = true;
              if (message) {
                this.$refs.chat.removeMessage(message);
              }
            }
          } else if (payload.includes("#MUTE USER VIDEO#")) {
            const data = JSON.parse(payload);
            const userSid = data.command.user;
            if (userSid && userSid === window.room.localParticipant.sid) {
              const bubbleTag = window.$("#videoToggle");
              if (
                bubbleTag &&
                window.$(bubbleTag).find("input").prop("checked")
              ) {
                window.$(bubbleTag).trigger("click");
              }
              this.isVideoMuted = false;
              this.onMuteUnmuteClick(this.TrackType.Video);
              if (message) {
                this.$refs.chat.removeMessage(message);
              }
            }
          } else if (payload.includes("#PIN USER#")) {
            const data = JSON.parse(payload);
            const userSid = data.command.user;
            if (userSid && userSid === window.room.localParticipant.sid) {
              // Pin the RemoteParticipant as the active Participant.
              if (window.activeParticipant.sid === userSid) {
                this.isPinned = false;
                window.$(`span#pinuserActionSVG-${userSid} svg`).remove();
                window
                  .$(`span#pinuserActionSVG-${userSid}`)
                  .append(this.actionSVG.pin);
              } else {
                this.isPinned = true;
                this.setVideoPriority(window.room.localParticipant, "high");
                window.isActiveParticipantPinned = true;
                this.setActiveParticipant(window.room.localParticipant);
              }
              if (message) {
                this.$refs.chat.removeMessage(message);
              }
            } else {
              const participants = Array.from(window.room.participants);
              const ele = participants.find((x) => x[0] == userSid)[1];
              if (ele.sid === userSid && this.isPinned) {
                this.isPinned = false;
                window.$(`span#pinuserActionSVG-${userSid} svg`).remove();
                window
                  .$(`span#pinuserActionSVG-${userSid}`)
                  .append(this.actionSVG.pin);
              } else {
                this.isPinned = true;
                this.setVideoPriority(ele, "high");
                window.isActiveParticipantPinned = true;
                this.setActiveParticipant(ele);
              }
              if (message) {
                this.$refs.chat.removeMessage(message);
              }
            }
          } 
        }
      } catch (error) {
        console.log(error);
      }
    },
    setupLocalDataTrack(sid) {
      if (!window.dataTrack) {
        window.dataTrack = new LocalDataTrack();
      }
      window.dataTrack.send(
        JSON.stringify({
          type: "audio",
          sid: sid || "",
        })
      );

      return window.dataTrack;
    },
    async releaseAudio() {
      this.recordAudio = false;
      const audio = await window.recorder.stop();
      audio.play();
    },
    async captureAudio() {
      this.recordAudio = true;
      window.recorder = await this.recordLiveAudio();
      window.recorder.start();
    },
    recordLiveAudio() {
      // eslint-disable-next-line no-async-promise-executor
      return new Promise(async (resolve) => {
        const stream = await navigator.mediaDevices.getUserMedia({
          audio: true,
        });
        const mediaRecorder = new MediaRecorder(stream);
        const audioChunks = [];

        mediaRecorder.addEventListener("dataavailable", (event) => {
          audioChunks.push(event.data);
        });

        const start = () => mediaRecorder.start();

        const stop = () =>
          new Promise((resolve) => {
            mediaRecorder.addEventListener("stop", () => {
              const audioBlob = new Blob(audioChunks, { type: "audio/mpeg" });
              const audioUrl = URL.createObjectURL(audioBlob);
              const audio = new Audio(audioUrl);
              const play = () => audio.play();
              resolve({ audioBlob, audioUrl, play });
            });

            mediaRecorder.stop();
          });

        resolve({ start, stop });
      });
    },
    openSettingModal() {
      this.buttonText = "Ok";
      this.showCancel = true;
      this.audioVideoSelectionModal = true;
      this.updateDeviceSelectionOptions();
    },
    hideModal() {
      this.audioVideoSelectionModal = false;
      if (!window.room || !window.room.localParticipant) {
        this.meetInRoom();
      }
    },
    cancelModal() {
      this.audioVideoSelectionModal = false;
      this.showCancel = false;
    },
    audioVideoSelection() {
      this.modalShow = false;
      this.audioVideoSelectionModal = true;
      this.buttonText = "Join Meeting";
      this.updateDeviceSelectionOptions();
    },
    testAudioVideoModal() {
      this.testModalShow = true;
      this.playVideoFromCamera();
    },
    async playVideoFromCamera() {
      try {
        const constraints = { video: true, audio: true };
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        if (!this.isAudioOnly) {
          this.isCamera = true;
          const videoElement = window.$("video#localVideo");
          videoElement.get(0).srcObject = stream;
        }
        this.mediaRecorder = new MediaRecorder(stream);
      } catch (error) {
        this.isCamera = false;
        console.error("Error opening video camera.", error);
      }
    },
    reset() {
      this.userRole = [];
      this.loading = false;
      this.data = {};
      this.localTrack = false;
      this.startJoin = false;
      this.roomConnected = false;
      this.remoteTrack = "";
      this.activeRoom = "";
      this.previewTracks = "";
      this.identity = "";
      this.roomName = null;
      this.buttonShow = true;
      this.leaveButtonShow = false;
      this.userName = "";
      this.modalShow = true;
      this.userData = "";
      this.track = null;
      this.screenTrack = null;
      this.testModalShow = false;
      this.localAudioTrack = null;
      this.localVideoTrack = null;
      this.mediaRecorder = null;
      this.isPinned = false;
      this.watchStreamUrl = null;
      videoTrack = null;
      this.selectedDevice = [];
      if (this.streaming) {
        this.endStream();
      }
    },
    /**
     * Set the active Participant's video.
     * @param participant - the active Participant
     */
    setActiveParticipant(participant) {
      if (window.activeParticipant) {
        const $activeParticipant = window.$(
          `div#${window.activeParticipant.sid}`,
          window.$participants
        );
        $activeParticipant.removeClass("active");
        $activeParticipant.removeClass("pinned");

        // Detach any existing VideoTrack of the active Participant.
        const { track: activeTrack } =
          Array.from(window.activeParticipant.videoTracks.values())[0] || {};
        if (activeTrack) {
          activeTrack.detach(window.$activeVideo.get(0));
          window.$activeVideo.css("opacity", "0");
        }
      }

      // Set the new active Participant.
      window.activeParticipant = participant;
      const { identity, sid } = participant;
      const userData = JSON.parse(identity);
      const userIdentity = userData.u;
      const $participant = window.$(`div#${sid}`, window.$participants);

      $participant.addClass("active");
      if (window.isActiveParticipantPinned) {
        $participant.addClass("pinned");
      }

      // Attach the new active Participant's video.
      const { track } = Array.from(participant.videoTracks.values())[0] || {};
      if (track) {
        track.attach(window.$activeVideo.get(0));
        window.$activeVideo.css("opacity", "");
      }

      // Set the new active Participant's identity
      window.$activeParticipant.attr("data-identity", userIdentity);
      window.$activeParticipant.attr("id", `active-${participant.sid}`);
      if (
        !this.isVideoMuted &&
        participant.sid === window.room.localParticipant.sid
      ) {
        const participantVideo = window.$(
          `div#active-participant >div.participant.main#active-${participant.sid} > video`
        );
        participantVideo.addClass("opacity-0");
        const participantThumbnailVideo = window.$(
          `div.participant#${participant.sid}>video`
        );
        participantThumbnailVideo.addClass("opacity-0");
      } else {
        const participantVideo = window.$(
          `div#active-participant >div.participant.main#active-${participant.sid} > video`
        );
        participantVideo.removeClass("opacity-0");
        const participantThumbnailVideo = window.$(
          `div.participant#${participant.sid}>video`
        );
        participantThumbnailVideo.removeClass("opacity-0");
      }
    },

    /**
     * Set the current active Participant in the Room.
     * @param room - the Room which contains the current active Participant
     */
    setCurrentActiveParticipant(room, participant) {
      const { dominantSpeaker, localParticipant } = room;
      this.setActiveParticipant(
        dominantSpeaker || participant || localParticipant
      );
    },

    /**
     * Set up the Participant's media container.
     * @param participant - the Participant whose media container is to be set up
     * @param room - the Room that the Participant joined
     */
    setupParticipantContainer(participant, room) {
      const { identity, sid } = participant;
      if (!identity || identity === "video-composer-v1") {
        return;
      }
      const userData = JSON.parse(identity);
      const userIdentity = userData.u;

      // Add a container for the Participant's media.
      const $container = window.$(`<div style="display: inline-grid;
    min-width:180px;max-width: 200px;" id="main-${sid}">
    <h6 class="participant-network-level" id="level${sid}"></h6>
    <div class="participant" data-identity="${userIdentity}" id="${sid}">
    <audio autoplay ${
      participant === room.localParticipant ? "muted" : ""
    } style="opacity: 0"></audio>
    <video autoplay muted playsinline style="opacity: 0"></video>
  </div>${
    participant !== room.localParticipant && (this.isModerator || this.isHost)
      ? `<div class="d-flex flex-wrap participant-option justify-content-between"><button class="btn btn-danger" id="${sid}" style="margin-right:2px;" onclick="muteRemoteUser('${sid}')"> <span id="userActionSVG-${sid}"> ${
          this.actionSVG.unmute
        } </span></button><button class="btn btn-danger" style="margin-left:2px;" id="remove-${sid}" onclick="kickUser('${sid}')"> ${
          this.actionSVG.remove
        }</button><button class="btn btn-danger" style="margin-left:2px;" id="disable-${sid}" onclick="disableChat('${sid}')"> ${
          this.actionSVG.chat
        }</button>${
          this.isAudioOnly
            ? ""
            : `<button class="btn btn-danger" style="margin-left:2px;" id="muteVideo-${sid}" onclick="muteRemoteUserVideo('${sid}')"> <span id="videoUserActionSVG-${sid}"> ${this.actionSVG.unmuteVideo} </span></button>`
        }<button class="btn btn-danger" id="pinUser-${sid}" onclick="pinUser('${sid}')"><span id="pinuserActionSVG-${sid}">${
          this.actionSVG.pin
        }</span></button></div>`
      : ""
  } </div>`);

      // Toggle the pinning of the active Participant's video.

      $container.on("click", () => {
        if (
          (this.isHost && !this.isPinned) ||
          (!this.isHost && !this.isPinned)
        ) {
          if (
            window.activeParticipant === participant &&
            window.isActiveParticipantPinned
          ) {
            // Unpin the RemoteParticipant and update the current active Participant.
            this.setVideoPriority(participant, null);
            window.isActiveParticipantPinned = false;
            this.setCurrentActiveParticipant(room);
          } else {
            // Pin the RemoteParticipant as the active Participant.
            if (window.isActiveParticipantPinned) {
              this.setVideoPriority(window.activeParticipant, null);
            }
            this.setVideoPriority(participant, "high");
            window.isActiveParticipantPinned = true;
            this.setActiveParticipant(participant);
          }
        }
      });
      // Add the Participant's container to the DOM.
      window.$participants.append($container);
    },

    /**
     * Set the VideoTrack priority for the given RemoteParticipant.This has no
     * effect in Peer-to-Peer Rooms.
     * @param participant - the RemoteParticipant whose VideoTrack priority is to be set
     * @param priority - null | 'low' | 'standard' | 'high'
     */
    setVideoPriority(participant, priority) {
      participant.videoTracks.forEach((publication) => {
        const track = publication.track;
        if (track && track.setPriority) {
          track.setPriority(priority);
        }
      });
    },

    /**
     * Attach a Track to the DOM.
     * @param track - the Track to attach
     * @param participant - the Participant which published the Track
     */
    attachTrack(track, participant) {
      // Attach the Participant's Track to the thumbnail.
      const $media = window.$(
        `div#${participant.sid} > ${track.kind}`,
        window.$participants
      );
      $media.css("opacity", "");
      track.attach($media.get(0));

      // If the attached Track is a VideoTrack that is published by the active
      // Participant, then attach it to the main video as well.
      if (track.kind === "video" && participant === window.activeParticipant) {
        track.attach(window.$activeVideo.get(0));
        // window.$activeVideo.css("opacity", "");
        window.$activeVideo.removeClass("opacity-0");
      }
    },

    /**
     * Detach a Track from the DOM.
     * @param track - the Track to be detached
     * @param participant - the Participant that is publishing the Track
     */
    detachTrack(track, participant) {
      // Detach the Participant's Track from the thumbnail.
      const $media = window.$(
        `div#${participant.sid} > ${track.kind}`,
        window.$participants
      );
      const mediaEl = $media.get(0);
      $media.css("opacity", "0");
      track.detach(mediaEl);
      mediaEl.srcObject = null;

      // If the detached Track is a VideoTrack that is published by the active
      // Participant, then detach it from the main video as well.
      if (track.kind === "video" && participant === window.activeParticipant) {
        const activeVideoEl = window.$activeVideo.get(0);
        track.detach(activeVideoEl);
        activeVideoEl.srcObject = null;
        window.$activeVideo.addClass("opacity-0");
        // window.$activeVideo.css("opacity", "0");
      }
    },
    /**
     * Handle the Participant's media.
     * @param participant - the Participant
     * @param room - the Room that the Participant joined
     */
    participantConnected(participant, room) {
      // Set up the Participant's media container.
      this.setupParticipantContainer(participant, room);

      // Handle the TrackPublications already published by the Participant.
      participant.tracks.forEach((publication) => {
        this.trackPublished(publication, participant);
      });

      // Handle theTrackPublications that will be published by the Participant later.
      participant.on("trackPublished", (publication) => {
        this.trackPublished(publication, participant);
      });
    },

    /**
     * Handle a disconnected Participant.
     * @param participant - the disconnected Participant
     * @param room - the Room that the Participant disconnected from
     */
    participantDisconnected(participant, room) {
      // If the disconnected Participant was pinned as the active Participant, then
      // unpin it so that the active Participant can be updated.
      if (!participant) return;
      if (
        window.activeParticipant === participant &&
        window.isActiveParticipantPinned
      ) {
        window.isActiveParticipantPinned = false;
        this.setCurrentActiveParticipant(room);
      }
      const dataIdentity = window
        .$(`div#${participant.sid}`, window.$participants)
        .attr("data-identity");
      window.$(`[data-identity="${dataIdentity}"]`).attr("data-identity", "");
      // Remove the Participant's media container.
      window.$(`div#${participant.sid}`, window.$participants).remove();
      window.$(`div#main-${participant.sid}`).remove();
      window.$(`button#${participant.sid}`).remove();
      window.$(`button#remove-${participant.sid}`).remove();
      window.$(`h6#level${participant.sid}`).remove();
      window.$(`button#disable-${participant.sid}`).remove();
      window.$(`button#muteVideo-${participant.sid}`).remove();
      window.$(`button#pinUser-${participant.sid}`).remove();
    },

    /**
     * Handle to the TrackPublication's media.
     * @param publication - the TrackPublication
     * @param participant - the publishing Participant
     */
    trackPublished(publication, participant) {
      // If the TrackPublication is already subscribed to, then attach the Track to the DOM.
      if (publication.track) {
        this.attachTrack(publication.track, participant);
      }

      // Once the TrackPublication is subscribed to, attach the Track to the DOM.
      publication.on("subscribed", (track) => {
        this.attachTrack(track, participant);
      });

      // Once the TrackPublication is unsubscribed from, detach the Track from the DOM.
      publication.on("unsubscribed", (track) => {
        this.detachTrack(track, participant);
      });
    },
    getAccessToken() {
      const data = {
        action: "join_group_video_by_ajax",
        device: "browser",
        user_name:
          window.da_user_name ||
          window.$(".video-group-username").val() ||
          this.userName,
        product_id:
          window.$(".video-group-productid").val() || config.product_id,
        wave_id: window.$(".video-group-productwave").val() || config.wave_id,
        order_id: window.$(".video-group-orderid").val() || config.order_id,
        user_id: window.$(".video-group-userid").val() || config.user_id,
        token: window.$(".video-group-token").val(),
      };
      const formData = new FormData();
      Object.keys(data).forEach((key) => {
        formData.append(key, data[key]);
      });
      let url = null;
      if (this.isDevelopment) {
        url = config.url;
      } else {
        url = window.da_endpoint_url || window.location.origin;
      }

      return axios.post(`${url}/wp-admin/admin-ajax.php`, formData);
    },
    /**
     * Listen to changes in the Network Quality report of a Participant and update
     * your application.
     * @param {Participant} participant - The Participant whose updates you want to listen to
     * @param {function} updateNetworkQualityReport - Updates the app UI with the new
     *   Network Quality report of the Participant.
     * @returns {void}
     */
    setupNetworkQualityUpdatesForParticipant(
      participant,
      updateNetworkQualityReport
    ) {
      updateNetworkQualityReport(participant);
      participant.on("networkQualityLevelChanged", () => {
        updateNetworkQualityReport(participant);
      });
    },
    /**
     * Listen to changes in the Network Quality reports and update your application.
     * @param {Room} room - The Room you just joined
     * @param {function} updateNetworkQualityReport - Updates the app UI with the new
     *   Network Quality report of a Participant.
     * @returns {void}
     */
    setupNetworkQualityUpdates(room, updateNetworkQualityReport) {
      // Listen to changes in Network Quality level of the LocalParticipant.
      this.setupNetworkQualityUpdatesForParticipant(
        room.localParticipant,
        updateNetworkQualityReport
      );
      // Listen to changes in Network Quality levels of RemoteParticipants already
      // in the Room.
      room.participants.forEach((participant) => {
        this.setupNetworkQualityUpdatesForParticipant(
          participant,
          updateNetworkQualityReport
        );
      });
      // Listen to changes in Network Quality levels of RemoteParticipants that will
      // join the Room in the future.
      room.on("participantConnected", (participant) => {
        this.setupNetworkQualityUpdatesForParticipant(
          participant,
          this.updateNetworkQualityReport
        );
      });
    },
    /**
     * Change the local and remote Network Quality verbosity levels after joining the Room.
     * @param {Room} room - The Room you just joined
     * @param {number} localVerbosity - Verbosity level of Network Quality reports
     *   for the LocalParticipant [1 - 3]
     * @param {number} remoteVerbosity - Verbosity level of Network Quality reports
     *   for the RemoteParticipant(s) [0 - 3]
     * @returns {void}
     */
    setNetworkQualityConfiguration(room, localVerbosity, remoteVerbosity) {
      room.localParticipant.setNetworkQualityConfiguration({
        local: localVerbosity,
        remote: remoteVerbosity,
      });
    },
    /**
     * Updates the Network Quality report for a Participant.
     */
    updateNetworkQualityReport(participant) {
      const participantDiv = document.getElementById(participant.sid);
      if (participantDiv && participantDiv.parentElement) {
        const title = participantDiv.parentElement.querySelector("h6");
        if (title) {
          title.innerHTML = `NQ Level: ${
            participant.networkQualityLevel
              ? participant.networkQualityLevel
              : 0
          }`;
        }
        const stats = participantDiv.querySelector("textarea");
        if (stats) {
          stats.value = `NQ Stats:\r\n========\r\n${JSON.stringify(
            participant.networkQualityStats,
            null,
            2
          )}`;
        }
      }
    },
    async meetInRoom() {
      this.audioVideoSelectionModal = false;
      const connectOptions = {
        name:
          window.da_room_name ||
          window.$(".video-group-roomname").val() ||
          config.roomName,
        audio: this.isAudioMuted,
        video: this.isVideoMuted,
        dominantSpeaker: true,
        networkQuality: {
          local: 3,
          remote: 3,
        },
        bandwidthProfile: {
          video: {
            dominantSpeakerPriority: "high",
            mode: "collaboration",
            clientTrackSwitchOffControl: "auto",
            contentPreferencesMode: "auto",
          },
        },
      };
      // For mobile browsers, limit the maximum incoming video bitrate to 2.5 Mbps.
      if (window.isMobile) {
        connectOptions.bandwidthProfile.video.maxSubscriptionBitrate = 2500000;
      }
      const userDevices = await navigator.mediaDevices.enumerateDevices();
      if (!userDevices.find((x) => x.kind === "videoinput")) {
        connectOptions.video = false;
      }
      this.startJoin = true;
      this.getAccessToken()
        .then((data) => {
          if (data.status == 200) {
            if (data.data.success != false) {
              this.joinRoom(data.data.token, connectOptions);
              const userData = JSON.parse(data.data.identity);
              this.userData = userData.u;
              this.userRole = JSON.parse(JSON.stringify(userData.r));
              this.conversationSid = data.data.conversation_sid;
            } else {
              this.$toastr.error(data.data.data.msg);
            }
          } else {
            this.$toastr.error("Something went wrong");
          }
        })
        .catch((err) => {
          this.$toastr.error("Not able to connect to room");
          this.startJoin = false;
          console.log(err);
        });
    },
    leaveRoom() {
      if (window.room && window.room.disconnect) window.room.disconnect();
      this.reset();
    },
    /**
     * @param muteUnmuteOptions - Specifies which kind of tracks to mute.
     */
    mute(muteUnmuteOptions) {
      if (!window.room || !window.room.localParticipant) {
        throw new Error("You must be connected to a room to mute tracks.");
      }
      if (muteUnmuteOptions.audio) {
        window.room.localParticipant.audioTracks.forEach((publication) => {
          publication.track.disable();
          this.trackPublished(publication, window.room.localParticipant);
        });
      }
      if (muteUnmuteOptions.video && !this.isAudioOnly) {
        window.room.localParticipant.videoTracks.forEach((publication) => {
          publication.track.disable();
          this.trackPublished(publication, window.room.localParticipant);
        });
        if (window.activeParticipant.sid === window.room.localParticipant.sid) {
          const participantVideo = window.$(
            `div#active-participant >div.participant.main > video`
          );
          participantVideo.addClass("opacity-0");
        }
        const participantThumbnailVideo = window.$(
          `div.participant#${window.room.localParticipant.sid}>video`
        );
        participantThumbnailVideo.addClass("opacity-0");
      }
    },
    /**
     * @param muteUnmuteOptions - Specifies which kind of tracks to mute.
     */
    unmute(muteUnmuteOptions) {
      if (!window.room || !window.room.localParticipant) {
        throw new Error("You must be connected to a room to unmute tracks.");
      }
      if (muteUnmuteOptions.audio) {
        window.room.localParticipant.audioTracks.forEach((publication) => {
          publication.track.enable();
          this.trackPublished(publication, window.room.localParticipant);
        });
      }
      if (muteUnmuteOptions.video && !this.isAudioOnly) {
        window.room.localParticipant.videoTracks.forEach((publication) => {
          publication.track.enable();
          this.trackPublished(publication, window.room.localParticipant);
        });
        if (window.activeParticipant.sid === window.room.localParticipant.sid) {
          const participantVideo = window.$(
            `div#active-participant >div.participant.main > video`
          );
          participantVideo.removeClass("opacity-0");
        }
        const participantThumbnailVideo = window.$(
          `div.participant#${window.room.localParticipant.sid}>video`
        );
        participantThumbnailVideo.removeClass("opacity-0");
      }
    },
    onMuteUnmuteClick(trackType) {
      if (trackType === this.TrackType.Audio) {
        const options = { audio: true, video: false };
        if (this.isAudioMuted) {
          this.unmute(options);
        } else {
          this.mute(options);
        }
      }
      if (trackType === this.TrackType.Video && !this.isAudioOnly) {
        const options = { audio: false, video: true };
        if (this.isVideoMuted) {
          this.unmute(options);
        } else {
          this.mute(options);
        }
      }
    },

    /**
     * Get the list of available media devices of the given kind.
     * @param {Array<MediaDeviceInfo>} deviceInfos
     * @param {string} kind - One of 'audioinput', 'audiooutput', 'videoinput'
     * @returns {Array<MediaDeviceInfo>} - Only those media devices of the given kind
     */
    getDevicesOfKind(deviceInfos, kind) {
      return deviceInfos.filter((deviceInfo) => {
        return deviceInfo.kind === kind;
      });
    },

    /**
     * Apply the selected audio output device.
     * @param {string} deviceId
     * @param {HTMLAudioElement} audio
     * @returns {Promise<void>}
     */
    applyAudioOutputDeviceSelection(deviceId, audio) {
      return typeof audio.setSinkId === "function"
        ? audio.setSinkId(deviceId)
        : Promise.reject(
            "This browser does not support setting an audio output device"
          );
    },

    /**
     * Apply the selected input device.
     * @param {string} deviceId
     * @param {?LocalTrack} localTrack - LocalAudioTrack or LocalVideoTrack; if null, a new LocalTrack will be created.
     * @param {'audio' | 'video'} kind
     * @returns {Promise<LocalTrack>} - The created or restarted LocalTrack
     */
    applyInputDeviceSelection(deviceId, localTrack, kind) {
      var constraints = { deviceId: { exact: deviceId } };
      if (localTrack) {
        return localTrack.restart(constraints).then(() => {
          return localTrack;
        });
      }
      return kind === "audio"
        ? createLocalAudioTrack(constraints)
        : createLocalVideoTrack(constraints);
    },

    /**
     * Ensure that media permissions are obtained.
     * @returns {Promise<void>}
     */
    ensureMediaPermissions() {
      return navigator.mediaDevices
        .enumerateDevices()
        .then((devices) => {
          return devices.every((device) => {
            return !(device.deviceId && device.label);
          });
        })
        .then((shouldAskForMediaPermissions) => {
          if (shouldAskForMediaPermissions) {
            return navigator.mediaDevices
              .getUserMedia({ audio: true, video: true })
              .then((mediaStream) => {
                mediaStream.getTracks().forEach((track) => {
                  track.stop();
                });
              });
          }
        });
    },

    // reads selected audio output, and updates preview to use the device.
    applyAudioOutputDeviceChange() {
      //(event)
      var audio = document.querySelector("audio#audioinputpreview");
      // Note: not supported on safari
      if (this.selectedDevice) {
        this.selectedDevice = this.selectedDevice.filter(
          (x) => x.kind != "audiooutput"
        );
      }
      if (this.deviceSelections.audiooutput.value) {
        this.selectedDevice.push({
          deviceId: this.deviceSelections.audiooutput.value,
          kind: "audiooutput",
        });
        this.applyAudioOutputDeviceSelection(
          this.deviceSelections.audiooutput.value,
          audio
        );
      }
    },

    /**
     * Get the list of available media devices.
     * @returns {Promise<DeviceSelectionOptions>}
     * @typedef {object} DeviceSelectionOptions
     * @property {Array<MediaDeviceInfo>} audioinput
     * @property {Array<MediaDeviceInfo>} audiooutput
     * @property {Array<MediaDeviceInfo>} videoinput
     */
    getDeviceSelectionOptions() {
      // before calling enumerateDevices, get media permissions (.getUserMedia)
      // w/o media permissions, browsers do not return device Ids and/or labels.
      return this.ensureMediaPermissions().then(() => {
        return navigator.mediaDevices.enumerateDevices().then((deviceInfos) => {
          var kinds = ["audioinput", "audiooutput", "videoinput"];
          return kinds.reduce((deviceSelectionOptions, kind) => {
            deviceSelectionOptions[kind] = this.getDevicesOfKind(
              deviceInfos,
              kind
            );
            return deviceSelectionOptions;
          }, {});
        });
      });
    },

    async applyVideoInputDeviceChange() {
      const video = document.querySelector("video#videoinputpreview");
      if (!this.deviceSelections.videoinput.value && !this.isAudioOnly) {
        if (this.selectedDevice) {
          this.selectedDevice = this.selectedDevice.filter(
            (x) => x.kind != "videoinput"
          );
        }
        this.selectedDevice.push({
          deviceId: "",
          kind: "videoinput",
        });
        if (this.localVideoTrack) {
          this.localVideoTrack.detach(video);
          this.localVideoTrack.stop();
          this.localVideoTrack = null;
          if (document.getElementById("videoinputpreview")) {
            document.getElementById("videoinputpreview").srcObject = null;
          }
          if (window.room && window.room.localParticipant) {
            this.isVideoMuted = false;
            window.room.localParticipant.videoTracks.forEach((publication) => {
              publication.track.disable();
              this.trackPublished(publication, window.activeParticipant);
            });
            const participantThumbnailVideo = window.$(
              `div.participant#${window.room.localParticipant.sid}>video`
            );
            participantThumbnailVideo.addClass("opacity-0");
          }
        }
        return;
      } else {
        this.isVideoMuted = true;
        if (this.selectedDevice) {
          this.selectedDevice = this.selectedDevice.filter(
            (x) => x.kind != "videoinput"
          );
        }
        this.selectedDevice.push({
          deviceId: this.deviceSelections.videoinput.value,
          kind: "videoinput",
        });
        this.localVideoTrack = await this.applyInputDeviceSelection(
          this.deviceSelections.videoinput.value,
          this.localVideoTrack,
          "video"
        );
        this.localVideoTrack.attach(video);
        if (window.room && window.room.localParticipant && !this.isAudioOnly) {
          const bubbleTag = window.$("#videoToggle");
          if (bubbleTag && window.$(bubbleTag).find("input").prop("checked")) {
            bubbleTag.trigger("click");
          }
          await window.room.localParticipant.publishTrack(this.localVideoTrack);
          window.room.localParticipant.videoTracks.forEach((publication) => {
            publication.track.enable();
            this.trackPublished(publication, window.activeParticipant);
          });
          if (
            window.activeParticipant.sid === window.room.localParticipant.sid
          ) {
            const participantVideo = window.$(
              `div#active-participant >div.participant.main > video`
            );
            participantVideo.removeClass("opacity-0");
          }
          const participantThumbnailVideo = window.$(
            `div.participant#${window.room.localParticipant.sid}>video`
          );
          participantThumbnailVideo.removeClass("opacity-0");
          return this.localVideoTrack;
        } else {
          return this.localVideoTrack;
        }
      }
    },

    // reads selected audio input, and updates preview and room to use the device.
    async applyAudioInputDeviceChange() {
      if (this.selectedDevice) {
        this.selectedDevice = this.selectedDevice.filter(
          (x) => x.kind != "audioinput"
        );
      }
      this.selectedDevice.push({
        deviceId: this.deviceSelections.audioinput.value,
        kind: "audioinput",
      });
      this.localAudioTrack = await this.applyInputDeviceSelection(
        this.deviceSelections.audioinput.value,
        this.localAudioTrack,
        "audio"
      );
      if (window.room && window.room.localParticipant) {
        await window.room.localParticipant.publishTrack(this.localAudioTrack);
      }
      return this.localAudioTrack;
    },

    /**
     * Build the list of available media devices.
     */
    updateDeviceSelectionOptions() {
      this.getDeviceSelectionOptions().then((deviceSelectionOptions) => {
        ["audioinput", "audiooutput", "videoinput"].forEach((kind) => {
          var kindDeviceInfos = deviceSelectionOptions[kind];
          var deviceSelections = {
            audioinput: document.querySelector("select#audioinput"),
            audiooutput: document.querySelector("select#audiooutput"),
            videoinput: document.querySelector("select#videoinput"),
          };
          this.deviceSelections = deviceSelections;
          var select = deviceSelections[kind];

          [].slice.call(select.children).forEach((option) => {
            option.remove();
          });

          kindDeviceInfos.forEach((kindDeviceInfo, index) => {
            var deviceId = kindDeviceInfo.deviceId;
            var label =
              kindDeviceInfo.label ||
              "Device [ id: " + deviceId.substr(0, 5) + "... ]";
            if (index === 0 && kind === "videoinput") {
              var emptyOption = document.createElement("option");
              emptyOption.value = "";
              emptyOption.appendChild(
                document.createTextNode("Join without video")
              );
              select.appendChild(emptyOption);
            }
            var option = document.createElement("option");
            option.value = deviceId;
            if (index === 0 && kind === "videoinput") {
              option.selected = true;
            }
            option.appendChild(document.createTextNode(label));
            select.appendChild(option);
          });
        });
        if (this.selectedDevice) {
          this.selectedDevice.forEach((element) => {
            if (element.kind === "videoinput") {
              var options = document.querySelector("select#videoinput");
              options.forEach((ele) => {
                if (element.deviceId === ele.value) {
                  ele.selected = true;
                }
              });
            } else if (element.kind === "audioinput") {
              var audioinputOptions =
                document.querySelector("select#audioinput");
              audioinputOptions.forEach((ele) => {
                if (element.deviceId === ele.value) {
                  ele.selected = true;
                }
              });
            } else if (element.kind === "audiooutput") {
              var audiooutptOptions =
                document.querySelector("select#audiooutput");
              audiooutptOptions.forEach((ele) => {
                if (element.deviceId === ele.value) {
                  ele.selected = true;
                }
              });
            }
          });
        }
        const videoInput = document.getElementById("videoinput");
        if (videoInput && videoInput.children && videoInput.children.length) {
          this.applyVideoInputDeviceChange();
        }
        const audioInput = document.querySelector("select#audioinput");
        if (audioInput && audioInput.children && audioInput.children.length) {
          this.applyAudioInputDeviceChange();
        }
        const audioOutput = document.querySelector("select#audiooutput");
        if (
          audioOutput &&
          audioOutput.children &&
          audioOutput.children.length
        ) {
          this.applyAudioOutputDeviceChange();
        }
      });
    },

    /**
     * update the UI to indicate room state.
     */
    onRoomStateChange(newState) {
      const oldStateBtn = document.querySelector("div.current");
      if (oldStateBtn) {
        oldStateBtn.classList.remove("current");
      }

      const newStateBtn = document.querySelector("div." + newState);
      if (newStateBtn) {
        newStateBtn.classList.add("current");
      }

      if (newState === "disconnected") {
        // once disconnected room needs to be recreated.
        this.$toastr.error("Disconnected due to network issue");
        this.leaveRoom();
      }
    },

    /**
     * Join a Room.
     * @param token - the AccessToken used to join a Room
     * @param connectOptions - the ConnectOptions used to join a Room
     */
    async joinRoom(token, connectOptions) {
      // Join to the Room with the given AccessToken and ConnectOptions.
      const room = await connect(token, {
        ...connectOptions,
      });
      this.startJoin = false;
      this.roomConnected = true;
      this.$nextTick(() => {
        if (this.$refs.chat) {
          this.$refs.chat.initializeChat(
            token,
            this.userData,
            connectOptions.name,
            this.userRole,
            this.conversationSid
          );
        }
      });
      this.track = room.localParticipant;
      // Save the LocalVideoTrack.
      let localVideoTrack = null;
      if (this.localVideoTrack) {
        localVideoTrack = this.localVideoTrack;
        await room.localParticipant.publishTrack(localVideoTrack);
      } else {
        this.isVideoMuted = false;
        room.localParticipant.videoTracks.forEach((publication) => {
          publication.track.disable();
        });
        const participantThumbnailVideo = window.$(
          `div.participant#${room.localParticipant.sid}>video`
        );
        participantThumbnailVideo.addClass("opacity-0");
      }

      // Make the Room available in the JavaScript console for debugging.
      window.room = room;
      // Handle the LocalParticipant's media.
      this.participantConnected(room.localParticipant, room);

      // Subscribe to the media published by RemoteParticipants already in the Room.
      room.participants.forEach((participant) => {
        this.participantConnected(participant, room);
      });

      room.on("trackSubscribed", (track) => {
        track.on("message", (message) => {
          if (message) {
            console.log(JSON.parse(message));
          }
        });
      });
      this.setupNetworkQualityUpdates(room, this.updateNetworkQualityReport);
      room.on("reconnected", () => {
        this.$toastr.success("Reconnected to the Room!");
        this.onRoomStateChange(room.state);
      });

      room.on("reconnecting", (error) => {
        if (error.code === 53001) {
          this.$toastr.warning("Reconnecting your signaling connection!");
          console.warn(
            "Reconnecting your signaling connection!",
            error.message
          );
        } else if (error.code === 53405) {
          this.$toastr.warning("Reconnecting your media connection!");
          console.warn("Reconnecting your media connection!", error.message);
        }
        this.onRoomStateChange(room.state);
      });

      // Subscribe to the media published by RemoteParticipants joining the Room later.
      room.on("participantConnected", (participant) => {
        this.participantConnected(participant, room);
      });

      if (
        (this.isHost && this.isLivestreamStart) ||
        (this.isModerator && this.isLivestreamStart)
      ) {
        this.startStream();
      }

      // Handle a disconnected RemoteParticipant.
      room.on("participantDisconnected", (participant) => {
        this.participantDisconnected(participant, room);
      });

      // Set the current active Participant.
      this.setCurrentActiveParticipant(room);

      // Update the active Participant when changed, only if the user has not
      // pinned any particular Participant as the active Participant.
      room.on("dominantSpeakerChanged", (participant) => {
        if (!window.isActiveParticipantPinned) {
          this.setCurrentActiveParticipant(room, participant);
        }
      });

      return new Promise((resolve, reject) => {
        // Leave the Room when the "beforeunload" event is fired.
        window.onbeforeunload = () => {
          room.disconnect();
          this.reset();
        };

        if (window.isMobile) {
          // TODO(mmalavalli): investigate why "pagehide" is not working in iOS Safari.
          // In iOS Safari, "beforeunload" is not fired, so use "pagehide" instead.
          window.onpagehide = () => {
            room.disconnect();
          };

          // On mobile browsers, use "visibilitychange" event to determine when
          // the app is backgrounded or foregrounded.
          document.onvisibilitychange = async () => {
            if (document.visibilityState === "hidden") {
              // When the app is backgrounded, your app can no longer capture
              // video frames.So, stop and unpublish the LocalVideoTrack.
              localVideoTrack.stop();
              room.localParticipant.unpublishTrack(localVideoTrack);
            } else {
              // When the app is foregrounded, your app can now continue to
              // capture video frames.So, publish a new LocalVideoTrack.
              localVideoTrack = await createLocalVideoTrack();
              await room.localParticipant.publishTrack(localVideoTrack);
            }
          };
        }

        room.once("disconnected", (room, error) => {
          // Clear the event handlers on document and window..
          window.onbeforeunload = null;
          if (window.isMobile) {
            window.onpagehide = null;
            document.onvisibilitychange = null;
          }

          // Stop the LocalVideoTrack.
          if (localVideoTrack) {
            localVideoTrack.stop();
          }
          this.localAudioTrack = null;
          this.localVideoTrack = null;
          // Handle the disconnected LocalParticipant.
          this.participantDisconnected(room.localParticipant, room);

          // Handle the disconnected RemoteParticipants.
          room.participants.forEach((participant) => {
            this.participantDisconnected(participant, room);
          });
          window.$("#participants button").remove();
          // Clear the active Participant's video.
          window.$activeVideo.get(0).srcObject = null;

          // Clear the Room reference used for debugging from the JavaScript console.
          window.room = null;

          if (error) {
            // Reject the Promise with the TwilioError so that the Room selection
            // modal (plus the TwilioError message) can be displayed.
            reject(error);
          } else {
            // Resolve the Promise so that the Room selection modal can be
            // displayed.
            resolve();
          }
        });
      });
    },

    /**
     * Create a LocalVideoTrack for your screen. You can then share it
     * with other Participants in the Room.
     * @param {number} height - Desired vertical resolution in pixels
     * @param {number} width - Desired horizontal resolution in pixels
     * @returns {Promise<LocalVideoTrack>}
     */
    createScreenTrack(height, width) {
      if (
        typeof navigator === "undefined" ||
        !navigator.mediaDevices ||
        !navigator.mediaDevices.getDisplayMedia
      ) {
        return Promise.reject(new Error("getDisplayMedia is not supported"));
      }
      return navigator.mediaDevices
        .getDisplayMedia({
          video: {
            height: height,
            width: width,
          },
        })
        .then((stream) => {
          return new LocalVideoTrack(stream.getVideoTracks()[0]);
        });
    },
    async screeenSharing() {
      try {
        // window.pinUser(window.room.localparticipant.sid);
        // Create and preview your local screen.
        this.screenTrack = await this.createScreenTrack(720, 1280);
        const userData = JSON.parse(window.room.localParticipant.identity);
        const identity = userData.u;
        const screenPreview = window
          .$(`div#active-participant [data-identity="${identity}"] video`)
          .get(0);
        this.screenTrack.attach(screenPreview);
        this.setActiveParticipant(window.room.localParticipant);
        // // Publish screen track to room
        window.room.localParticipant.videoTracks.forEach((publication) => {
          window.room.localParticipant.unpublishTrack(publication.track);
        });
        await window.room.localParticipant.publishTrack(this.screenTrack);

        // When screen sharing is stopped, unpublish the screen track.
        this.screenTrack.on("stopped", () => {
          if (window.room && window.room.localParticipant) {
            window.room.localParticipant.unpublishTrack(
              window.videoThat.screenTrack
            );
            setTimeout(() => {
              window.room.localParticipant.publishTrack(
                window.videoThat.localVideoTrack
              );
            }, 500);
            if (!this.localVideoTrack || this.isAudioOnly) {
              window.room.localParticipant.videoTracks.forEach(
                (publication) => {
                  publication.track.disable();
                  window.videoThat.trackPublished(
                    publication,
                    window.room.localParticipant
                  );
                }
              );
              window.room.localParticipant.publishTrack(
                window.videoThat.localVideoTrack
              );
              const participantVideo = window.$(
                `div#active-participant >div.participant.main#active-${window.room.localParticipant.sid} > video`
              );
              participantVideo.addClass("opacity-0");
              const participantThumbnailVideo = window.$(
                `div.participant#${window.room.localParticipant.sid}>video`
              );
              participantThumbnailVideo.addClass("opacity-0");
            } else {
              window.room.localParticipant.videoTracks.forEach(
                (publication) => {
                  publication.track.enable();
                  window.videoThat.trackPublished(
                    publication,
                    window.room.localParticipant
                  );
                }
              );
              window.room.localParticipant.publishTrack(
                window.videoThat.localVideoTrack
              );
            }
            const video = window.$(
              `div #${window.room.localParticipant.sid} [data-identity="${identity}"]`
            );
            video.css("opacity", "");
          }
        });
      } catch (e) {
        console.log(e.message);
      }
    },
    async StartRecording() {
      this.stopScreenRecording = true;
      this.screenRecordingText = "StopRecording";
      const stream = await navigator.mediaDevices.getDisplayMedia({
        video: { mediaSource: "screen" },
      });
      window.videoThat.mediaRecorder = new MediaRecorder(stream);
      const chunks = [];
      window.videoThat.mediaRecorder.ondataavailable = (e) =>
        chunks.push(e.data);

      window.videoThat.mediaRecorder.onstop = (e) => {
        console.log(e);
        const completeBlob = new Blob(chunks, { type: chunks[0].type });
        const fileReader = new FileReader();

        fileReader.onload = (e) => {
          let anchor = document.createElement("a");
          anchor.href = e.target.result;
          anchor.download = `video-${new Date()}`;
          anchor.click();
        };

        fileReader.readAsDataURL(completeBlob);
      };
      window.videoThat.mediaRecorder.start();
    },
    stopRecording() {
      window.videoThat.mediaRecorder.stop();
      this.screenRecordingText = "Screen Recording";
      this.stopScreenRecording = false;
    },
    downloadRecording() {
      const blob = new Blob(window.videoThat.recordedBlobs, {
        type: "video/webm",
      });
      const url = window.URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.style.display = "none";
      a.href = url;
      a.download = "test.webm";
      document.body.appendChild(a);
      a.click();
      setTimeout(() => {
        document.body.removeChild(a);
        window.URL.revokeObjectURL(url);
      }, 100);
    },
  },
};
</script>
<style>
@import url("./../styles/videos.css");
</style>

Code in action:

1 Comment

  1. MEYSAM

    Many thanks. Could you please provide a Github URL for this source? Because some points (step 2) is not clear enough for me.

    0
    0
    Reply

Submit a Comment

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

Subscribe

Select Categories