This commit is contained in:
Ayushya Amitabh
2020-04-17 14:37:39 -04:00
parent 1acbca3bf0
commit 15679cd001
12 changed files with 439 additions and 190 deletions
+27
View File
@@ -48,6 +48,7 @@ const createRoom = async (req, res) => {
.collection('rooms') .collection('rooms')
.add({ .add({
name: bodydata.roomName, name: bodydata.roomName,
player: bodydata.player,
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
}) })
@@ -206,6 +207,31 @@ const deleteRoom = async (req, res) => {
res.status(200).send(); res.status(200).send();
}; };
const updatePlayer = async (req, res) => {
const { player, roomId, uid } = JSON.parse(req.body);
const userExists = await admin
.firestore()
.collection('rooms')
.doc(roomId)
.collection('users')
.doc(uid)
.get()
.then((userSnap) => userSnap.exists);
if (userExists) {
await admin
.firestore()
.collection('rooms')
.doc(roomId)
.update({
player,
})
.catch((err) => res.status(500).send(err));
res.status(200).send();
} else {
res.status(401).send('Not in room to sync');
}
};
const app = express(); const app = express();
app.use(cors({ origin: true })); app.use(cors({ origin: true }));
app.use(authenticator); app.use(authenticator);
@@ -220,5 +246,6 @@ app.post('/join', joinRoom);
app.post('/message', sendMessage); app.post('/message', sendMessage);
app.delete('/leave/:rid/users/:uid', leaveRoom); app.delete('/leave/:rid/users/:uid', leaveRoom);
app.delete('/delete/:rid', deleteRoom); app.delete('/delete/:rid', deleteRoom);
app.post('/updatePlayer', updatePlayer);
exports.widgets = functions.https.onRequest(app); exports.widgets = functions.https.onRequest(app);
+32 -1
View File
@@ -5,6 +5,27 @@ const API_HOST = window.location.href.startsWith('http://localhost')
? 'http://localhost:5001/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';
export const PLAYER_STATUS = {
PLAYING: 'PLAYING',
PAUSED: 'PAUSED',
NOT_PLAYING: 'NOT_PLAYING',
};
export const PLAYER_SOURCE = {
NETFLIX: 'https://www.netflix.',
AMAZON: 'https://www.amazon.',
HULU: 'https://www.hulu.',
LOCAL: 'http://localhost',
};
export const getPlayerSource = () => {
const HREF = window.location.href;
if (HREF.startsWith(PLAYER_SOURCE.NETFLIX)) return PLAYER_SOURCE.NETFLIX;
if (HREF.startsWith(PLAYER_SOURCE.HULU)) return PLAYER_SOURCE.HULU;
if (HREF.startsWith(PLAYER_SOURCE.AMAZON)) return PLAYER_SOURCE.AMAZON;
if (HREF.startsWith(PLAYER_SOURCE.LOCAL)) return PLAYER_SOURCE.LOCAL;
};
export const isAuthed = () => true && firebase.auth().currentUser; export const isAuthed = () => true && firebase.auth().currentUser;
export const doAuth = () => firebase.auth().signInAnonymously(); export const doAuth = () => firebase.auth().signInAnonymously();
@@ -42,7 +63,7 @@ export const SendMessageApi = async (data) => {
if (!isAuthed()) await doAuth(); if (!isAuthed()) await doAuth();
return fetcher({ return fetcher({
ENDPOINT: 'message', ENDPOINT: 'message',
METHOD: 'post', METHOD: 'POST',
body: JSON.stringify(data), body: JSON.stringify(data),
}); });
}; };
@@ -62,3 +83,13 @@ export const DeleteRoomApi = async (data) => {
METHOD: 'DELETE', METHOD: 'DELETE',
}); });
}; };
export const UpdatePlayerApi = async (data) => {
if (isAuthed()) {
return fetcher({
ENDPOINT: 'updatePlayer',
METHOD: 'POST',
body: JSON.stringify(data),
});
}
};
+45 -4
View File
@@ -1,7 +1,11 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import Router from 'route-lite'; import Router from 'route-lite';
import { Fab, createMuiTheme, ThemeProvider } from '@material-ui/core'; import { Fab, createMuiTheme, ThemeProvider, Snackbar } from '@material-ui/core';
import Create from './Create'; import Create from './Create';
import Loading from './Loading';
import firebase from 'firebase/app';
import 'firebase/firestore';
import Inactive from './Inactive';
const darkTheme = createMuiTheme({ const darkTheme = createMuiTheme({
palette: { palette: {
@@ -14,12 +18,31 @@ const darkTheme = createMuiTheme({
const Routes = (props) => ( const Routes = (props) => (
<Router> <Router>
<Create /> <Create {...props} />
</Router> </Router>
); );
const App = () => { const App = () => {
const closedNotification = { open: false, message: '' };
const [status, setStatus] = useState({ active: false, message: '' });
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const [notification, setNotification] = useState(closedNotification);
useEffect(() => {
setLoading(true);
firebase
.firestore()
.collection('server')
.doc('status')
.onSnapshot((statusSnap) => {
setStatus(statusSnap.data());
setLoading(false);
});
}, []);
// HANDLE VISIBILITY STYLING
const PartyRoutesWrapper = document.getElementById('party-routes-wrapper'); const PartyRoutesWrapper = document.getElementById('party-routes-wrapper');
if (visible) { if (visible) {
document.body.classList.add('visible'); document.body.classList.add('visible');
@@ -28,11 +51,29 @@ const App = () => {
document.body.classList.remove('visible'); document.body.classList.remove('visible');
PartyRoutesWrapper && PartyRoutesWrapper.classList.remove('visible'); PartyRoutesWrapper && PartyRoutesWrapper.classList.remove('visible');
} }
return ( return (
<ThemeProvider theme={darkTheme}> <ThemeProvider theme={darkTheme}>
<Snackbar
className="party-notification-container"
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
open={notification.open}
onClose={() => setNotification(closedNotification)}
message={notification.message}
resumeHideDuration={500}
autoHideDuration={2000}
/>
<div className="party-content-wrapper"> <div className="party-content-wrapper">
<div id="party-routes-wrapper"> <div id="party-routes-wrapper">
<Routes /> {loading && <Loading />}
{status.active ? (
<Routes
updateLoading={setLoading}
notify={(message) => setNotification({ open: true, message })}
/>
) : (
<Inactive status={status} />
)}
</div> </div>
<Fab onClick={() => setVisible(!visible)} className="watch-party-fab"> <Fab onClick={() => setVisible(!visible)} className="watch-party-fab">
<span /> <span />
+39 -4
View File
@@ -16,9 +16,10 @@ import Sync from '@material-ui/icons/SyncOutlined';
import Exit from '@material-ui/icons/ExitToAppRounded'; import Exit from '@material-ui/icons/ExitToAppRounded';
import Share from '@material-ui/icons/ShareRounded'; import Share from '@material-ui/icons/ShareRounded';
import Delete from '@material-ui/icons/Delete'; import Delete from '@material-ui/icons/Delete';
import { SendMessageApi, LeaveRoomApi, DeleteRoomApi } from './Api'; import { SendMessageApi, LeaveRoomApi, DeleteRoomApi, UpdatePlayerApi } from './Api';
import { goTo } from 'route-lite'; import { goTo } from 'route-lite';
import Create from './Create'; import Create from './Create';
import VideoListener from './VideoListener';
export default class Chat extends Component { export default class Chat extends Component {
constructor(props) { constructor(props) {
@@ -28,8 +29,15 @@ export default class Chat extends Component {
messages: [], messages: [],
newMessage: '', newMessage: '',
admin: false, admin: false,
player: {},
localPlayer: {},
}; };
this.sendMessage = this.sendMessage.bind(this); this.sendMessage = this.sendMessage.bind(this);
this.endParty = this.endParty.bind(this);
this.leaveParty = this.leaveParty.bind(this);
this.shareParty = this.shareParty.bind(this);
this.videoEventListener = this.videoEventListener.bind(this);
this.mVideoListener = new VideoListener(this.videoEventListener);
} }
componentDidMount() { componentDidMount() {
window.addEventListener('keyup', (ev) => { window.addEventListener('keyup', (ev) => {
@@ -49,9 +57,12 @@ export default class Chat extends Component {
.doc(window.localStorage.getItem('watchPartyRoomId')) .doc(window.localStorage.getItem('watchPartyRoomId'))
.onSnapshot( .onSnapshot(
(snap) => { (snap) => {
if (snap.data().player.status !== this.state.localPlayer.status) {
this.mVideoListener.doUpdate(snap.data().player);
}
this.setState(snap.data()); this.setState(snap.data());
}, },
(err) => console.error(err) (err) => {}
); );
firebase firebase
.firestore() .firestore()
@@ -75,27 +86,50 @@ export default class Chat extends Component {
const pcc = document.getElementsByClassName('thread-container')[0]; const pcc = document.getElementsByClassName('thread-container')[0];
if (pcc) pcc.scrollTop = pcc.scrollHeight; if (pcc) pcc.scrollTop = pcc.scrollHeight;
}); });
this.mVideoListener.start();
this.props.updateLoading(false);
}
videoEventListener(video) {
const { status, url, time, source } = video;
if (this.state.player.status !== status) {
UpdatePlayerApi({
roomId: window.localStorage.getItem('watchPartyRoomId'),
player: {
status,
url,
time,
source,
},
});
}
this.setState({ localPlayer: video });
} }
endParty() { endParty() {
this.props.notify('Ending party...');
this.props.updateLoading(true);
const data = { const data = {
roomId: window.localStorage.getItem('watchPartyRoomId'), roomId: window.localStorage.getItem('watchPartyRoomId'),
}; };
DeleteRoomApi(data).then((res) => { DeleteRoomApi(data).then((res) => {
if (res.status === 200) { if (res.status === 200) {
this.props.notify('Ended party');
window.localStorage.removeItem('watchPartyRoomId'); window.localStorage.removeItem('watchPartyRoomId');
goTo(Create); goTo(Create, this.props);
} }
}); });
} }
leaveParty() { leaveParty() {
this.props.notify('Leaving party...');
this.props.updateLoading(true);
const data = { const data = {
roomId: window.localStorage.getItem('watchPartyRoomId'), roomId: window.localStorage.getItem('watchPartyRoomId'),
uid: firebase.auth().currentUser.uid, uid: firebase.auth().currentUser.uid,
}; };
LeaveRoomApi(data).then((res) => { LeaveRoomApi(data).then((res) => {
if (res.status === 200) { if (res.status === 200) {
this.props.notify('Left party');
window.localStorage.removeItem('watchPartyRoomId'); window.localStorage.removeItem('watchPartyRoomId');
goTo(Create); goTo(Create, this.props);
} }
}); });
} }
@@ -106,6 +140,7 @@ export default class Chat extends Component {
el.select(); el.select();
document.execCommand('copy'); document.execCommand('copy');
document.body.removeChild(el); document.body.removeChild(el);
this.props.notify('Copied room code');
} }
sendMessage(message) { sendMessage(message) {
const data = { const data = {
+72 -19
View File
@@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { import {
Button, Button,
InputAdornment, InputAdornment,
@@ -8,17 +8,27 @@ import {
Paper, Paper,
} from '@material-ui/core'; } from '@material-ui/core';
import Save from '@material-ui/icons/Save'; import Save from '@material-ui/icons/Save';
import { JoinRoomApi, CreateRoomApi } from './Api'; 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';
const saveName = (name) => window.localStorage.setItem('watchPartyUserName', name); const saveName = (data) => {
const { name, notify } = data;
window.localStorage.setItem('watchPartyUserName', name);
notify('Saved name');
};
const getName = () => window.localStorage.getItem('watchPartyUserName'); const getName = () => window.localStorage.getItem('watchPartyUserName');
const saveRoomId = (rid) => window.localStorage.setItem('watchPartyRoomId', rid); const saveRoomId = (data) => {
const { rid, notify } = data;
window.localStorage.setItem('watchPartyRoomId', rid);
notify('Saved room');
};
const getRoomId = () => window.localStorage.getItem('watchPartyRoomId'); const getRoomId = () => window.localStorage.getItem('watchPartyRoomId');
const joinRoom = async (data) => { const joinRoom = async (data) => {
data.props.updateLoading(true);
data.props.notify('Joining party...');
const apidata = { const apidata = {
roomId: data.joinName.trim(), roomId: data.joinName.trim(),
userName: data.name.trim(), userName: data.name.trim(),
@@ -26,25 +36,58 @@ const joinRoom = async (data) => {
const confirmedRoomId = await JoinRoomApi(apidata) const confirmedRoomId = await JoinRoomApi(apidata)
.then((res) => res.json()) .then((res) => res.json())
.then((res) => res.roomId) .then((res) => res.roomId)
.catch((err) => console.error('Join Room API error', err)); .catch((err) => {
data.props.updateLoading(false);
data.props.notify('Something went wrong');
console.error('Join Room API error', err);
});
if (confirmedRoomId === apidata.roomId) { if (confirmedRoomId === apidata.roomId) {
saveRoomId(confirmedRoomId); saveName({
saveName(apidata.userName); name: apidata.userName,
goTo(Chat); notify: data.props.notify,
});
saveRoomId({
rid: confirmedRoomId,
notify: data.props.notify,
});
data.props.notify('Joined party');
goTo(Chat, data.props);
} }
}; };
const createRoom = async (data) => { const createRoom = async (data) => {
data.props.updateLoading(true);
data.props.notify('Starting party...');
const apidata = { const apidata = {
roomName: data.createName, roomName: data.createName,
userName: data.name, userName: data.name,
player: {
source: getPlayerSource(),
status: PLAYER_STATUS.NOT_PLAYING,
time: 0,
url: '',
},
}; };
CreateRoomApi(apidata) CreateRoomApi(apidata)
.then((res) => res.json()) .then((res) => res.json())
.then((res) => { .then((res) => {
saveRoomId(res.roomId); saveName({
saveName(data.name); name: data.name,
goTo(Chat); notify: data.props.notify,
});
saveRoomId({
rid: res.roomId,
notify: data.props.notify,
});
data.props.notify('Started party');
goTo(Chat, data.props);
})
.catch((err) => {
data.props.updateLoading(false);
data.props.notify('Something went wrong');
console.error('Join Room API error', err);
}); });
}; };
@@ -52,11 +95,21 @@ const Create = (props) => {
const [name, setName] = useState(getName() || ''); const [name, setName] = useState(getName() || '');
const [createName, setCreateName] = useState(''); const [createName, setCreateName] = useState('');
const [joinName, setJoinName] = useState(getRoomId() || ''); const [joinName, setJoinName] = useState(getRoomId() || '');
if (getName() && getRoomId()) const { notify, updateLoading } = props;
joinRoom({
joinName: getRoomId(), const joinOnMount = () => {
name: getName(), updateLoading(false);
}); // NEEDS FIX - REPETITIVE API CALLS
// if (getName() && getRoomId()) {
// joinRoom({
// joinName,
// name,
// props,
// });
// }
};
useEffect(joinOnMount, []);
return ( return (
<div className="party-create-container"> <div className="party-create-container">
<Typography className="brand-title" variant="h6" display="block" gutterBottom> <Typography className="brand-title" variant="h6" display="block" gutterBottom>
@@ -73,7 +126,7 @@ const Create = (props) => {
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<InputAdornment position="end"> <InputAdornment position="end">
<IconButton onClick={() => saveName(name)}> <IconButton onClick={() => saveName({ name, notify })}>
<Save /> <Save />
</IconButton> </IconButton>
</InputAdornment> </InputAdornment>
@@ -92,7 +145,7 @@ const Create = (props) => {
label="Party Name" label="Party Name"
/> />
<Button <Button
onClick={() => createRoom({ name, createName })} onClick={() => createRoom({ name, createName, props })}
disabled={createName === ''} disabled={createName === ''}
color="primary" color="primary"
fullWidth fullWidth
@@ -114,7 +167,7 @@ const Create = (props) => {
label="Party Name" label="Party Name"
/> />
<Button <Button
onClick={() => joinRoom({ joinName, name })} onClick={() => joinRoom({ joinName, name, props })}
disabled={joinName === ''} disabled={joinName === ''}
variant="contained" variant="contained"
color="secondary" color="secondary"
+13
View File
@@ -0,0 +1,13 @@
import React from 'react';
import { Typography } from '@material-ui/core';
import { Warning } from '@material-ui/icons';
const Inactive = (props) => (
<div id="party-loading" className="inactive">
<Warning />
<Typography variant="overline">PARTY IS PAUSED FOR A LITTLE CLEANUP</Typography>
<Typography variant="caption">{props.status.message}</Typography>
</div>
);
export default Inactive;
+4 -4
View File
@@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import { LoadingOutlined } from '@ant-design/icons'; import { CircularProgress } from '@material-ui/core';
const Loading = () => ( const Loading = () => (
<div id="loading_container"> <div id="party-loading">
<LoadingOutlined /> <CircularProgress />
</div> </div>
); );
export default Loading; export default Loading;
+133
View File
@@ -0,0 +1,133 @@
import { PLAYER_STATUS, getPlayerSource, PLAYER_SOURCE } from './Api';
const templateListener = (video) => console.log(video);
export default class VideoListener {
constructor(listener = templateListener) {
this.video = {
status: PLAYER_STATUS.NOT_PLAYING,
time: 0,
url: '',
source: '',
element: null,
};
this.listener = listener;
this.updateStatus = this.updateStatus.bind(this);
this.firstTimeSync = this.firstTimeSync.bind(this);
this.doUpdate = this.doUpdate.bind(this);
this.start = this.start.bind(this);
}
updateStatus(newStatus) {
this.video = Object.assign({}, this.video, newStatus);
this.listener(this.video);
}
doUpdate(serverPlayer) {
if (this.video.element) this.video.element.currentTime = serverPlayer.time;
switch (serverPlayer.status) {
case PLAYER_STATUS.PAUSED:
if (this.video.element) this.video.element.pause();
break;
case PLAYER_STATUS.NOT_PLAYING:
if (this.video.element) this.video.element.pause();
break;
case PLAYER_STATUS.PLAYING:
if (this.video.element) this.video.element.play();
break;
default:
break;
}
}
firstTimeSync(videoElement) {
this.updateStatus({
element: videoElement,
});
if (videoElement.paused) {
this.updateStatus({
time: videoElement.currentTime,
status: PLAYER_STATUS.PAUSED,
});
}
if (videoElement.currentTime > 0 && !videoElement.paused) {
this.updateStatus({
time: videoElement.currentTime,
status: PLAYER_STATUS.PLAYING,
});
}
videoElement.onplaying = (ev) => {
this.updateStatus({ status: PLAYER_STATUS.PLAYING, time: ev.timeStamp });
if (this.video.url === '') this.updateStatus({ url: window.location.href });
};
videoElement.onpause = (ev) =>
this.updateStatus({ status: PLAYER_STATUS.PAUSED, time: ev.timeStamp });
videoElement.onended = (ev) => {
this.updateStatus({ status: PLAYER_STATUS.NOT_PLAYING, url: '', time: 0 });
};
}
start() {
this.updateStatus({ source: getPlayerSource(), url: window.location.href });
switch (this.video.source) {
case PLAYER_SOURCE.NETFLIX:
if (document.getElementsByClassName('AkiraPlayer')[0]) {
const videoElement = document
.getElementsByClassName('AkiraPlayer')[0]
.getElementsByTagName('video')[0];
if (videoElement) {
this.firstTimeSync(videoElement);
}
} else {
document.body.addEventListener('DOMNodeInserted', (ev) => {
if (ev.target.tagName === 'VIDEO') {
document.body.removeEventListener('DOMNodeInserted');
this.start();
}
});
}
break;
case PLAYER_SOURCE.HULU:
const videoElement = document.getElementsByClassName('video-player')[0];
if (videoElement) {
this.firstTimeSync(videoElement);
} else {
document.body.addEventListener('DOMNodeInserted', (ev) => {
if (ev.target.tagName === 'VIDEO') {
document.body.removeEventListener('DOMNodeInserted');
this.start();
}
});
}
break;
case PLAYER_SOURCE.AMAZON:
if (document.getElementsByClassName('webPlayerSDKContainer')[0]) {
const videoElement = document
.getElementsByClassName('webPlayerSDKContainer')[0]
.getElementsByTagName('video')[0];
if (videoElement) {
this.firstTimeSync(videoElement);
}
} else {
document.body.addEventListener('DOMNodeInserted', (ev) => {
if (ev.target.tagName === 'VIDEO') {
document.body.removeEventListener('DOMNodeInserted');
this.start();
}
});
}
break;
case PLAYER_SOURCE.LOCAL:
const localVideoElement = document.getElementsByTagName('video')[0];
if (localVideoElement) {
this.firstTimeSync(localVideoElement);
} else {
document.body.addEventListener('DOMNodeInserted', (ev) => {
if (ev.target.tagName === 'VIDEO') {
document.body.removeEventListener('DOMNodeInserted');
this.start();
}
});
}
break;
default:
break;
}
}
}
-38
View File
@@ -1,38 +0,0 @@
const DATA_MODEL = {
users: {
'<someuid>': {
uid: '<someuid>',
rooms: ['<someroomid>'],
},
'<someuid2>': {
uid: '<someuid2>',
rooms: ['<someroomid>'],
},
},
rooms: {
'<someroomid>': {
player: {
source: 'hulu|netlix|amazon',
state: 'playing|paused',
time: '<video_time>',
},
users: ['<someuid>', '<someuid2>'],
messages: {
'<m1>': {
uid: '<someuid>',
name: 'Jane Doe',
text: 'Hello!!!!',
time: '<timestamp>',
},
'<m2>': {
uid: '<someuid2>',
name: 'Long John',
text: 'oh hi mark',
time: '<timestamp>',
},
},
},
},
};
export default DATA_MODEL;
+48 -4
View File
@@ -10,6 +10,9 @@ body.visible .watch-party-fab {
right: calc(35% - 4rem) !important; right: calc(35% - 4rem) !important;
top: 1.5rem; top: 1.5rem;
} }
body.visible .party-notification-container {
display: block !important;
}
body.netflix.visible .AkiraPlayer, /* NETFLIX */ body.netflix.visible .AkiraPlayer, /* NETFLIX */
body.hulu.visible .Player__container.addFocus,/* HULU */ body.hulu.visible .Player__container.addFocus,/* HULU */
@@ -26,11 +29,41 @@ body.amazon-prime #party-routes-wrapper {
width: 35% !important; width: 35% !important;
height: 100% !important; height: 100% !important;
} }
/* NETFLIX HEADER FIX */
body.netflix .pinning-header-container {
right: 35% !important;
}
@keyframes backgroundpulse {
0% {
background-color: black;
color: red;
}
50% {
background-color: red;
color: black;
}
100% {
background-color: black;
color: red;
}
}
#party-loading {
position: absolute;
width: 100%;
height: 100%;
z-index: 9999;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
top: 0;
left: 0;
align-items: center;
justify-content: center;
}
#party-loading.inactive {
flex-direction: column;
text-align: center;
animation-duration: 4s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-name: backgroundpulse;
}
#party-routes-wrapper { #party-routes-wrapper {
margin: 0; margin: 0;
position: fixed; position: fixed;
@@ -48,6 +81,17 @@ body.netflix .pinning-header-container {
#party-routes-wrapper.visible { #party-routes-wrapper.visible {
right: 0; right: 0;
} }
.party-notification-container {
display: none !important;
bottom: 0.8rem !important;
right: 1rem !important;
width: calc(35% - 2rem) !important;
}
.party-notification-container .MuiPaper-root {
border-radius: 26px !important;
color: white;
background-color: black;
}
.watch-party-fab { .watch-party-fab {
z-index: 9999; z-index: 9999;
position: fixed !important; position: fixed !important;
+26 -8
View File
@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom';
import firebase from 'firebase/app'; import firebase from 'firebase/app';
import App from './App'; import App from './App';
import './index.css'; import './index.css';
import { PLAYER_SOURCE, getPlayerSource } from './Api';
const firebaseConfig = { const firebaseConfig = {
apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU', apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU',
@@ -21,15 +22,32 @@ const PartyWrapper = document.createElement('div');
PartyWrapper.id = 'party_wrapper'; PartyWrapper.id = 'party_wrapper';
document.body.appendChild(PartyWrapper); document.body.appendChild(PartyWrapper);
const HREF = window.location.href; switch (getPlayerSource()) {
case PLAYER_SOURCE.NETFLIX:
document.body.classList.add('netflix');
break;
case PLAYER_SOURCE.HULU:
document.body.classList.add('hulu');
break;
case PLAYER_SOURCE.AMAZON:
document.body.classList.add('amazon-prime');
break;
default:
break;
}
// https://www.netflix. -- netflix // document.body.addEventListener('DOMNodeInserted', (ev) =>
// https://www.hulu. -- hulu // console.log('DOC CHANGE', ev.target.tagName === 'VIDEO')
// https://www.amazon. -- amazon prime // );
const VideoPlayer = document.createElement('video');
if (HREF.startsWith('https://www.amazon')) document.body.classList.add('amazon-prime'); VideoPlayer.autoplay = true;
if (HREF.startsWith('https://www.hulu')) document.body.classList.add('hulu'); VideoPlayer.muted = true;
if (HREF.startsWith('https://www.netflix')) document.body.classList.add('netflix'); VideoPlayer.controls = true;
VideoPlayer.style.width = '300px';
VideoPlayer.src =
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4';
VideoPlayer.addEventListener('change', (e) => console.log(e));
document.body.appendChild(VideoPlayer);
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
-108
View File
@@ -1,108 +0,0 @@
body.netflix {
display: flex;
height: 100vh;
overflow: hidden;
background-color: black;
}
body.netflix #appMountPoint {
z-index: 999;
overflow-y: auto;
max-height: 100vh;
resize: horizontal;
width: 1802px;
margin: 0px;
max-width: 100%;
flex-grow: 3;
flex-basis: 3;
}
.netflix-sans-font-loaded,
.netflix-sans-font-loaded > div,
.netflix-sans-font-loaded > div > div,
.nf-kb-nav-wrapper,
.sizing-wrapper {
height: 100%;
}
#appMountPoint .netflix-sans-font-loaded > div > div > .nf-kb-nav-wrapper .sizing-wrapper {
position: relative !important;
}
#party_wrapper.netflix {
position: relative;
z-index: 999;
flex-shrink: 3;
flex-basis: 1;
background-color: #1e2126;
width: 25%;
overflow: hidden;
display: flex;
}
#party_wrapper.netflix .content__wrapper {
flex: 1;
display: flex;
padding: 20px;
flex-direction: column;
}
#party_wrapper.netflix .party_chat_container {
max-height: max-content;
overflow-y: auto;
padding-bottom: 1rem;
flex: 1;
padding-right: 1rem;
}
#party_wrapper.netflix h3 {
color: white;
}
#party_wrapper.netflix h4 {
display: flex;
align-items: center;
font-weight: 200;
color: white;
}
#party_wrapper.netflix h4 button {
margin-right: 15px;
}
#party_wrapper.netflix h4 span > svg {
width: 15px;
height: 15px;
}
.party_chat_input {
position: relative;
display: flex;
}
.party_chat_input #chat_input {
resize: none;
max-height: 100px;
border-radius: 13px;
}
.party_chat_input ::-webkit-scrollbar {
display: none;
}
.party_chat_input #chat_send {
align-self: flex-end;
margin-left: 5px;
margin-bottom: 5px;
color: white;
}
.party_chat_input #chat_send svg {
height: 1.8rem;
width: 1.8rem;
}
#loading_container {
position: absolute;
top: 0;
left: 0;
z-index: 1000;
background-color: rgba(80, 80, 80, 0.9);
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#loading_container svg {
color: wheat;
width: 40px;
height: 40px;
}
.ant-popover-inner-content {
padding: 0;
}