From ccd7f1cfab6fe95839eee3bcd5ab6a9d0c2779c2 Mon Sep 17 00:00:00 2001 From: Ayushya Amitabh Date: Fri, 29 May 2020 17:43:10 -0400 Subject: [PATCH] v1.3.0-beta/start of voice call and custom code: --- .gitignore | 1 + app/src/Api.js | 14 +++ app/src/App.js | 5 +- app/src/CallManager.js | 119 +++++++++--------- app/src/Chat.js | 57 +++++---- app/src/index.css | 3 + app/src/index.js | 1 + functions/app.js | 2 + functions/modules/authenticator.js | 60 ++++----- functions/modules/createRoom.js | 27 +++-- functions/modules/index.js | 4 + functions/modules/tokenRefresher.js | 20 +++ functions/modules/tokenValidator.js | 20 +++ package-lock.json | 181 ++++++++++++++++++++++++++++ package.json | 5 +- 15 files changed, 396 insertions(+), 123 deletions(-) create mode 100644 functions/modules/tokenRefresher.js create mode 100644 functions/modules/tokenValidator.js create mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore index 6335649..99bb35d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ yarn-error.log* .history/ FirebaseSecret.json +VoxeetSecret.json .firebase *.cache *.log \ No newline at end of file diff --git a/app/src/Api.js b/app/src/Api.js index 92bc5bb..1c68191 100644 --- a/app/src/Api.js +++ b/app/src/Api.js @@ -109,3 +109,17 @@ export const UpdatePlayerApi = async (data) => { body: JSON.stringify(data), }); }; + +export const GetVoxeetTokenApi = async () => { + return fetcher({ + endpoint: 'validator', + method: 'GET', + }); +}; + +export const GetRefreshedVoxeetTokenApi = async (data) => { + return fetcher({ + endpoint: `refresher/${data.token}`, + method: 'GET', + }); +}; diff --git a/app/src/App.js b/app/src/App.js index 772ef5c..793af17 100644 --- a/app/src/App.js +++ b/app/src/App.js @@ -7,7 +7,6 @@ import firebase from 'firebase/app'; import 'firebase/firestore'; import Inactive from './Inactive'; import { getUserUid } from './Api'; -import Chat from './Chat'; const darkTheme = createMuiTheme({ palette: { @@ -20,7 +19,7 @@ const darkTheme = createMuiTheme({ const Routes = (props) => ( - + ); @@ -34,7 +33,7 @@ const App = () => { useEffect(() => { setLoading(true); - // getUserUid(); + getUserUid(); firebase .firestore() .collection('server') diff --git a/app/src/CallManager.js b/app/src/CallManager.js index 32cc494..73f8fd7 100644 --- a/app/src/CallManager.js +++ b/app/src/CallManager.js @@ -1,67 +1,70 @@ -/* global webkitRTCPeerConnection */ -const emptyListener = (e) => console.log('EMPTY LISTENER FOR PHONE MANAGER', e); +import { VoxeetSDK } from '@voxeet/voxeet-web-sdk'; +import { GetVoxeetTokenApi, GetRefreshedVoxeetTokenApi } from './Api'; -const CONFIG = { - iceServers: [ - { - urls: [ - 'stun:stun.l.google.com:19302', - 'stun:stun1.l.google.com:19302', - 'stun:stun2.l.google.com:19302', - 'stun:stun3.l.google.com:19302', - 'stun:stun4.l.google.com:19302', - ], - }, - ], - iceCandidatePoolSize: 10, -}; - -export default class PhoneManager { - constructor(listener = emptyListener) { - this.mediaStream = null; +class CallManager { + constructor(listener) { this.listener = listener; - /* B I N D S */ - this.joinCall = this.joinCall.bind(this); - this.getMediaStream = this.getMediaStream.bind(this); + this.__refreshToken = null; + this.__accessToken = null; } - async getMediaStream() { + toggleCall(onCall, conferenceRoomId) { + if (onCall === true) this.leaveCall(); + else this.boot(conferenceRoomId); + } + async boot(conferenceRoomId) { + if ( + this.__accessToken !== undefined && + this.__refreshToken !== undefined && + this.__accessToken !== null && + this.__refreshToken !== null + ) { + try { + await VoxeetSDK.session.open({ + externalId: window.localStorage.getItem('watchPartyUserUid'), + name: window.localStorage.getItem('watchPartyUserName'), + }); + } catch (error) { + console.error('Error Opening Voxeet Session', error); + } + } else { + this.initialize(); + } + } + async leaveCall() {} + async openSession() {} + async initialize() { + GetVoxeetTokenApi() + .then((res) => res.json()) + .then((tokenResult) => { + if (tokenResult.status <= 299) { + const { access_token, refresh_token } = tokenResult; + this.updateTokens(access_token, refresh_token); + VoxeetSDK.initializeToken(access_token, this.refreshToken); + } else { + console.error('Get Voxeet Token API Result Error', tokenResult); + } + }) + .catch((err) => { + console.error('Get Voxeet Token API error', err); + }); + } + async refreshToken() { return new Promise((resolve, reject) => { - if (this.mediaStream !== null && this.mediaStream !== undefined) resolve(true); - navigator.mediaDevices - .getUserMedia({ - video: false, - audio: true, + 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); }) - .then((mediaStream) => { - this.mediaStream = mediaStream; - resolve(true); - }) - .catch((error) => reject(error)); + .catch((err) => { + console.error('Get Voxeet Token API error', err); + reject(err); + }); }); } - async joinCall() { - await this.getMediaStream(); - - var peerConnection = new webkitRTCPeerConnection(CONFIG); - console.log('Created Peer Connection', peerConnection); - - peerConnection.addTrack(this.mediaStream.getAudioTracks()[0], this.mediaStream); - peerConnection.onicecandidate = (ev) => console.log('Got ICE', ev); - - var offer = peerConnection.createOffer(); - console.log('Created Offer', offer); - - var answer = peerConnection.createAnswer(); - console.log('Created Answer', answer); - } - async leaveCall() { - if (this.mediaStream !== null && this.mediaStream !== undefined) { - this.mediaStream.getAudioTracks().forEach((track) => track.stop()); - } - } - toggleMute() { - if (this.mediaStream !== null && this.mediaStream !== undefined) { - this.mediaStream.getAudioTracks().forEach((track) => (track.enabled = !track.enabled)); - } + updateTokens(aT, rT) { + this.__accessToken = aT; + this.__refreshToken = rT; } } diff --git a/app/src/Chat.js b/app/src/Chat.js index 34a4e15..10643de 100644 --- a/app/src/Chat.js +++ b/app/src/Chat.js @@ -55,16 +55,18 @@ export default class Chat extends Component { this.port = null; this.gifClick = this.gifClick.bind(this); this.endParty = this.endParty.bind(this); + this.handleMute = this.handleMute.bind(this); this.leaveParty = this.leaveParty.bind(this); this.shareParty = this.shareParty.bind(this); this.syncPlayer = this.syncPlayer.bind(this); this.sendMessage = this.sendMessage.bind(this); this.getCarousel = this.getCarousel.bind(this); this.handlePhoneCall = this.handlePhoneCall.bind(this); + this.callEventListener = this.callEventListener.bind(this); + this.mCallManager = new CallManager(this.callEventListener); this.sendMessageInvoker = this.sendMessageInvoker.bind(this); this.videoEventListener = this.videoEventListener.bind(this); this.mVideoListener = new VideoListener(this.videoEventListener); - this.mCallManager = new CallManager(); } componentDidMount() { // EVENT CAPTURE FOR ENTER TO SEND @@ -84,24 +86,24 @@ export default class Chat extends Component { this.mVideoListener.start(); // CHROME FIREBASE FIRESTORE LISTENER - // this.port = chrome.runtime.connect({ name: window.localStorage.getItem('watchPartyRoomId') }); - // // Initialize listener - // this.port.postMessage({ - // type: 'INIT', - // roomId: window.localStorage.getItem('watchPartyRoomId'), - // }); - // // Listener - // this.port.onMessage.addListener((response) => { - // if (response.isError === true) { - // this.props.notify('Uh oh. Got lost from the party.'); - // goTo(Create, this.props); - // return; - // } - // this.setState(response); - // if (response.player) this.mVideoListener.doUpdate(response.player); - // const pcc = document.getElementsByClassName('thread-container')[0]; - // if (pcc) pcc.scrollTop = pcc.scrollHeight; - // }); + this.port = chrome.runtime.connect({ name: window.localStorage.getItem('watchPartyRoomId') }); + // Initialize listener + this.port.postMessage({ + type: 'INIT', + roomId: window.localStorage.getItem('watchPartyRoomId'), + }); + // Listener + this.port.onMessage.addListener((response) => { + if (response.isError === true) { + this.props.notify('Uh oh. Got lost from the party.'); + goTo(Create, this.props); + return; + } + this.setState(response); + if (response.player) this.mVideoListener.doUpdate(response.player); + const pcc = document.querySelector('.thread-container'); + if (pcc) pcc.scrollTop = pcc.scrollHeight; + }); // DONE WITH MOUNTING TASKS this.props.updateLoading(false); @@ -112,6 +114,7 @@ export default class Chat extends Component { videoEventListener(video) { this.syncPlayer(video); } + callEventListener(event) {} syncPlayer(video) { const { status, showName, time, source, element, showChanged } = video; if (element) { @@ -216,10 +219,12 @@ export default class Chat extends Component { : giphyFetch.search(this.state.newMessage, { offset, limit: 1 }); } async handlePhoneCall() { - if (!this.state.onCall) this.mCallManager.joinCall(); - else this.mCallManager.leaveCall(); this.setState({ onCall: !this.state.onCall }); } + handleMute() { + this.mCallManager.toggleMute(); + this.setState({ muted: !this.state.muted }); + } render() { const { admin, name, messages, newMessage, gif, onCall, muted } = this.state; if (getPlayerSource() === PLAYER_SOURCE.HBONOW && messages[0] !== alertMessage) @@ -253,17 +258,17 @@ export default class Chat extends Component {
- {newMessage ? null : ( + {gif || newMessage ? null : ( {onCall ? : } )} - {!newMessage && onCall && !muted ? ( - this.mCallManager.toggleMute()} color="secondary"> + {!gif && !newMessage && onCall && !muted ? ( + - ) : !newMessage && onCall && muted ? ( - this.mCallManager.toggleMute()} color="primary"> + ) : !gif && !newMessage && onCall && muted ? ( + ) : null} diff --git a/app/src/index.css b/app/src/index.css index 80101ec..e0204a6 100644 --- a/app/src/index.css +++ b/app/src/index.css @@ -8,6 +8,9 @@ body { background-color: black; transition: max-width ease-in-out 0.35s; } +body.local { + background-color: white; +} body.visible { width: var(--body-width); } diff --git a/app/src/index.js b/app/src/index.js index 70335f7..37f7987 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -57,6 +57,7 @@ switch (getPlayerSource()) { document.body.classList.add('hbo'); break; case PLAYER_SOURCE.LOCAL: + document.body.classList.add('local'); break; default: break; diff --git a/functions/app.js b/functions/app.js index 743d13e..e706444 100644 --- a/functions/app.js +++ b/functions/app.js @@ -6,6 +6,8 @@ const app = express(); app.use(cors({ origin: true })); app.use(FunctionModules.authenticator); +app.get('/validator', FunctionModules.tokenValidator); +app.get('/refresher/:token', FunctionModules.tokenRefresher); app.post('/create', FunctionModules.createRoom); app.post('/join', FunctionModules.joinRoom); app.post('/message', FunctionModules.sendMessage); diff --git a/functions/modules/authenticator.js b/functions/modules/authenticator.js index e2d0e1e..303cc8c 100644 --- a/functions/modules/authenticator.js +++ b/functions/modules/authenticator.js @@ -4,31 +4,37 @@ module.exports = (req, res, next) => { res.status(200).send(); return next(); } - const token = req.headers.authorization.split(' ')[1]; - const reqToken = token.split(':')[0]; - const reqUid = token.split(':')[1]; - admin - .auth() - .verifyIdToken(reqToken) - .then((user) => { - if (user.uid === reqUid) { - console.info('Verified Token - UID - ', reqUid); - if (req.method === 'POST') { - const bodydata = JSON.parse(req.body); - bodydata.uid = user.uid; - req.body = JSON.stringify(bodydata); - } - if (req.method === 'GET' || req.method === 'DELETE') { - req.body = JSON.stringify({ uid: user.uid }); - } - return next(); - } else { - return res.status(401).send({ message: 'Unauthorized' }); - } - }) - .catch((err) => { - console.error('Failed Auth - ', req.headers.authorization, '\n', err); - return res.status(500).send({ message: 'Unable to verify user token', ...err }); - }); - return null; + if (req.headers.authorization) { + try { + const token = req.headers.authorization.split(' ')[1]; + const reqToken = token.split(':')[0]; + const reqUid = token.split(':')[1]; + admin + .auth() + .verifyIdToken(reqToken) + .then((user) => { + if (user.uid === reqUid) { + console.info('Verified Token - UID - ', reqUid); + if (req.method === 'POST') { + const bodydata = JSON.parse(req.body); + bodydata.uid = user.uid; + req.body = JSON.stringify(bodydata); + } + if (req.method === 'GET' || req.method === 'DELETE') { + req.body = JSON.stringify({ uid: user.uid }); + } + return next(); + } else { + return res.status(401).send({ message: 'Unauthorized' }); + } + }) + .catch((err) => { + console.error('Failed Auth - ', req.headers.authorization, '\n', err); + return res.status(500).send({ message: 'Unable to verify user token', ...err }); + }); + } catch (err) { + return res.status(401).send({ message: 'Got no user token', ...err }); + } + } + return res.status(401).send({ message: 'Got no user token' }); }; diff --git a/functions/modules/createRoom.js b/functions/modules/createRoom.js index 9e8cd71..b7e6232 100644 --- a/functions/modules/createRoom.js +++ b/functions/modules/createRoom.js @@ -4,16 +4,24 @@ module.exports = async (req, res) => { const bodydata = JSON.parse(req.body); console.log('Requested Create - UID - ', bodydata.uid); if (!bodydata.userName) res.status(400).send({ message: 'Missing user name' }); - const roomId = await admin + if (!bodydata.roomName) res.status(400).send({ message: 'Missing party name' }); + const roomNameTaken = await admin .firestore() .collection('rooms') - .add({ + .doc(bodydata.roomName) + .get() + .then((docSnap) => docSnap.exists); + if (roomNameTaken) return res.status(409).send({ message: 'That party name is already taken' }); + await admin + .firestore() + .collection('rooms') + .doc(bodydata.roomName) + .set({ name: bodydata.roomName, player: bodydata.player, createdAt: new Date(), updatedAt: new Date(), }) - .then((ref) => ref.id) .catch((err) => { res.status(500).send({ message: 'Unable to start party.' }); console.error(err); @@ -21,7 +29,7 @@ module.exports = async (req, res) => { await admin .firestore() .collection('rooms') - .doc(roomId) + .doc(bodydata.roomName) .collection('messages') .add({ uid: '', @@ -30,12 +38,15 @@ module.exports = async (req, res) => { time: new Date(), }) .catch((err) => { - console.error(`Failed to add system create party message : \n\t Room: ${roomId}`, err); + console.error( + `Failed to add system create party message : \n\t Room: ${bodydata.roomName}`, + err + ); }); await admin .firestore() .collection('rooms') - .doc(roomId) + .doc(bodydata.roomName) .collection('users') .doc(bodydata.uid) .set({ @@ -44,9 +55,9 @@ module.exports = async (req, res) => { }) .catch((err) => { console.error( - `Failed to set admin when creating party : \n\t Room: ${roomId} \n\t User: ${bodydata.uid}`, + `Failed to set admin when creating party : \n\t Room: ${bodydata.roomName} \n\t User: ${bodydata.uid}`, err ); }); - res.status(201).send({ roomId, message: 'Started a party!' }); + res.status(201).send({ roomId: bodydata.roomName, message: 'Started a party!' }); }; diff --git a/functions/modules/index.js b/functions/modules/index.js index aabb20c..cc4c21a 100644 --- a/functions/modules/index.js +++ b/functions/modules/index.js @@ -8,6 +8,8 @@ const leaveRoom = require('./leaveRoom'); const deleteRoom = require('./deleteRoom'); const timeDiff = require('./timeDiff'); const updatePlayer = require('./updatePlayer'); +const tokenValidator = require('./tokenValidator'); +const tokenRefresher = require('./tokenRefresher'); module.exports = { authenticator, @@ -20,4 +22,6 @@ module.exports = { scheduledCleanUp, cleanUpRoom, timeDiff, + tokenValidator, + tokenRefresher, }; diff --git a/functions/modules/tokenRefresher.js b/functions/modules/tokenRefresher.js new file mode 100644 index 0000000..8981197 --- /dev/null +++ b/functions/modules/tokenRefresher.js @@ -0,0 +1,20 @@ +const VoxeetSecret = require('../VoxeetSecret.json'); +module.exports = async (req, res) => { + const { key, secret } = VoxeetSecret; + const credentials = btoa(encodeURI(key) + ':' + encodeURI(secret)); + const headers = { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache', + Authorization: 'Basic ' + credentials, + }; + const oauth = await fetch(VoxeetSecret.refreshURL, { + method: 'POST', + headers, + body: JSON.stringify({ + refresh_token: req.params.token, + }), + }) + .then((oauthResponse) => oauthResponse.json()) + .catch((err) => res.status(500).send(err)); + if (oauth) res.status(200).send(oauth); +}; diff --git a/functions/modules/tokenValidator.js b/functions/modules/tokenValidator.js new file mode 100644 index 0000000..249794d --- /dev/null +++ b/functions/modules/tokenValidator.js @@ -0,0 +1,20 @@ +const VoxeetSecret = require('../VoxeetSecret.json'); +module.exports = async (req, res) => { + const { key, secret } = VoxeetSecret; + const credentials = btoa(encodeURI(key) + ':' + encodeURI(secret)); + const headers = { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache', + Authorization: 'Basic ' + credentials, + }; + const oauth = await fetch(VoxeetSecret.validateURL, { + method: 'POST', + headers, + body: JSON.stringify({ + grant_type: 'client_credentials', + }), + }) + .then((oauthResponse) => oauthResponse.json()) + .catch((err) => res.status(500).send(err)); + if (oauth) res.status(200).send(oauth); +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..262e932 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,181 @@ +{ + "name": "watch-party", + "version": "1.2.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@voxeet/voxeet-web-sdk": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@voxeet/voxeet-web-sdk/-/voxeet-web-sdk-2.3.1.tgz", + "integrity": "sha512-y3/rewt+Rtkk7KVUY9DuYffAY9s+m2nrvumfFuwMnOxAeJw2N4fB1KuyGx80MQqk70xpFfZCLTt3JO6l19uJIg==", + "requires": { + "bowser": "^2.8.1", + "browserslist": "^4.5.2", + "btoa": "^1.1.2", + "events": "^1.1.0", + "get-float-time-domain-data": "^0.1.0", + "global": "^4.4.0", + "lodash": "^4.17.4", + "moment": "^2.24.0", + "sdp": "^2.3.0", + "webrtc-adapter": "^6.3.2" + } + }, + "bowser": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz", + "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" + }, + "browserslist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", + "requires": { + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" + } + }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, + "caniuse-lite": { + "version": "1.0.30001066", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz", + "integrity": "sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw==" + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "electron-to-chromium": { + "version": "1.3.453", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.453.tgz", + "integrity": "sha512-IQbCfjJR0NDDn/+vojTlq7fPSREcALtF8M1n01gw7nQghCtfFYrJ2dfhsp8APr8bANoFC8vRTFVXMOGpT0eetw==" + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "get-float-time-domain-data": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-float-time-domain-data/-/get-float-time-domain-data-0.1.0.tgz", + "integrity": "sha1-XYVZJKQwOITJY4qrEnzOTQBlljs=" + }, + "global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "requires": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "moment": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", + "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==" + }, + "node-releases": { + "version": "1.1.57", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.57.tgz", + "integrity": "sha512-ZQmnWS7adi61A9JsllJ2gdj2PauElcjnOwTp2O011iGzoakTxUsDGSe+6vD7wXbKdqhSFymC0OSx35aAMhrSdw==" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "requires": { + "find-up": "^2.1.0" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "rtcpeerconnection-shim": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz", + "integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==", + "requires": { + "sdp": "^2.6.0" + } + }, + "sdp": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz", + "integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw==" + }, + "webrtc-adapter": { + "version": "6.4.8", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-6.4.8.tgz", + "integrity": "sha512-YM8yl545c/JhYcjGHgaCoA7jRK/KZuMwEDFeP2AcP0Auv5awEd+gZE0hXy9z7Ed3p9HvAXp8jdbe+4ESb1zxAw==", + "requires": { + "rtcpeerconnection-shim": "^1.2.14", + "sdp": "^2.9.0" + } + } + } +} diff --git a/package.json b/package.json index 4fb0355..b12ad18 100644 --- a/package.json +++ b/package.json @@ -6,5 +6,8 @@ "start": "env NODE_ENV=DEV firebase emulators:start", "build": "cd ./app && node ./scripts/build.js" }, - "private": true + "private": true, + "dependencies": { + "@voxeet/voxeet-web-sdk": "^2.3.1" + } }