v1.3.0-beta/UI redone

This commit is contained in:
2020-05-31 00:35:24 -04:00
parent fba32fd7d1
commit aa15c4eb30
21 changed files with 676 additions and 333 deletions
+124 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "watch-party-crx", "name": "watch-party-crx",
"version": "1.2.0", "version": "1.2.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -1914,6 +1914,64 @@
} }
} }
}, },
"@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"
},
"dependencies": {
"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"
}
},
"caniuse-lite": {
"version": "1.0.30001066",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz",
"integrity": "sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw=="
},
"electron-to-chromium": {
"version": "1.3.455",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.455.tgz",
"integrity": "sha512-4lwnxp+ArqOX9hiLwLpwhfqvwzUHFuDgLz4NTiU3lhygUzWtocIJ/5Vix+mWVNE2HQ9aI1k2ncGe5H/0OktMvA=="
},
"events": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
},
"node-releases": {
"version": "1.1.57",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.57.tgz",
"integrity": "sha512-ZQmnWS7adi61A9JsllJ2gdj2PauElcjnOwTp2O011iGzoakTxUsDGSe+6vD7wXbKdqhSFymC0OSx35aAMhrSdw=="
},
"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"
}
}
}
},
"@webassemblyjs/ast": { "@webassemblyjs/ast": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
@@ -3054,6 +3112,11 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
}, },
"bowser": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz",
"integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA=="
},
"boxen": { "boxen": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
@@ -3248,6 +3311,11 @@
"node-int64": "^0.4.0" "node-int64": "^0.4.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=="
},
"buffer": { "buffer": {
"version": "4.9.2", "version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
@@ -4951,6 +5019,11 @@
"urijs": "^1.16.1" "urijs": "^1.16.1"
} }
}, },
"dom-walk": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
},
"domain-browser": { "domain-browser": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@@ -7628,6 +7701,11 @@
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
}, },
"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="
},
"get-stdin": { "get-stdin": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
@@ -7686,6 +7764,15 @@
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz",
"integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs="
}, },
"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"
}
},
"global-dirs": { "global-dirs": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
@@ -10084,6 +10171,14 @@
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
}, },
"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"
}
},
"mini-css-extract-plugin": { "mini-css-extract-plugin": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz",
@@ -10178,6 +10273,11 @@
"minimist": "^1.2.5" "minimist": "^1.2.5"
} }
}, },
"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=="
},
"move-concurrently": { "move-concurrently": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -12375,8 +12475,7 @@
"process": { "process": {
"version": "0.11.10", "version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
"dev": true
}, },
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.1", "version": "2.0.1",
@@ -13662,6 +13761,14 @@
"resolved": "https://registry.npmjs.org/route-lite/-/route-lite-0.3.0.tgz", "resolved": "https://registry.npmjs.org/route-lite/-/route-lite-0.3.0.tgz",
"integrity": "sha512-5A5Mh7gbQfk1P7u0jh0O87vu+/zAyS1xx16YO6ds+4oeLR6cuVtncEA8/d4qevSBv4hijvLloI+IKybtZMUB9g==" "integrity": "sha512-5A5Mh7gbQfk1P7u0jh0O87vu+/zAyS1xx16YO6ds+4oeLR6cuVtncEA8/d4qevSBv4hijvLloI+IKybtZMUB9g=="
}, },
"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"
}
},
"run-async": { "run-async": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
@@ -13760,6 +13867,11 @@
"ajv-keywords": "^3.4.1" "ajv-keywords": "^3.4.1"
} }
}, },
"sdp": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz",
"integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw=="
},
"select-hose": { "select-hose": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -16514,6 +16626,15 @@
"source-map": "~0.6.1" "source-map": "~0.6.1"
} }
}, },
"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"
}
},
"websocket-driver": { "websocket-driver": {
"version": "0.7.3", "version": "0.7.3",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz",
+1
View File
@@ -7,6 +7,7 @@
"@giphy/react-components": "^1.0.4", "@giphy/react-components": "^1.0.4",
"@material-ui/core": "^4.9.10", "@material-ui/core": "^4.9.10",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.9.1",
"@voxeet/voxeet-web-sdk": "^2.3.1",
"autoprefixer": "7.1.6", "autoprefixer": "7.1.6",
"babel-core": "6.26.3", "babel-core": "6.26.3",
"babel-eslint": "10.x", "babel-eslint": "10.x",
+1 -1
View File
@@ -1,5 +1,5 @@
/* global chrome */ /* global chrome */
/*global firebase*/ /* global firebase */
const firebaseConfig = { const firebaseConfig = {
apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU', apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU',
+2 -1
View File
@@ -12,7 +12,8 @@
"https://*.amazon.com/*", "https://*.amazon.com/*",
"https://*.disneyplus.com/*", "https://*.disneyplus.com/*",
"https://*.youtube.com/*", "https://*.youtube.com/*",
"https://play.hbonow.com/*" "https://play.hbonow.com/*",
"http://localhost/*"
], ],
"content_security_policy": "script-src 'self' https://www.gstatic.com/ https://*.firebaseio.com https://www.googleapis.com; object-src 'self'", "content_security_policy": "script-src 'self' https://www.gstatic.com/ https://*.firebaseio.com https://www.googleapis.com; object-src 'self'",
"content_scripts": [ "content_scripts": [
+14 -4
View File
@@ -1,9 +1,9 @@
/*global chrome*/ /*global chrome*/
const API_HOST = window.location.href.startsWith('http://localhost') const API_HOST = window.location.href.startsWith('http://localhost')
? 'http://localhost:5001/watch-party-crx/us-central1/widgets' ? 'http://localhost:5000/watch-party-crx/us-central1/widgets'
: // : 'http://localhost:5001/watch-party-crx/us-central1/widgets'; : // : 'https://us-central1-watch-party-crx.cloudfunctions.net/widgets';
'https://us-central1-watch-party-crx.cloudfunctions.net/widgets'; 'http://localhost:5000/watch-party-crx/us-central1/widgets';
export const PLAYER_STATUS = { export const PLAYER_STATUS = {
PLAYING: 'PLAYING', PLAYING: 'PLAYING',
@@ -32,6 +32,16 @@ export const getPlayerSource = () => {
if (HREF.startsWith(PLAYER_SOURCE.LOCAL)) return PLAYER_SOURCE.LOCAL; if (HREF.startsWith(PLAYER_SOURCE.LOCAL)) return PLAYER_SOURCE.LOCAL;
}; };
export const LocalData = () => {
return JSON.parse(window.localStorage.getItem('WatchParty')) || {};
};
export const updateLocalData = (data) => {
window.localStorage.setItem('WatchParty', JSON.stringify(Object.assign({}, LocalData(), data)));
};
export const emptyLocalData = () => window.localStorage.setItem('WatchParty', undefined);
export const getUserUid = async () => export const getUserUid = async () =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
chrome.runtime.sendMessage( chrome.runtime.sendMessage(
@@ -40,7 +50,7 @@ export const getUserUid = async () =>
}, },
(response) => { (response) => {
const { uid } = response; const { uid } = response;
window.localStorage.setItem('watchPartyUserUid', uid); updateLocalData({ userUid: uid });
resolve(uid); resolve(uid);
} }
); );
+4 -1
View File
@@ -12,7 +12,10 @@ const darkTheme = createMuiTheme({
palette: { palette: {
type: 'dark', type: 'dark',
primary: { primary: {
main: '#a6d4fa', main: '#FF0000',
},
secondary: {
main: '#c0f300',
}, },
}, },
}); });
+1 -1
View File
@@ -1,7 +1,7 @@
import { VoxeetSDK } from '@voxeet/voxeet-web-sdk'; import { VoxeetSDK } from '@voxeet/voxeet-web-sdk';
import { GetVoxeetTokenApi, GetRefreshedVoxeetTokenApi } from './Api'; import { GetVoxeetTokenApi, GetRefreshedVoxeetTokenApi } from './Api';
class CallManager { export default class CallManager {
constructor(listener) { constructor(listener) {
this.listener = listener; this.listener = listener;
this.__refreshToken = null; this.__refreshToken = null;
+137 -37
View File
@@ -7,6 +7,11 @@ import {
IconButton, IconButton,
ButtonGroup, ButtonGroup,
Button, Button,
ExpansionPanel,
ExpansionPanelSummary,
ExpansionPanelDetails,
Card,
CardContent,
} from '@material-ui/core'; } from '@material-ui/core';
import Thread from './Thread'; import Thread from './Thread';
import Send from '@material-ui/icons/Send'; import Send from '@material-ui/icons/Send';
@@ -27,11 +32,20 @@ import {
UpdatePlayerApi, UpdatePlayerApi,
getPlayerSource, getPlayerSource,
PLAYER_SOURCE, PLAYER_SOURCE,
LocalData,
PLAYER_STATUS,
} from './Api'; } from './Api';
import { goTo } from 'route-lite'; import { goTo } from 'route-lite';
import Create from './Create'; import Create from './Create';
import VideoListener from './VideoListener'; import VideoListener from './VideoListener';
import CallManager from './CallManager'; import CallManager from './CallManager';
import {
MoreVert,
FileCopyTwoTone,
PauseCircleFilled,
LinkOutlined,
PlayCircleFilled,
} from '@material-ui/icons';
const giphyFetch = new GiphyFetch('ojJbIMUN6XRMn4mHiFFav3rMCpXUg8sq'); const giphyFetch = new GiphyFetch('ojJbIMUN6XRMn4mHiFFav3rMCpXUg8sq');
const alertMessage = { const alertMessage = {
@@ -51,6 +65,7 @@ export default class Chat extends Component {
gif: false, gif: false,
onCall: false, onCall: false,
muted: false, muted: false,
menuPopover: true,
}; };
this.port = null; this.port = null;
this.gifClick = this.gifClick.bind(this); this.gifClick = this.gifClick.bind(this);
@@ -86,11 +101,11 @@ export default class Chat extends Component {
this.mVideoListener.start(); this.mVideoListener.start();
// CHROME FIREBASE FIRESTORE LISTENER // CHROME FIREBASE FIRESTORE LISTENER
this.port = chrome.runtime.connect({ name: window.localStorage.getItem('watchPartyRoomId') }); this.port = chrome.runtime.connect({ name: LocalData().partyName });
// Initialize listener // Initialize listener
this.port.postMessage({ this.port.postMessage({
type: 'INIT', type: 'INIT',
roomId: window.localStorage.getItem('watchPartyRoomId'), roomId: LocalData().partyName,
}); });
// Listener // Listener
this.port.onMessage.addListener((response) => { this.port.onMessage.addListener((response) => {
@@ -119,15 +134,16 @@ export default class Chat extends Component {
const { status, showName, time, source, element, showChanged } = video; const { status, showName, time, source, element, showChanged } = video;
if (element) { if (element) {
UpdatePlayerApi({ UpdatePlayerApi({
roomId: window.localStorage.getItem('watchPartyRoomId'), roomId: LocalData().partyName,
showChanged, showChanged,
userName: window.localStorage.getItem('watchPartyUserName'), userName: LocalData().userName,
player: { player: {
status, status,
showName, showName,
time, time,
source, source,
updatedBy: window.localStorage.getItem('watchPartyUserUid'), updatedBy: LocalData().userUid,
url: window.location.href,
}, },
}).catch((err) => { }).catch((err) => {
this.props.updateLoading(false); this.props.updateLoading(false);
@@ -142,12 +158,12 @@ export default class Chat extends Component {
this.props.notify('Ending party...'); this.props.notify('Ending party...');
this.props.updateLoading(true); this.props.updateLoading(true);
const data = { const data = {
roomId: window.localStorage.getItem('watchPartyRoomId'), roomId: LocalData().partyName,
}; };
DeleteRoomApi(data) DeleteRoomApi(data)
.then((res) => { .then((res) => {
this.props.notify('Ended party'); this.props.notify('Ended party');
window.localStorage.removeItem('watchPartyRoomId'); // window.localStorage.removeItem('watchPartyRoomId');
goTo(Create, this.props); goTo(Create, this.props);
this.props.updateLoading(false); this.props.updateLoading(false);
}) })
@@ -161,13 +177,13 @@ export default class Chat extends Component {
this.props.notify('Leaving party...'); this.props.notify('Leaving party...');
this.props.updateLoading(true); this.props.updateLoading(true);
const data = { const data = {
roomId: window.localStorage.getItem('watchPartyRoomId'), roomId: LocalData().partyName,
uid: window.localStorage.getItem('watchPartyUserUid'), uid: LocalData().userUid,
}; };
LeaveRoomApi(data) LeaveRoomApi(data)
.then((res) => { .then((res) => {
this.props.notify('Left party'); this.props.notify('Left party');
window.localStorage.removeItem('watchPartyRoomId'); // window.localStorage.removeItem('watchPartyRoomId');
goTo(Create, this.props); goTo(Create, this.props);
this.props.updateLoading(false); this.props.updateLoading(false);
}) })
@@ -179,12 +195,14 @@ export default class Chat extends Component {
} }
shareParty() { shareParty() {
const el = document.createElement('textarea'); const el = document.createElement('textarea');
el.value = window.localStorage.getItem('watchPartyRoomId'); el.value = `Join my party on Watch Party!\nParty Name: ${LocalData().partyName}\nParty Pin: ${
LocalData().partyPin
}\n${window.location.href}\n---\nGet Watch Party at https:\\\\get.chromewatchparty.com`;
document.body.appendChild(el); document.body.appendChild(el);
el.select(); el.select();
document.execCommand('copy'); document.execCommand('copy');
document.body.removeChild(el); document.body.removeChild(el);
this.props.notify('Copied room code'); this.props.notify('Copied party invitation');
} }
sendMessage(message) { sendMessage(message) {
const data = { const data = {
@@ -225,54 +243,136 @@ export default class Chat extends Component {
this.mCallManager.toggleMute(); this.mCallManager.toggleMute();
this.setState({ muted: !this.state.muted }); this.setState({ muted: !this.state.muted });
} }
getPrettyTime(t) {
return t > 3600
? new Date(t * 1000).toISOString().substr(11, 8)
: new Date(t * 1000).toISOString().substr(14, 5);
}
render() { render() {
const { admin, name, messages, newMessage, gif, onCall, muted } = this.state; const { admin, name, messages, newMessage, gif, onCall, muted, player } = this.state;
if (getPlayerSource() === PLAYER_SOURCE.HBONOW && messages[0] !== alertMessage) if (getPlayerSource() === PLAYER_SOURCE.HBONOW && messages[0] !== alertMessage)
messages.unshift(alertMessage); messages.unshift(alertMessage);
return ( return (
<div className="party-chat-container"> <div className="party-chat-container">
<div className="party-chat-header"> <div className="party-chat-header">
<div className="party-chat-header-text"> <ExpansionPanel elevation={0} className="expander">
<Typography className="brand-title" variant="overline" display="block"> <ExpansionPanelSummary
WATCH PARTY expandIcon={
</Typography> <MoreVert
<Typography className="room-title" variant="h4" gutterBottom> alt="Share, leave, and other controls"
{name} aria-label="Share, leave, and other controls"
</Typography> />
</div> }
<ButtonGroup className="party-controls" size="small" color="secondary"> >
<Button onClick={this.shareParty} startIcon={<Share />}> <div className="party-chat-header-text">
<span className="party-control-button-text">Share</span> <Typography
</Button> color="secondary"
<Button onClick={this.leaveParty} startIcon={<Exit />}> className="brand-title"
<span className="party-control-button-text">Leave</span> variant="caption"
</Button> display="block"
{admin && ( >
<Button onClick={this.endParty} startIcon={<Delete />}> WATCH PARTY
<span className="party-control-button-text">End</span> </Typography>
</Button> <Typography color="textPrimary" className="room-title" variant="h4" gutterBottom>
)} {name}
</ButtonGroup> </Typography>
</div>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<TextField
label="Party Details"
value={`Party Name: ${LocalData().partyName}\nParty Pin: ${LocalData().partyPin}\n${
window.location.href
}`}
multiline
rowsMax={2}
variant="outlined"
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={this.shareParty}>
<Share />
</IconButton>
</InputAdornment>
),
}}
/>
<ButtonGroup fullWidth className="party-controls" size="small">
<Button onClick={this.leaveParty} startIcon={<Exit />}>
<Typography variant="caption" className="party-control-button-text">
Leave
</Typography>
</Button>
{admin && (
<Button onClick={this.endParty} startIcon={<Delete />}>
<Typography variant="caption" className="party-control-button-text">
End
</Typography>
</Button>
)}
</ButtonGroup>
</ExpansionPanelDetails>
</ExpansionPanel>
{player ? (
<Card elevation={0} className="media-info-card">
{player.showName && player.status !== PLAYER_STATUS.NOT_PLAYING ? (
<CardContent>
<Typography className="media-info-at" variant="overline">
{player.status === PLAYER_STATUS.PAUSED
? `PAUSED AT ${this.getPrettyTime(player.time)}`
: player.status === PLAYER_STATUS.PLAYING
? `STARTED PLAYING FROM ${this.getPrettyTime(player.time)}`
: `SHOW IS AT ${this.getPrettyTime(player.time)}`}
</Typography>
<div className="media-info-container">
{player.status === PLAYER_STATUS.PAUSED ? (
<PauseCircleFilled className="media-info-icon" />
) : (
<PlayCircleFilled className="media-info-icon" />
)}
<div className="media-info-right">
<Typography className="show-name" variant="subtitle1">
{player.showName}
</Typography>
</div>
</div>
</CardContent>
) : (
<CardContent>
<Typography className="media-info-at" variant="caption">
Nothing playing right now
</Typography>
</CardContent>
)}
{player && player.showName && player.status ? (
<Button className="media-info-link" href={player.url} startIcon={<LinkOutlined />}>
Link to show
</Button>
) : null}
</Card>
) : null}
</div> </div>
<div className="thread-container"> <div className="thread-container">
<Thread messages={messages} /> <Thread messages={messages} />
</div> </div>
<div className="text-phone-panel"> <div className="text-phone-panel">
{gif || newMessage ? null : ( {gif || newMessage ? null : (
<IconButton onClick={this.handlePhoneCall} color={onCall ? 'secondary' : 'primary'}> <IconButton onClick={this.handlePhoneCall} color={!onCall ? 'secondary' : 'primary'}>
{onCall ? <PhoneDisable /> : <PhoneEnable />} {onCall ? <PhoneDisable /> : <PhoneEnable />}
</IconButton> </IconButton>
)} )}
{!gif && !newMessage && onCall && !muted ? ( {!gif && !newMessage && onCall && !muted ? (
<IconButton onClick={this.handleMute} color="secondary"> <IconButton onClick={this.handleMute} color="primary">
<MicOff /> <MicOff />
</IconButton> </IconButton>
) : !gif && !newMessage && onCall && muted ? ( ) : !gif && !newMessage && onCall && muted ? (
<IconButton onClick={this.handleMute} color="primary"> <IconButton onClick={this.handleMute} color="secondary">
<MicOn /> <MicOn />
</IconButton> </IconButton>
) : null} ) : null}
<TextField <TextField
color="secondary"
multiline multiline
rowsMax="3" rowsMax="3"
size="small" size="small"
+188 -180
View File
@@ -1,200 +1,208 @@
import React, { useState } from 'react'; import React, { Component } from 'react';
import { Button, TextField, Typography, Paper, ButtonGroup } from '@material-ui/core';
import { import {
Button, JoinRoomApi,
InputAdornment, CreateRoomApi,
TextField, PLAYER_STATUS,
Typography, getPlayerSource,
IconButton, LocalData,
Paper, updateLocalData,
} from '@material-ui/core'; } from './Api';
import Save from '@material-ui/icons/Save';
import { JoinRoomApi, CreateRoomApi, PLAYER_STATUS, getPlayerSource } from './Api';
import { goTo } from 'route-lite'; import { goTo } from 'route-lite';
import Chat from './Chat'; import Chat from './Chat';
import { ChevronRight } from '@material-ui/icons';
const saveName = (data) => { const CONSTANTS = {
const { name, notify } = data; USER_NAME_LENGTH: 12,
window.localStorage.setItem('watchPartyUserName', name); NAME_LENGTH: 24,
notify('Saved name'); PIN_LENGTH: 9,
}; };
const getName = () => window.localStorage.getItem('watchPartyUserName'); export default class Create extends Component {
constructor(props) {
const saveRoomId = (data) => { super(props);
const { rid, notify } = data; const defaultMode = LocalData().partyName ? 'join' : 'create';
window.localStorage.setItem('watchPartyRoomId', rid); this.state = {
notify('Saved room'); userName: LocalData().userName || '',
}; name: LocalData().partyName || '',
const getRoomId = () => window.localStorage.getItem('watchPartyRoomId'); pin: LocalData().partyPin || '',
mode: defaultMode,
const joinRoom = async (data) => { };
data.props.updateLoading(true); this.createCall = this.createCall.bind(this);
data.props.notify('Joining party...'); this.joinCall = this.joinCall.bind(this);
const apidata = { this.success = this.success.bind(this);
roomId: data.joinName.trim(),
userName: data.name.trim(),
source: getPlayerSource(),
};
const confirmedRoomId = await JoinRoomApi(apidata)
.then((res) => res.json())
.then((res) => res.roomId)
.catch((err) => {
data.props.updateLoading(false);
data.props.notify(err.message || err.statusText);
console.error('Join Room API error', err);
});
if (confirmedRoomId && confirmedRoomId === apidata.roomId) {
saveName({
name: apidata.userName,
notify: data.props.notify,
});
saveRoomId({
rid: confirmedRoomId,
notify: data.props.notify,
});
data.props.notify('Joined party');
goTo(Chat, data.props);
} }
}; partyNameChange(value) {
if (value.length <= CONSTANTS.NAME_LENGTH) this.setState({ name: value });
const createRoom = async (data) => { }
data.props.updateLoading(true); partyPinChange(value) {
data.props.notify('Starting party...'); if (value.length <= CONSTANTS.PIN_LENGTH) this.setState({ pin: value });
}
const apidata = { userNameChange(value) {
roomPin: data.createPin, if (value.length <= CONSTANTS.USER_NAME_LENGTH) this.setState({ userName: value });
roomName: data.createName, }
userName: data.name, createOrJoin() {
player: { const { userName, name, pin, mode } = this.state;
source: getPlayerSource(), if (!userName.replace(/\s+/g, '')) {
status: PLAYER_STATUS.PAUSED, this.props.notify('Missing user name');
time: 0, return;
url: '', }
}, if (!name.replace(/\s+/g, '')) {
}; this.props.notify('Missing party name');
return;
CreateRoomApi(apidata) }
.then((res) => res.json()) if (!pin.replace(/\s+/g, '')) {
.then((res) => { this.props.notify('Missing party pin');
if (res.status <= 299) { return;
saveName({ }
name: data.name, if (userName && name && pin) {
notify: data.props.notify, switch (mode) {
}); case 'create':
saveRoomId({ this.createCall(userName, name, pin);
rid: res.roomId, break;
notify: data.props.notify, case 'join':
}); this.joinCall(userName, name, pin);
data.props.notify('Started party'); break;
goTo(Chat, data.props); default:
} else { break;
data.props.updateLoading(false);
data.props.notify(res.message || res.statusText);
} }
}) } else {
.catch((err) => { this.props.notify('Double check your inputs');
data.props.updateLoading(false); }
data.props.notify(err.message || err.statusText); }
console.error('Create Room API error', err); createCall(userName, partyName, partyPin) {
}); this.props.updateLoading(true);
}; const apidata = {
userName,
const Create = (props) => { partyName,
const [name, setName] = useState(getName() || ''); partyPin,
const [createName, setCreateName] = useState(''); player: {
const [createPin, setCreatePin] = useState(''); source: getPlayerSource(),
const [joinName, setJoinName] = useState(getRoomId() || ''); status: PLAYER_STATUS.PAUSED,
const [joinPin, setJoinPin] = useState(''); time: 0,
const { notify } = props; url: window.location.href,
},
return ( };
<div className="party-create-container"> CreateRoomApi(apidata)
<Typography className="brand-title" variant="h6" display="block" gutterBottom> .then((res) => res.json())
WATCH PARTY .then((createResult) => {
</Typography> if (createResult.status <= 299) {
<div className="party-create-inputs"> this.success(userName, partyName, partyPin);
<TextField } else {
fullWidth this.props.updateLoading(false);
value={name} this.props.notify(createResult.message || createResult.statusText);
onChange={(e) => (e.target.value.length <= 24 ? setName(e.target.value) : null)} }
className="name-field" })
label="Name" .catch((err) => {
variant="filled" console.error('Create Party API Error', err);
helperText={name.length > 0 ? `${name.length}/24` : null} this.props.notify(err.message || 'Something went wrong');
InputProps={{ this.props.updateLoading(false);
endAdornment: ( });
<InputAdornment position="end"> }
<IconButton onClick={() => saveName({ name, notify })}> joinCall(userName, partyName, partyPin) {
<Save /> const apidata = {
</IconButton> userName,
</InputAdornment> partyName,
), partyPin,
}} source: getPlayerSource(),
/> };
<Paper className="join-card" elevation={3}> JoinRoomApi(apidata)
<Typography variant="button" display="block" gutterBottom> .then((res) => res.json())
CREATE PARTY .then((joinResult) => {
</Typography> if (joinResult.status <= 299) {
<TextField this.success(userName, partyName, partyPin);
fullWidth } else {
value={createName} this.props.updateLoading(false);
className="card-field addFocus" this.props.notify(joinResult.message || joinResult.statusText);
onChange={(e) => (e.target.value.length <= 24 ? setCreateName(e.target.value) : null)} }
helperText={createName.length > 0 ? `${createName.length}/24` : null} })
label="Party Name" .catch((err) => {
/> console.error('Join Party API Error', err);
<TextField this.props.notify(err.message || 'Something went wrong');
fullWidth this.props.updateLoading(false);
type="number" });
value={createPin} }
className="card-field addFocus" success(userName, partyName, partyPin) {
onChange={(e) => (e.target.value.length <= 10 ? setCreatePin(e.target.value) : null)} updateLocalData({ userName, partyName, partyPin });
helperText={createPin.length > 0 ? `${createPin.length}/10` : null} this.props.updateLoading(false);
label="Pin Code" goTo(Chat, this.props);
/> }
<Button render() {
onClick={() => createRoom({ name, createName, props, createPin })} const { mode, name, pin, userName } = this.state;
disabled={createName === ''} return (
color="primary" <div className="party-create-container">
fullWidth <Typography
variant="contained" color="secondary"
> className="brand-title"
CREATE variant="h6"
</Button> display="block"
</Paper> gutterBottom
<Paper className="join-card" elevation={3}> >
<Typography variant="button" display="block" gutterBottom> WATCH PARTY
JOIN PARTY </Typography>
</Typography> <Paper className="create-input-card" elevation={3}>
<ButtonGroup className="create-mode-select" size="small" fullWidth>
<Button
id="mode-select-left"
variant={mode === 'create' ? 'contained' : null}
color={mode === 'create' ? 'secondary' : null}
disableElevation={mode === 'create'}
onClick={() => this.setState({ mode: 'create' })}
>
Create
</Button>
<Button
id="mode-select-right"
variant={mode === 'join' ? 'contained' : null}
color={mode === 'join' ? 'secondary' : null}
disableElevation={mode === 'join'}
onClick={() => this.setState({ mode: 'join' })}
>
Join
</Button>
</ButtonGroup>
<TextField <TextField
color="secondary" color="secondary"
fullWidth size="small"
value={joinName} className="create-name"
className="card-field addFocus" label="Name"
onChange={(e) => setJoinName(e.target.value)} variant="outlined"
label="Party Name" value={userName}
onChange={(e) => this.userNameChange(e.target.value)}
helperText={
userName ? `${userName.length}/${CONSTANTS.USER_NAME_LENGTH} Characters` : null
}
/> />
<TextField <TextField
fullWidth color="secondary"
size="small"
className="create-name"
label="Party Name"
variant="outlined"
value={name}
onChange={(e) => this.partyNameChange(e.target.value)}
helperText={name ? `${name.length}/${CONSTANTS.NAME_LENGTH} Characters` : null}
/>
<TextField
color="secondary"
size="small"
className="create-pin"
label="Party Pin"
variant="outlined"
type="number" type="number"
value={joinPin} value={pin}
className="card-field addFocus" onChange={(e) => this.partyPinChange(e.target.value)}
onChange={(e) => setJoinPin(e.target.value)} helperText={pin ? `${pin.length}/${CONSTANTS.PIN_LENGTH} Digits` : null}
helperText="Ask your party leader for this code"
label="Pin Code"
/> />
<Button <Button
onClick={() => joinRoom({ joinName, name, props, joinPin })} size="small"
disabled={joinName === ''}
variant="contained" variant="contained"
color="secondary" color="secondary"
fullWidth onClick={() => this.createOrJoin()}
> >
JOIN <span>{mode === 'create' ? 'Start Party' : 'Join Party'}</span> <ChevronRight />
</Button> </Button>
</Paper> </Paper>
</div> </div>
</div> );
); }
}; }
export default Create;
+74 -30
View File
@@ -129,11 +129,10 @@ body.amazon-prime textarea:focus {
position: fixed; position: fixed;
background-color: rgb(27, 27, 27); background-color: rgb(27, 27, 27);
bottom: 0; bottom: 0;
width: calc(var(--party-width) - 2rem);
right: var(--neg-party-width); right: var(--neg-party-width);
transition: all ease-in-out 0.3s; transition: all ease-in-out 0.3s;
height: calc(100% - 2rem); width: var(--party-width);
padding: 1rem; height: 100%;
color: white; color: white;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -150,7 +149,6 @@ body.amazon-prime textarea:focus {
width: calc(var(--party-width) - 2rem) !important; width: calc(var(--party-width) - 2rem) !important;
} }
.party-notification-container .MuiPaper-root { .party-notification-container .MuiPaper-root {
border-radius: 26px !important;
color: white; color: white;
background-color: black; background-color: black;
} }
@@ -177,14 +175,15 @@ body.amazon-prime textarea:focus {
background-image: url('chrome-extension://__MSG_@@extension_id__/android-icon-192x192.png'); background-image: url('chrome-extension://__MSG_@@extension_id__/android-icon-192x192.png');
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 2rem; background-size: 75%;
animation-name: showbutton; animation-name: showbutton;
animation-iteration-count: 1; animation-iteration-count: 1;
animation-duration: 2s; animation-duration: 2s;
transition: all ease-in-out 0.3s !important; transition: all ease-in-out 0.3s !important;
} }
.watch-party-fab:hover { .watch-party-fab:hover {
right: 1rem !important; right: 0rem !important;
background-size: 90%;
} }
.watch-party-fab .fab-logo { .watch-party-fab .fab-logo {
max-width: 75%; max-width: 75%;
@@ -192,37 +191,24 @@ body.amazon-prime textarea:focus {
} }
.party-create-container { .party-create-container {
padding: 3.5rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
.party-create-container .brand-title { .party-create-container .create-input-card {
padding-top: 1.4rem !important;
padding-left: 3.5rem !important;
}
.party-create-container .name-field {
margin-bottom: 2rem !important;
}
.party-create-container .join-card {
padding: 1rem !important;
margin-bottom: 2rem;
}
.party-create-container .join-card .card-field {
margin-bottom: 1rem !important;
}
.party-create-container .party-create-inputs {
flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center;
padding: 2rem; padding: 2rem;
margin: auto 0;
} }
.party-create-container .create-input-card .create-mode-select,
.party-chat-container { .party-create-container .create-input-card .create-name,
display: flex; .party-create-container .create-input-card .create-pin {
max-height: 100%; margin-bottom: 1rem;
flex-direction: column; }
flex: 1; .party-create-container .create-input-card {
background-color: #2d2d2d;
} }
.thread-container { .thread-container {
@@ -258,11 +244,32 @@ textarea::-webkit-scrollbar {
.chat-message-box > div { .chat-message-box > div {
border-radius: 26px; border-radius: 26px;
} }
.party-chat-container {
display: flex;
max-height: 100%;
flex-direction: column;
flex: 1;
padding: 2rem;
padding-bottom: 1rem;
}
.party-chat-header { .party-chat-header {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
padding-left: 2.25rem; padding-left: 1rem;
flex-direction: column;
} }
.party-chat-header .expander {
flex: 1;
width: 100%;
margin-bottom: 0 !important;
}
.party-chat-header .expander .MuiExpansionPanelDetails-root {
flex-direction: column;
}
.party-chat-header-text { .party-chat-header-text {
flex: 1; flex: 1;
} }
@@ -277,10 +284,47 @@ textarea::-webkit-scrollbar {
} }
.party-controls button:hover .party-control-button-text { .party-controls button:hover .party-control-button-text {
display: initial; display: initial;
font-size: 1rem !important;
} }
.party-controls button:hover .MuiButton-startIcon { .party-controls button:hover .MuiButton-startIcon {
margin-right: 8px !important; margin-right: 8px !important;
} }
.media-info-card {
width: 100%;
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
padding: 0 2rem !important;
padding-bottom: 1rem !important;
}
.media-info-card .MuiCardContent-root {
padding: 0 !important;
}
.media-info-at {
font-size: 1rem !important;
padding-left: 3px !important;
}
.media-info-container {
display: flex;
flex-direction: row;
}
.media-info-icon {
width: 3.5rem !important;
height: 3.5rem !important;
}
.media-info-right {
flex: 1;
padding-left: 1rem;
}
.media-info-right .show-name {
font-size: 1.2rem;
line-height: 1.75rem;
white-space: break-spaces !important;
}
.media-info-link {
color: inherit !important;
}
.giphy-carousel::before { .giphy-carousel::before {
position: relative; position: relative;
bottom: 40%; bottom: 40%;
+5 -1
View File
@@ -6,7 +6,7 @@
"source": "functions" "source": "functions"
}, },
"hosting": { "hosting": {
"public": "./website", "public": "website",
"ignore": [ "ignore": [
"firebase.json", "firebase.json",
"**/.*", "**/.*",
@@ -26,5 +26,9 @@
"ui": { "ui": {
"enabled": true "enabled": true
} }
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
} }
} }
+4
View File
@@ -0,0 +1,4 @@
{
"indexes": [],
"fieldOverrides": []
}
+19
View File
@@ -0,0 +1,19 @@
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function userInRoom(rid) {
return exists(/databases/$(database)/documents/rooms/$(rid)/users/$(request.auth.uid));
}
match /rooms/{rid}/{detail=**} {
allow read: if userInRoom(rid);
allow write: if false;
}
match /server/status {
allow read: if true;
allow write: if false;
}
}
}
+1
View File
@@ -0,0 +1 @@
node_modules/
-4
View File
@@ -8,10 +8,6 @@ const FIREBASE_OPTIONS = {
credential: admin.credential.cert(secret), credential: admin.credential.cert(secret),
databaseURL: 'https://watch-party-crx.firebaseio.com', databaseURL: 'https://watch-party-crx.firebaseio.com',
}; };
if (process.env.NODE_ENV === 'DEV') {
FIREBASE_OPTIONS.databaseURL = 'http://localhost:4000/firestore';
console.info('RUNNING IN DEV ENV');
}
admin.initializeApp(FIREBASE_OPTIONS); admin.initializeApp(FIREBASE_OPTIONS);
+6 -4
View File
@@ -1,3 +1,5 @@
const admin = require('firebase-admin');
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
res.set('Access-Control-Allow-Origin', ['*']); res.set('Access-Control-Allow-Origin', ['*']);
if (req.method === 'OPTIONS') { if (req.method === 'OPTIONS') {
@@ -30,11 +32,11 @@ module.exports = (req, res, next) => {
}) })
.catch((err) => { .catch((err) => {
console.error('Failed Auth - ', req.headers.authorization, '\n', err); console.error('Failed Auth - ', req.headers.authorization, '\n', err);
return res.status(500).send({ message: 'Unable to verify user token', ...err }); return res.status(500).send({ message: 'Unable to verify user token' });
}); });
} catch (err) { } catch (err) {
return res.status(401).send({ message: 'Got no user token', ...err }); console.log(err);
return res.status(401).send({ message: 'Something went wrong when authorizing' });
} }
} } else return res.status(401).send({ message: 'Got no user token' });
return res.status(401).send({ message: 'Got no user token' });
}; };
+11 -9
View File
@@ -4,20 +4,22 @@ module.exports = async (req, res) => {
const bodydata = JSON.parse(req.body); const bodydata = JSON.parse(req.body);
console.log('Requested Create - UID - ', bodydata.uid); console.log('Requested Create - UID - ', bodydata.uid);
if (!bodydata.userName) res.status(400).send({ message: 'Missing user name' }); if (!bodydata.userName) res.status(400).send({ message: 'Missing user name' });
if (!bodydata.roomName) res.status(400).send({ message: 'Missing party name' }); if (!bodydata.partyName) res.status(400).send({ message: 'Missing party name' });
if (!bodydata.partyPin) res.status(400).send({ message: 'Missing party pin' });
const roomNameTaken = await admin const roomNameTaken = await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomName) .doc(bodydata.partyName)
.get() .get()
.then((docSnap) => docSnap.exists); .then((docSnap) => docSnap.exists);
if (roomNameTaken) return res.status(409).send({ message: 'That party name is already taken' }); if (roomNameTaken) return res.status(409).send({ message: 'That party name is already taken' });
await admin await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomName) .doc(bodydata.partyName)
.set({ .set({
name: bodydata.roomName, name: bodydata.partyName,
pin: bodydata.partyPin,
player: bodydata.player, player: bodydata.player,
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
@@ -29,7 +31,7 @@ module.exports = async (req, res) => {
await admin await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomName) .doc(bodydata.partyName)
.collection('messages') .collection('messages')
.add({ .add({
uid: '<system>', uid: '<system>',
@@ -39,14 +41,14 @@ module.exports = async (req, res) => {
}) })
.catch((err) => { .catch((err) => {
console.error( console.error(
`Failed to add system create party message : \n\t Room: ${bodydata.roomName}`, `Failed to add system create party message : \n\t Room: ${bodydata.partyName}`,
err err
); );
}); });
await admin await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomName) .doc(bodydata.partyName)
.collection('users') .collection('users')
.doc(bodydata.uid) .doc(bodydata.uid)
.set({ .set({
@@ -55,9 +57,9 @@ module.exports = async (req, res) => {
}) })
.catch((err) => { .catch((err) => {
console.error( console.error(
`Failed to set admin when creating party : \n\t Room: ${bodydata.roomName} \n\t User: ${bodydata.uid}`, `Failed to set admin when creating party : \n\t Room: ${bodydata.partyName} \n\t User: ${bodydata.uid}`,
err err
); );
}); });
res.status(201).send({ roomId: bodydata.roomName, message: 'Started a party!' }); res.status(201).send({ roomId: bodydata.partyName, message: 'Started a party!' });
}; };
+52 -25
View File
@@ -2,46 +2,72 @@ const admin = require('firebase-admin');
module.exports = async (req, res) => { module.exports = async (req, res) => {
const bodydata = JSON.parse(req.body); const bodydata = JSON.parse(req.body);
console.log('Requested Join - UID - ', bodydata.uid, '\n\tRoom - ', bodydata.roomId); console.log('Requested Join - UID - ', bodydata.uid, '\n\tRoom - ', bodydata.partyName);
const [exists, roomSource] = await admin // GET ROOM EXISTS, ROOM SOURCE, & ROOM PIN
const [exists, roomSource, roomPin] = await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomId) .doc(bodydata.partyName)
.get() .get()
.then((roomSnap) => [roomSnap.exists, roomSnap.exists ? roomSnap.data().player.source : '']) .then((roomSnap) => [
roomSnap.exists,
roomSnap.exists ? roomSnap.data().player.source : '',
roomSnap.exists ? roomSnap.data().pin : '',
])
.catch((err) => { .catch((err) => {
console.error( console.error(
`Failed to get room when joining room : \n\t Room: ${bodydata.roomId} \n\t User: ${bodydata.uid}`, `Failed to get room when joining room : \n\t Room: ${bodydata.partyName} \n\t User: ${bodydata.uid}`,
err err
); );
}); });
const [userExists, userAdmin] = await admin if (exists && roomPin !== bodydata.partyPin)
return res.status(401).send({ message: 'Incorrect party pin' });
// GET IF USER EXISTS AND IS ADMIN
const [userExists, userAdmin, userName] = await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomId) .doc(bodydata.partyName)
.collection('users') .collection('users')
.doc(bodydata.uid) .doc(bodydata.uid)
.get() .get()
.then((userSnap) => [userSnap.exists, userSnap.exists ? userSnap.data().admin : false]) .then((userSnap) => [
userSnap.exists,
userSnap.exists ? userSnap.data().admin : false,
userSnap.exists ? userSnap.data().name : '',
])
.catch((err) => { .catch((err) => {
console.error( console.error(
`Failed to get user when joining room : \n\t Room: ${bodydata.roomId} \n\t User: ${bodydata.uid}`, `Failed to get user when joining room : \n\t Room: ${bodydata.partyName} \n\t User: ${bodydata.uid}`,
err err
); );
}); });
if (userExists && exists && roomSource === bodydata.source) // IF USER PRE-EXISTS AND SAME SOURCE - JOIN
res.status(200).send({ roomId: bodydata.roomId, message: 'Joined a party' }); if (userExists && exists && roomSource === bodydata.source) {
// UPDATE NAME IF NEEDED
if (userName !== bodydata.userName) {
await admin
.firestore()
.collection('rooms')
.doc(bodydata.partyName)
.collection('users')
.doc(body.uid)
.update({ name: bodydata.userName });
}
res.status(200).send({ partyName: bodydata.partyName, message: 'Joined a party' });
}
// USER DOES NOT PRE-EXIST BUT SAME SOURCE AND RIGHT PIN
else if ( else if (
!userExists && !userExists &&
exists && exists &&
bodydata.roomId && bodydata.partyName &&
bodydata.userName && bodydata.userName &&
roomSource === bodydata.source roomSource === bodydata.source &&
roomPin === bodydata.partyPin
) { ) {
await admin await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomId) .doc(bodydata.partyName)
.collection('messages') .collection('messages')
.add({ .add({
uid: '<system>', uid: '<system>',
@@ -51,14 +77,14 @@ module.exports = async (req, res) => {
}) })
.catch((err) => { .catch((err) => {
console.error( console.error(
`Failed to add system join party message : \n\t Room: ${roomId} \n\t ${bodydata.uid}`, `Failed to add system join party message : \n\t Room: ${partyName} \n\t ${bodydata.uid}`,
err err
); );
}); });
await admin await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomId) .doc(bodydata.partyName)
.collection('users') .collection('users')
.doc(bodydata.uid) .doc(bodydata.uid)
.set({ .set({
@@ -67,36 +93,37 @@ module.exports = async (req, res) => {
}) })
.catch((err) => { .catch((err) => {
console.error( console.error(
`Failed to set admin when joining party : \n\t Room: ${roomId} \n\t User: ${bodydata.uid}`, `Failed to set admin when joining party : \n\t Room: ${partyName} \n\t User: ${bodydata.uid}`,
err err
); );
}); });
await admin await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomId) .doc(bodydata.partyName)
.update({ .update({
updatedAt: new Date(), updatedAt: new Date(),
}) })
.catch((err) => { .catch((err) => {
console.error( console.error(
`Failed to set updated time when joining party : \n\t Room: ${roomId} \n\t User: ${bodydata.uid}`, `Failed to set updated time when joining party : \n\t Room: ${partyName} \n\t User: ${bodydata.uid}`,
err err
); );
}); });
res.status(200).send({ roomId: bodydata.roomId, message: 'Joined a party' }); res.status(200).send({ partyName: bodydata.partyName, message: 'Joined a party' });
} else if (!userExists && !bodydata.userName) }
res.status(400).send({ message: 'Missing user name' }); // ONLY WRONG SOURCE
else if (exists && roomSource !== bodydata.source) { else if (exists && roomSource !== bodydata.source) {
// MOVE SOURCE IF ADMIN
if (userAdmin === true) { if (userAdmin === true) {
await admin await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
.doc(bodydata.roomId) .doc(bodydata.partyName)
.update({ .update({
player: { player: {
showName: null, showName: null,
source: roomSource, source: null,
status: 'NOT_PLAYING', status: 'NOT_PLAYING',
time: 0, time: 0,
updatedBy: bodydata.uid, updatedBy: bodydata.uid,
@@ -104,7 +131,7 @@ module.exports = async (req, res) => {
}) })
.catch(() => { .catch(() => {
console.error( console.error(
`Failed to update player source when admin joining party \n\t Room: ${roomId} \n\t User: ${bodydata.uid}` `Failed to update player source when admin joining party \n\t Room: ${partyName} \n\t User: ${bodydata.uid}`
); );
res.status(500).send({ message: 'Unable to move party, please try again' }); res.status(500).send({ message: 'Unable to move party, please try again' });
}); });
+29 -29
View File
@@ -2,18 +2,18 @@ const admin = require('firebase-admin');
module.exports = async (req, res) => { module.exports = async (req, res) => {
const { player, roomId, uid, showChanged, userName } = JSON.parse(req.body); const { player, roomId, uid, showChanged, userName } = JSON.parse(req.body);
const statusString = // const statusString =
player.status === 'PLAYING' // player.status === 'PLAYING'
? 'started playing from' // ? 'started playing from'
: player.status === 'PAUSED' // : player.status === 'PAUSED'
? 'paused at' // ? 'paused at'
: player.status === 'NOT_PLAYING' // : player.status === 'NOT_PLAYING'
? 'stopped at' // ? 'stopped at'
: 'is at'; // : 'is at';
const timeString = // const timeString =
player.time > 3600 // player.time > 3600
? new Date(player.time * 1000).toISOString().substr(11, 8) // ? new Date(player.time * 1000).toISOString().substr(11, 8)
: new Date(player.time * 1000).toISOString().substr(14, 5); // : new Date(player.time * 1000).toISOString().substr(14, 5);
const [roomExists, roomData] = await admin const [roomExists, roomData] = await admin
.firestore() .firestore()
.collection('rooms') .collection('rooms')
@@ -55,23 +55,23 @@ module.exports = async (req, res) => {
console.error(`Failed to update player : \n\t Room: ${roomId} \n\t ${uid}`, err); console.error(`Failed to update player : \n\t Room: ${roomId} \n\t ${uid}`, err);
res.status(500).send({ message: "Uh oh party could't be synced", err }); res.status(500).send({ message: "Uh oh party could't be synced", err });
}); });
await admin // await admin
.firestore() // .firestore()
.collection('rooms') // .collection('rooms')
.doc(roomId) // .doc(roomId)
.collection('messages') // .collection('messages')
.add({ // .add({
uid: '<system>', // uid: '<system>',
name: '', // name: '',
text: `${userName} ${statusString} ${timeString}`, // text: `${userName} ${statusString} ${timeString}`,
time: new Date(), // time: new Date(),
}) // })
.catch((err) => { // .catch((err) => {
console.error( // console.error(
`Failed to add system update player message : \n\t Room: ${roomId} \n\t ${uid}`, // `Failed to add system update player message : \n\t Room: ${roomId} \n\t ${uid}`,
err // err
); // );
}); // });
return res.status(200).send({ message: 'Synced party player' }); return res.status(200).send({ message: 'Synced party player' });
} else { } else {
return res.status(208).send({ message: 'Player already up to date' }); return res.status(208).send({ message: 'Player already up to date' });
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "watchparty", "name": "watch-party-api",
"version": "1.0.0-beta", "version": "1.2.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
+1 -1
View File
@@ -3,7 +3,7 @@
"version": "1.2.1", "version": "1.2.1",
"description": "Watch Party", "description": "Watch Party",
"scripts": { "scripts": {
"start": "env NODE_ENV=DEV firebase emulators:start", "start": "firebase emulators:start",
"build": "cd ./app && node ./scripts/build.js" "build": "cd ./app && node ./scripts/build.js"
}, },
"private": true, "private": true,