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"
+ }
}