Files
watchparty/app/src/CallManager.js
T
2020-09-08 14:21:34 -04:00

298 lines
9.8 KiB
JavaScript

/************************************************************************
Copyright (C) Ayushya Amitabh - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
https://www.chromewatchparty.com/copyright
=========================================================================
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
CITED ARE THE SOLE INTELLECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
************************************************************************/
import VoxeetSDK from '@voxeet/voxeet-web-sdk';
import {
GetVoxeetTokenApi,
GetRefreshedVoxeetTokenApi,
LocalData,
UpdateVoxeetApi,
updateLocalData,
} from './Api';
export default class CallManager {
constructor(listener, notify) {
this.listener = listener;
this.notify = notify;
this.__refreshToken = LocalData().vaT;
this.__accessToken = LocalData().vrT;
this.__sessionInitialized = false;
this.__admin = false;
this.__adminOnly = true;
this.__useVideo = false;
}
toggleCall(onCall, conference, admin, adminOnly, useVideo) {
try {
this.listener({ callActionPending: true });
this.__admin = admin;
this.__adminOnly = adminOnly;
this.__useVideo = useVideo;
if (onCall === true) this.leaveCall();
else this.boot(conference, useVideo);
} catch (e) {
this.notify('Uh oh something went wrong, try rejoining the call.');
}
}
toggleMute(muted) {
this.listener({ callActionPending: true });
VoxeetSDK.conference.mute(VoxeetSDK.session.participant, !muted);
this.listener({ muted: !muted, callActionPending: false });
}
toggleVideo(useVideo) {
if (useVideo === true) {
VoxeetSDK.conference.startVideo(VoxeetSDK.session.participant).then(() => {
this.listener({ usingVideo: useVideo });
});
} else {
VoxeetSDK.conference.stopVideo(VoxeetSDK.session.participant).then(() => {
this.listener({ usingVideo: useVideo });
});
}
}
async boot(conference, useVideo) {
await this.initialize();
const opened = await this.openSession();
if (opened && conference) {
await VoxeetSDK.conference
.fetch(conference._id)
.then((c) => this.joinCall(c, useVideo))
.catch((err) => {
console.error('Failed to fetch conference', err);
this.startCall(useVideo);
});
} else if (opened && !conference) {
this.startCall(useVideo);
} else {
this.listener({ onCall: false, muted: false, callActionPending: false });
console.error('Unable to open Voxeet Session');
}
}
async startCall(useVideo) {
if (!this.__adminOnly || (this.__adminOnly && this.__admin)) {
VoxeetSDK.conference
.create({
alias: `AUTODELETE_${LocalData().partyName}`,
pin: LocalData().partyPin,
})
.then(async (conference) => {
const updatedVoxeet = await UpdateVoxeetApi({
conference,
partyName: LocalData().partyName,
partyPin: LocalData().partyPin,
userName: LocalData().userName,
})
.then((res) => res.json())
.then((res) => res.status <= 299)
.catch((err) => {
if (err.conference) {
this.joinCall(err.conference, useVideo);
return true;
} else {
this.notify(err.message);
this.listener({
onCall: false,
muted: false,
callActionPending: false,
usingVideo: false,
});
console.error('Update Voxeet API Error', err);
return false;
}
});
if (updatedVoxeet === true) {
this.joinCall(conference, useVideo);
} else {
this.listener({
onCall: false,
muted: false,
callActionPending: false,
usingVideo: false,
});
console.error('Failed to update party Voxeet');
}
});
} else {
this.notify('Ask your party admin to start the call.');
this.leaveCall();
}
}
async joinCall(conference, useVideo) {
const joined = await VoxeetSDK.conference
.join(conference, { constraints: { audio: true, video: useVideo } })
.then(() => true)
.catch((e) => {
this.listener({
onCall: false,
muted: false,
callActionPending: false,
usingVideo: false,
});
this.notify('Unable to join call. Try again in a moment.');
console.error('Error joining call', e);
return false;
});
if (joined === true) {
this.listener({ usingVideo: useVideo });
}
// Leave changes as comment for later
// if (!joined) {
// this.startCall(useVideo);
// }
}
async leaveCall() {
VoxeetSDK.conference.leave().then(() => {
VoxeetSDK.session.close();
this.listener({
onCall: false,
muted: false,
callActionPending: false,
usingVideo: false,
conference: null,
});
});
}
async openSession() {
if (!this.__sessionInitialized) {
try {
await VoxeetSDK.session.open({
externalId: LocalData().userUid,
name: LocalData().userName,
});
return true;
} catch (error) {
console.error('Error Opening Voxeet Session', error);
}
console.error('Session not initialized');
return false;
}
return true;
}
async initialize() {
VoxeetSDK.conference.on('joined', () => {
this.listener({ onCall: true, callActionPending: false, usingVideo: this.__useVideo });
});
VoxeetSDK.conference.on('left', () => {
this.listener({
onCall: false,
callActionPending: false,
usersOnCall: {},
usingVideo: false,
});
});
VoxeetSDK.conference.on('streamAdded', (participant, stream) => {
this.listener({ callActionPending: true });
const vid = 'video#' + participant.id;
if (!document.querySelector(vid)) {
const vidNode = document.createElement('video');
vidNode.id = participant.id;
vidNode.muted = true;
vidNode.srcObject = stream;
vidNode.onloadedmetadata = vidNode.play;
const vidContainerNode = document.createElement('div');
vidContainerNode.classList.add('vxt-video');
vidContainerNode.setAttribute('data-id', participant.id);
vidContainerNode.setAttribute('data-name', participant.info.name || '');
vidContainerNode.appendChild(vidNode);
console.log(participant);
if (participant.info.externalId !== LocalData().userUid) {
const volSliderNode = document.createElement('input');
volSliderNode.setAttribute('type', 'range');
volSliderNode.min = '0';
volSliderNode.max = '1';
volSliderNode.value = '1';
volSliderNode.step = '0.1';
volSliderNode.id = 'vxt-slider-' + participant.id;
volSliderNode.classList.add('vxt-slider');
volSliderNode.onchange = (e) => {
const v = e.target.value;
const audioOuts = document.querySelectorAll('audio.vxt-audio');
if (audioOuts.length > 0) {
audioOuts.forEach((el) => {
if (el.id === `audio-${participant.id}`) {
el.volume = v;
}
});
}
};
vidContainerNode.appendChild(volSliderNode);
}
document.querySelector('.vxt-video-container').appendChild(vidContainerNode);
}
this.listener({ callActionPending: false });
});
VoxeetSDK.conference.on('streamRemoved', (participant, stream) => {
const vcid = ".vxt-video[data-id='" + participant.id + "']";
const vid = 'video#' + participant.id;
if (document.querySelector(vcid)) {
document
.querySelector(vid)
.srcObject.getTracks()
.forEach((track) => {
track.stop();
});
document.querySelector(vcid).remove();
}
this.listener({ callActionPending: false });
});
if (
this.__accessToken !== undefined ||
this.__refreshToken !== undefined ||
this.__accessToken !== null ||
this.__refreshToken !== null
) {
await GetVoxeetTokenApi({ partyName: LocalData().partyName })
.then((res) => res.json())
.then(async (tokenResult) => {
if (tokenResult.status <= 299) {
const { access_token, refresh_token } = tokenResult;
this.updateTokens(access_token, refresh_token);
await VoxeetSDK.initializeToken(access_token, refresh_token);
} else {
console.error('Get Voxeet Token API Result Error', tokenResult);
}
})
.catch((err) => {
console.error('Get Voxeet Token API error', err);
});
} else {
await VoxeetSDK.initializeToken(this.__accessToken, this.__refreshToken);
}
}
async refreshToken() {
return new Promise((resolve, reject) => {
GetRefreshedVoxeetTokenApi({ token: this.__refreshToken })
.then((res) => res.json())
.then((tokenResult) => {
const { access_token, refresh_token } = tokenResult;
this.updateTokens(access_token, refresh_token);
resolve(access_token);
})
.catch((err) => {
console.error('Get Voxeet Token API error', err);
reject(err);
});
});
}
updateTokens(aT, rT) {
this.__accessToken = aT;
this.__refreshToken = rT;
updateLocalData({ vaT: aT, vrT: rT });
}
}