From 6ccf7c620165994d1ed9b5fa8221bcdb6651a849 Mon Sep 17 00:00:00 2001 From: Ayushya Amitabh Date: Sat, 8 Aug 2020 17:41:40 -0400 Subject: [PATCH] v1.6.0/speculative fix for call conflict --- app/src/CallManager.js | 86 ++++++++++++++++++----------- app/src/Thread.css | 5 +- functions/modules/updateVoxeet.js | 63 ++++++++++++++------- functions/webhooks/conferenceEnd.js | 78 +++++++++++++++++++------- 4 files changed, 157 insertions(+), 75 deletions(-) diff --git a/app/src/CallManager.js b/app/src/CallManager.js index f2a2c23..5a82256 100644 --- a/app/src/CallManager.js +++ b/app/src/CallManager.js @@ -9,14 +9,14 @@ ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, U CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. ************************************************************************/ -import VoxeetSDK from '@voxeet/voxeet-web-sdk'; +import VoxeetSDK from "@voxeet/voxeet-web-sdk"; import { GetVoxeetTokenApi, GetRefreshedVoxeetTokenApi, LocalData, UpdateVoxeetApi, updateLocalData, -} from './Api'; +} from "./Api"; export default class CallManager { constructor(listener, notify) { @@ -29,11 +29,15 @@ export default class CallManager { this.__adminOnly = true; } toggleCall(onCall, conference, admin, adminOnly) { - this.listener({ callActionPending: true }); - this.__admin = admin; - this.__adminOnly = adminOnly; - if (onCall === true) this.leaveCall(); - else this.boot(conference); + try { + this.listener({ callActionPending: true }); + this.__admin = admin; + this.__adminOnly = adminOnly; + if (onCall === true) this.leaveCall(); + else this.boot(conference); + } catch (e) { + this.notify("Uh oh something went wrong, try rejoining the call."); + } } toggleMute(muted) { this.listener({ callActionPending: true }); @@ -48,20 +52,23 @@ export default class CallManager { .fetch(conference._id) .then(this.joinCall) .catch((err) => { - console.error('Failed to fetch conference', err); + console.error("Failed to fetch conference", err); this.startCall(); }); } else if (opened && !conference) { this.startCall(); } else { this.listener({ onCall: false, muted: false, callActionPending: false }); - console.error('Unable to open Voxeet Session'); + console.error("Unable to open Voxeet Session"); } } async startCall() { if (!this.__adminOnly || (this.__adminOnly && this.__admin)) { VoxeetSDK.conference - .create({ alias: `AUTODELETE_${LocalData().partyName}`, pin: LocalData().partyPin }) + .create({ + alias: `AUTODELETE_${LocalData().partyName}`, + pin: LocalData().partyPin, + }) .then(async (conference) => { const updatedVoxeet = await UpdateVoxeetApi({ conference, @@ -77,20 +84,28 @@ export default class CallManager { return true; } else { this.notify(err.message); - this.listener({ onCall: false, muted: false, callActionPending: false }); - console.error('Update Voxeet API Error', err); + this.listener({ + onCall: false, + muted: false, + callActionPending: false, + }); + console.error("Update Voxeet API Error", err); return false; } }); if (updatedVoxeet === true) { this.joinCall(conference); } else { - this.listener({ onCall: false, muted: false, callActionPending: false }); - console.error('Failed to update party Voxeet'); + this.listener({ + onCall: false, + muted: false, + callActionPending: false, + }); + console.error("Failed to update party Voxeet"); } }); } else { - this.notify('Ask your party admin to start the call.'); + this.notify("Ask your party admin to start the call."); this.leaveCall(); } } @@ -99,14 +114,15 @@ export default class CallManager { .join(conference, { constraints: { audio: true, video: false } }) .then(() => true) .catch((e) => { - this.listener({ onCall: false, muted: false, callActionPending: false }); - this.notify('Unable to join call. Try again in a moment.'); - console.error('Error joining call', e); + this.listener({ + onCall: false, + muted: false, + callActionPending: false, + }); + this.notify("Unable to join call. Try again in a moment."); + console.error("Error joining call", e); return true; }); - if (!joined) { - this.startCall(); - } } async leaveCall() { VoxeetSDK.conference.leave().then(() => { @@ -123,9 +139,9 @@ export default class CallManager { }); return true; } catch (error) { - console.error('Error Opening Voxeet Session', error); + console.error("Error Opening Voxeet Session", error); } - console.error('Session not initialized'); + console.error("Session not initialized"); return false; } return true; @@ -138,22 +154,26 @@ export default class CallManager { this.listener({ usersOnCall }); } async initialize() { - VoxeetSDK.conference.on('participantAdded', () => this.usersListHandle()); - VoxeetSDK.conference.on('participantUpdated', () => this.usersListHandle()); + VoxeetSDK.conference.on("participantAdded", () => this.usersListHandle()); + VoxeetSDK.conference.on("participantUpdated", () => this.usersListHandle()); - VoxeetSDK.conference.on('joined', () => { + VoxeetSDK.conference.on("joined", () => { this.listener({ onCall: true, callActionPending: false }); }); - VoxeetSDK.conference.on('left', () => { - this.listener({ onCall: false, callActionPending: false, usersOnCall: {} }); + VoxeetSDK.conference.on("left", () => { + this.listener({ + onCall: false, + callActionPending: false, + usersOnCall: {}, + }); }); - VoxeetSDK.conference.on('streamAdded', (participant, stream) => { + VoxeetSDK.conference.on("streamAdded", (participant, stream) => { this.listener({ callActionPending: false }); }); - VoxeetSDK.conference.on('streamRemoved', (participant, stream) => { + VoxeetSDK.conference.on("streamRemoved", (participant, stream) => { this.listener({ callActionPending: false }); }); @@ -171,11 +191,11 @@ export default class CallManager { this.updateTokens(access_token, refresh_token); VoxeetSDK.initializeToken(access_token, refresh_token); } else { - console.error('Get Voxeet Token API Result Error', tokenResult); + console.error("Get Voxeet Token API Result Error", tokenResult); } }) .catch((err) => { - console.error('Get Voxeet Token API error', err); + console.error("Get Voxeet Token API error", err); }); } else { VoxeetSDK.initializeToken(this.__accessToken, this.__refreshToken); @@ -191,7 +211,7 @@ export default class CallManager { resolve(access_token); }) .catch((err) => { - console.error('Get Voxeet Token API error', err); + console.error("Get Voxeet Token API error", err); reject(err); }); }); diff --git a/app/src/Thread.css b/app/src/Thread.css index eabe966..1988e4f 100644 --- a/app/src/Thread.css +++ b/app/src/Thread.css @@ -12,8 +12,9 @@ COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. .thread-container * { margin: 0; padding: 0; - font-family: 'Roboto', sans-serif !important; + font-family: "Roboto", sans-serif !important; letter-spacing: 1.15px; + word-break: break-word !important; } .thread-container { max-height: none; @@ -99,7 +100,7 @@ COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. line-height: 16px; font-size: 13px; font-weight: 100; - font-family: 'Roboto'; + font-family: "Roboto"; letter-spacing: 1.5px; white-space: break-spaces; word-break: break-all; diff --git a/functions/modules/updateVoxeet.js b/functions/modules/updateVoxeet.js index 386f0d9..dc7415b 100644 --- a/functions/modules/updateVoxeet.js +++ b/functions/modules/updateVoxeet.js @@ -1,16 +1,23 @@ -const admin = require('firebase-admin'); +const admin = require("firebase-admin"); module.exports = async (req, res) => { - const { conference, partyName, partyPin, userName, uid } = JSON.parse(req.body); + const { conference, partyName, partyPin, userName, uid } = JSON.parse( + req.body + ); // GET ROOM PIN const [exists, roomPin, conferencePending, partyConference] = await admin .firestore() - .collection('rooms') + .collection("rooms") .doc(partyName) .get() .then((roomSnap) => { const data = roomSnap.exists ? roomSnap.data() : {}; - return [roomSnap.exists, data.pin, data.conferencePending, data.conference]; + return [ + roomSnap.exists, + data.pin, + data.conferencePending, + data.conference, + ]; }); // CHECK IF ROOM EXISTS if (exists === true) { @@ -22,47 +29,61 @@ module.exports = async (req, res) => { // UPDATE CONFERENCE DETAILS await admin .firestore() - .collection('rooms') + .collection("rooms") .doc(partyName) .update({ conference }) .catch((err) => - res.status(500).send({ message: 'Unable to update the call details', error: err }) + res.status(500).send({ + message: "Unable to update the call details", + error: err, + }) ); // REMOVE PENDING STATUS await admin - .firestore() - .collection('rooms') - .doc(partyName) - .update({ conferencePending : admin.firestore.FieldValue.delete() }) - .catch((err) => console.error('Failed to remove pending status', err)); + .firestore() + .collection("rooms") + .doc(partyName) + .update({ + conferencePending: admin.firestore.FieldValue.delete(), + }) + .catch((err) => + console.error("Failed to remove pending status", err) + ); // ADD SYSTEM MESSAGE await admin .firestore() - .collection('rooms') + .collection("rooms") .doc(partyName) - .collection('messages') + .collection("messages") .add({ - uid: '', - name: '', + uid: "", + name: "", text: `${userName} started the call, everyone can join now.`, time: new Date(), }) - .catch((err) => console.error('Failed to add update conference message', err)); - res.status(200).send({ message: 'Started the conference' }); + .catch((err) => + console.error("Failed to add update conference message", err) + ); + res.status(200).send({ message: "Started the conference" }); } else { - res.status(409).send({ message: 'Looks like someone is already starting a call' }); + res.status(409).send({ + message: "Looks like someone is already starting a call", + }); } } else if (partyConference) { - res.status(409).send({ message: 'Call already exists', conference: partyConference }); + res.status(409).send({ + message: "Call already exists", + conference: partyConference, + }); } } else { // WRONG ROOM PIN - res.status(401).send({ message: 'Wrong party pin, try rejoining' }); + res.status(401).send({ message: "Wrong party pin, try rejoining" }); } } } else { // ROOM DOESN'T EXIST - res.status(404).send({ message: 'Party Not Found' }); + res.status(404).send({ message: "Party Not Found" }); } return; }; diff --git a/functions/webhooks/conferenceEnd.js b/functions/webhooks/conferenceEnd.js index 60febff..b66ccef 100644 --- a/functions/webhooks/conferenceEnd.js +++ b/functions/webhooks/conferenceEnd.js @@ -1,30 +1,70 @@ -const admin = require('firebase-admin'); +const admin = require("firebase-admin"); module.exports = async (req, res) => { - console.info(`[START]\tWEBHOOKS/Conference End\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}`); - if (req.body.eventType === 'Conference.Ended') { + console.info( + `[START]\tWEBHOOKS/Conference End\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}` + ); + // MAKE SURE CORRECT EVENT TYPE + if (req.body.eventType === "Conference.Ended") { + // MAKE SURE IT'S NOT A DUPLICATE FROM CREATE CONFLICT if (req.body.participants.length > 0) { - if (req.body.confAlias.startsWith('AUTODELETE_')) { + // MAKE SURE ELIGIBLE FOR DELETE + if (req.body.confAlias.startsWith("AUTODELETE_")) { const partyName = req.body.confAlias.replace(/AUTODELETE_/, ""); - admin.firestore() - .collection('rooms') - .doc(partyName) - .update({ conference: admin.firestore.FieldValue.delete() }) - .then(() => { - console.info(`[DONE]\tWEBHOOKS/Conference End\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}`); - return; - }) - .catch(err => { - console.error(`[ERROR]\tWEBHOOKS/Conference End/Firebase Error\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}\nError:${JSON.stringify(err)}`) - }); - res.status(200).send({ message: 'Processed conference end'}); + const existingConference = await admin + .firestore() + .collection("rooms") + .doc(partyName) + .get() + .then((snap) => (snap.exists ? snap.data().conference : null)); + // MAKE SURE EXISTS AND HAS NOT BEEN REPLACED + if ( + existingConference && + existingConference._id === req.body.conference.confId + ) { + // REMOVE CONFERENCE DETAILS + await admin + .firestore() + .collection("rooms") + .doc(partyName) + .update({ conference: admin.firestore.FieldValue.delete() }) + .then(() => { + console.info( + `[DONE]\tWEBHOOKS/Conference End\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}` + ); + return; + }) + .catch((err) => { + console.error( + `[ERROR]\tWEBHOOKS/Conference End/Firebase Error\tID:${ + req.body.conference.confId + }\tParty:${ + req.body.conference.confAlias + }\tError:${JSON.stringify(err)}` + ); + }); + } else { + console.info( + `[DONE]\tWEBHOOKS/Conference End/Conference Was Already Replaced\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}` + ); + } + res.status(200).send({ message: "Processed conference end" }); + } else { + console.info( + `[DONE]\tWEBHOOKS/Conference End/Not Eligible For Autodelete\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}` + ); + res.status(200).send({ message: "Processed conference end" }); } } else { - console.info(`[DONE]\tWEBHOOKS/Conference End/End Of Duplicate Conference\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}`); + console.info( + `[DONE]\tWEBHOOKS/Conference End/End Of Duplicate Conference\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}` + ); } } else { - console.error(`[ERROR]\tWEBHOOKS/Conference End/Wrong Event Type\nID:${req.body.conference.confId}\nParty:${req.body.conference.confAlias}`) - res.status(400).send({ message: 'Wrong event type' }); + console.error( + `[ERROR]\tWEBHOOKS/Conference End/Wrong Event Type\tID:${req.body.conference.confId}\tParty:${req.body.conference.confAlias}` + ); + res.status(400).send({ message: "Wrong event type" }); } return; };