v0.9
This commit is contained in:
@@ -48,6 +48,7 @@ const createRoom = async (req, res) => {
|
||||
.collection('rooms')
|
||||
.add({
|
||||
name: bodydata.roomName,
|
||||
player: bodydata.player,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
})
|
||||
@@ -206,6 +207,31 @@ const deleteRoom = async (req, res) => {
|
||||
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();
|
||||
app.use(cors({ origin: true }));
|
||||
app.use(authenticator);
|
||||
@@ -220,5 +246,6 @@ app.post('/join', joinRoom);
|
||||
app.post('/message', sendMessage);
|
||||
app.delete('/leave/:rid/users/:uid', leaveRoom);
|
||||
app.delete('/delete/:rid', deleteRoom);
|
||||
app.post('/updatePlayer', updatePlayer);
|
||||
|
||||
exports.widgets = functions.https.onRequest(app);
|
||||
|
||||
+32
-1
@@ -5,6 +5,27 @@ const API_HOST = window.location.href.startsWith('http://localhost')
|
||||
? 'http://localhost:5001/watch-party-crx/us-central1/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 doAuth = () => firebase.auth().signInAnonymously();
|
||||
@@ -42,7 +63,7 @@ export const SendMessageApi = async (data) => {
|
||||
if (!isAuthed()) await doAuth();
|
||||
return fetcher({
|
||||
ENDPOINT: 'message',
|
||||
METHOD: 'post',
|
||||
METHOD: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
};
|
||||
@@ -62,3 +83,13 @@ export const DeleteRoomApi = async (data) => {
|
||||
METHOD: 'DELETE',
|
||||
});
|
||||
};
|
||||
|
||||
export const UpdatePlayerApi = async (data) => {
|
||||
if (isAuthed()) {
|
||||
return fetcher({
|
||||
ENDPOINT: 'updatePlayer',
|
||||
METHOD: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
+45
-4
@@ -1,7 +1,11 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
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 Loading from './Loading';
|
||||
import firebase from 'firebase/app';
|
||||
import 'firebase/firestore';
|
||||
import Inactive from './Inactive';
|
||||
|
||||
const darkTheme = createMuiTheme({
|
||||
palette: {
|
||||
@@ -14,12 +18,31 @@ const darkTheme = createMuiTheme({
|
||||
|
||||
const Routes = (props) => (
|
||||
<Router>
|
||||
<Create />
|
||||
<Create {...props} />
|
||||
</Router>
|
||||
);
|
||||
|
||||
const App = () => {
|
||||
const closedNotification = { open: false, message: '' };
|
||||
|
||||
const [status, setStatus] = useState({ active: false, message: '' });
|
||||
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');
|
||||
if (visible) {
|
||||
document.body.classList.add('visible');
|
||||
@@ -28,11 +51,29 @@ const App = () => {
|
||||
document.body.classList.remove('visible');
|
||||
PartyRoutesWrapper && PartyRoutesWrapper.classList.remove('visible');
|
||||
}
|
||||
|
||||
return (
|
||||
<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 id="party-routes-wrapper">
|
||||
<Routes />
|
||||
{loading && <Loading />}
|
||||
{status.active ? (
|
||||
<Routes
|
||||
updateLoading={setLoading}
|
||||
notify={(message) => setNotification({ open: true, message })}
|
||||
/>
|
||||
) : (
|
||||
<Inactive status={status} />
|
||||
)}
|
||||
</div>
|
||||
<Fab onClick={() => setVisible(!visible)} className="watch-party-fab">
|
||||
<span />
|
||||
|
||||
+39
-4
@@ -16,9 +16,10 @@ import Sync from '@material-ui/icons/SyncOutlined';
|
||||
import Exit from '@material-ui/icons/ExitToAppRounded';
|
||||
import Share from '@material-ui/icons/ShareRounded';
|
||||
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 Create from './Create';
|
||||
import VideoListener from './VideoListener';
|
||||
|
||||
export default class Chat extends Component {
|
||||
constructor(props) {
|
||||
@@ -28,8 +29,15 @@ export default class Chat extends Component {
|
||||
messages: [],
|
||||
newMessage: '',
|
||||
admin: false,
|
||||
player: {},
|
||||
localPlayer: {},
|
||||
};
|
||||
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() {
|
||||
window.addEventListener('keyup', (ev) => {
|
||||
@@ -49,9 +57,12 @@ export default class Chat extends Component {
|
||||
.doc(window.localStorage.getItem('watchPartyRoomId'))
|
||||
.onSnapshot(
|
||||
(snap) => {
|
||||
if (snap.data().player.status !== this.state.localPlayer.status) {
|
||||
this.mVideoListener.doUpdate(snap.data().player);
|
||||
}
|
||||
this.setState(snap.data());
|
||||
},
|
||||
(err) => console.error(err)
|
||||
(err) => {}
|
||||
);
|
||||
firebase
|
||||
.firestore()
|
||||
@@ -75,27 +86,50 @@ export default class Chat extends Component {
|
||||
const pcc = document.getElementsByClassName('thread-container')[0];
|
||||
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() {
|
||||
this.props.notify('Ending party...');
|
||||
this.props.updateLoading(true);
|
||||
const data = {
|
||||
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
||||
};
|
||||
DeleteRoomApi(data).then((res) => {
|
||||
if (res.status === 200) {
|
||||
this.props.notify('Ended party');
|
||||
window.localStorage.removeItem('watchPartyRoomId');
|
||||
goTo(Create);
|
||||
goTo(Create, this.props);
|
||||
}
|
||||
});
|
||||
}
|
||||
leaveParty() {
|
||||
this.props.notify('Leaving party...');
|
||||
this.props.updateLoading(true);
|
||||
const data = {
|
||||
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
||||
uid: firebase.auth().currentUser.uid,
|
||||
};
|
||||
LeaveRoomApi(data).then((res) => {
|
||||
if (res.status === 200) {
|
||||
this.props.notify('Left party');
|
||||
window.localStorage.removeItem('watchPartyRoomId');
|
||||
goTo(Create);
|
||||
goTo(Create, this.props);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -106,6 +140,7 @@ export default class Chat extends Component {
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
this.props.notify('Copied room code');
|
||||
}
|
||||
sendMessage(message) {
|
||||
const data = {
|
||||
|
||||
+72
-19
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Button,
|
||||
InputAdornment,
|
||||
@@ -8,17 +8,27 @@ import {
|
||||
Paper,
|
||||
} from '@material-ui/core';
|
||||
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 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 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 joinRoom = async (data) => {
|
||||
data.props.updateLoading(true);
|
||||
data.props.notify('Joining party...');
|
||||
const apidata = {
|
||||
roomId: data.joinName.trim(),
|
||||
userName: data.name.trim(),
|
||||
@@ -26,25 +36,58 @@ const joinRoom = async (data) => {
|
||||
const confirmedRoomId = await JoinRoomApi(apidata)
|
||||
.then((res) => res.json())
|
||||
.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) {
|
||||
saveRoomId(confirmedRoomId);
|
||||
saveName(apidata.userName);
|
||||
goTo(Chat);
|
||||
saveName({
|
||||
name: apidata.userName,
|
||||
notify: data.props.notify,
|
||||
});
|
||||
saveRoomId({
|
||||
rid: confirmedRoomId,
|
||||
notify: data.props.notify,
|
||||
});
|
||||
data.props.notify('Joined party');
|
||||
goTo(Chat, data.props);
|
||||
}
|
||||
};
|
||||
|
||||
const createRoom = async (data) => {
|
||||
data.props.updateLoading(true);
|
||||
data.props.notify('Starting party...');
|
||||
|
||||
const apidata = {
|
||||
roomName: data.createName,
|
||||
userName: data.name,
|
||||
player: {
|
||||
source: getPlayerSource(),
|
||||
status: PLAYER_STATUS.NOT_PLAYING,
|
||||
time: 0,
|
||||
url: '',
|
||||
},
|
||||
};
|
||||
|
||||
CreateRoomApi(apidata)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
saveRoomId(res.roomId);
|
||||
saveName(data.name);
|
||||
goTo(Chat);
|
||||
saveName({
|
||||
name: data.name,
|
||||
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 [createName, setCreateName] = useState('');
|
||||
const [joinName, setJoinName] = useState(getRoomId() || '');
|
||||
if (getName() && getRoomId())
|
||||
joinRoom({
|
||||
joinName: getRoomId(),
|
||||
name: getName(),
|
||||
});
|
||||
const { notify, updateLoading } = props;
|
||||
|
||||
const joinOnMount = () => {
|
||||
updateLoading(false);
|
||||
// NEEDS FIX - REPETITIVE API CALLS
|
||||
// if (getName() && getRoomId()) {
|
||||
// joinRoom({
|
||||
// joinName,
|
||||
// name,
|
||||
// props,
|
||||
// });
|
||||
// }
|
||||
};
|
||||
useEffect(joinOnMount, []);
|
||||
|
||||
return (
|
||||
<div className="party-create-container">
|
||||
<Typography className="brand-title" variant="h6" display="block" gutterBottom>
|
||||
@@ -73,7 +126,7 @@ const Create = (props) => {
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton onClick={() => saveName(name)}>
|
||||
<IconButton onClick={() => saveName({ name, notify })}>
|
||||
<Save />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
@@ -92,7 +145,7 @@ const Create = (props) => {
|
||||
label="Party Name"
|
||||
/>
|
||||
<Button
|
||||
onClick={() => createRoom({ name, createName })}
|
||||
onClick={() => createRoom({ name, createName, props })}
|
||||
disabled={createName === ''}
|
||||
color="primary"
|
||||
fullWidth
|
||||
@@ -114,7 +167,7 @@ const Create = (props) => {
|
||||
label="Party Name"
|
||||
/>
|
||||
<Button
|
||||
onClick={() => joinRoom({ joinName, name })}
|
||||
onClick={() => joinRoom({ joinName, name, props })}
|
||||
disabled={joinName === ''}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
|
||||
@@ -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
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
import { CircularProgress } from '@material-ui/core';
|
||||
|
||||
const Loading = () => (
|
||||
<div id="loading_container">
|
||||
<LoadingOutlined />
|
||||
<div id="party-loading">
|
||||
<CircularProgress />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Loading;
|
||||
export default Loading;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -10,6 +10,9 @@ body.visible .watch-party-fab {
|
||||
right: calc(35% - 4rem) !important;
|
||||
top: 1.5rem;
|
||||
}
|
||||
body.visible .party-notification-container {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
body.netflix.visible .AkiraPlayer, /* NETFLIX */
|
||||
body.hulu.visible .Player__container.addFocus,/* HULU */
|
||||
@@ -26,11 +29,41 @@ body.amazon-prime #party-routes-wrapper {
|
||||
width: 35% !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 {
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
@@ -48,6 +81,17 @@ body.netflix .pinning-header-container {
|
||||
#party-routes-wrapper.visible {
|
||||
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 {
|
||||
z-index: 9999;
|
||||
position: fixed !important;
|
||||
|
||||
+26
-8
@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom';
|
||||
import firebase from 'firebase/app';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
import { PLAYER_SOURCE, getPlayerSource } from './Api';
|
||||
|
||||
const firebaseConfig = {
|
||||
apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU',
|
||||
@@ -21,15 +22,32 @@ const PartyWrapper = document.createElement('div');
|
||||
PartyWrapper.id = 'party_wrapper';
|
||||
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
|
||||
// https://www.hulu. -- hulu
|
||||
// https://www.amazon. -- amazon prime
|
||||
|
||||
if (HREF.startsWith('https://www.amazon')) document.body.classList.add('amazon-prime');
|
||||
if (HREF.startsWith('https://www.hulu')) document.body.classList.add('hulu');
|
||||
if (HREF.startsWith('https://www.netflix')) document.body.classList.add('netflix');
|
||||
// document.body.addEventListener('DOMNodeInserted', (ev) =>
|
||||
// console.log('DOC CHANGE', ev.target.tagName === 'VIDEO')
|
||||
// );
|
||||
const VideoPlayer = document.createElement('video');
|
||||
VideoPlayer.autoplay = true;
|
||||
VideoPlayer.muted = true;
|
||||
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(
|
||||
<React.StrictMode>
|
||||
|
||||
-108
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user