add functions, create and join room

This commit is contained in:
Ayushya Amitabh
2020-03-28 21:41:21 -04:00
parent d3c19fe4cc
commit 24b2c6a4e0
19 changed files with 4340 additions and 39 deletions
+65
View File
@@ -0,0 +1,65 @@
import React, { Component } from 'react';
import firebase from 'firebase/app';
import Chat from './Chat';
import Create from './Create';
import Loading from './Loading';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
loggedIn: false,
page: 'create',
};
this.updateLoading = this.updateLoading.bind(this);
this.changePage = this.changePage.bind(this);
}
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
const roomId = window.localStorage.getItem('roomId');
if (user) {
this.setState({
loggedIn: true,
});
if (roomId)
this.setState({
page: 'chat',
});
} else {
if (roomId) {
window.localStorage.removeItem('roomId');
window.localStorage.removeItem('watchPartyUserName');
window.localStorage.removeItem('watchPartyUser');
}
}
});
}
changePage(page) {
this.setState({
page,
});
}
updateLoading(loading) {
this.setState({
loading,
});
}
render() {
const { loading, loggedIn, page } = this.state;
return (
<div className="party-content-wrapper">
{loading && <Loading />}
{page === 'chat' ? (
<Chat changePage={this.changePage} updateLoading={this.updateLoading} />
) : page === 'create' ? (
<Create
changePage={this.changePage}
loggedIn={loggedIn}
updateLoading={this.updateLoading}
/>
) : null}
</div>
);
}
}
+62
View File
@@ -0,0 +1,62 @@
import React, { Component } from 'react';
import { Typography, Input, message } from 'antd';
import firebase from 'firebase/app';
import 'firebase/firestore';
import { RightCircleFilled } from '@ant-design/icons';
import Thread from './Thread';
export default class Chat extends Component {
constructor (props) {
super(props);
this.state = {
messages: []
};
}
componentDidMount() {
firebase.firestore()
.collection('rooms')
.doc(window.localStorage.getItem('roomId'))
.onSnapshot(snap => {
this.setState(snap.data());
}, err => {
this.props.updateLoading(true);
if (err.code === 'permission-denied') {
message.error('Couldn\'t join old party');
this.props.changePage('create');
this.props.updateLoading(false);
}
})
firebase.firestore()
.collection('rooms')
.doc(window.localStorage.getItem('roomId'))
.collection('messages')
.onSnapshot(snap => {
this.setState({messages: snap.docs.map(v => ({ ...v.data(), id: v.id}) )});
});
}
render() {
const {messages, name} = this.state;
return (
<div className="content__wrapper">
<div>
<Typography.Title level={3}>{name}</Typography.Title>
<Typography.Text underline>Watch Party</Typography.Text>
</div>
<div className="party_chat_container">
<Thread messages={messages} />
</div>
<div className="party_chat_input">
<Input.TextArea
autoSize={{
maxRows:3,
minRows:1,
}}
id="chat_input"
placeholder="Begin typing..."
/>
<RightCircleFilled id="chat_send" />
</div>
</div>
);
}
}
+145
View File
@@ -0,0 +1,145 @@
import React, { useState } from 'react';
import firebase from 'firebase/app';
import 'firebase/auth';
import { Button, Input, Divider, Typography, message } from 'antd';
import { PlusCircleFilled, PlayCircleFilled } from '@ant-design/icons';
const login = async () => firebase.auth().signInAnonymously();
const Create = props => {
const [userName, setUserName] = useState(window.localStorage.getItem('watchPartyUserName') || '');
const [roomName, setRoomName] = useState('');
const [roomCode, setRoomCode] = useState(window.localStorage.getItem('roomId') || '');
const requestCreateRoom = async () => {
props.updateLoading(true);
if (userName === '') {
message.error('You forgot your name');
props.updateLoading(false);
return;
}
if (roomName === '') {
message.error('You forgot to give a party name');
props.updateLoading(false);
return;
}
if (!props.loggedIn) await login();
firebase
.auth()
.currentUser.getIdToken()
.then(idToken => {
fetch('http://localhost:5001/watch-party-crx/us-central1/widgets/create', {
method: 'POST',
headers: {
Authorization: `Token ${idToken}:${firebase.auth().currentUser.uid}`,
},
body: JSON.stringify({
roomName,
userName,
}),
})
.then(res => res.json())
.then(res => {
res.roomId && window.localStorage.setItem('roomId', res.roomId);
window.localStorage.setItem('watchPartyUserName', userName);
window.localStorage.setItem('watchPartyUser', firebase.auth().currentUser.uid);
props.changePage('chat');
props.updateLoading(false);
});
});
};
const requestJoinRoom = async () => {
props.updateLoading(true);
if (userName === '') {
message.error('You forgot your name');
props.updateLoading(false);
return;
}
if (roomCode === '') {
message.error('Please enter a party code');
props.updateLoading(false);
return;
}
if (!props.loggedIn) await login();
firebase
.auth()
.currentUser.getIdToken()
.then(idToken => {
fetch(`http://localhost:5001/watch-party-crx/us-central1/widgets/join`, {
method: 'POST',
headers: {
Authorization: `Token ${idToken}:${firebase.auth().currentUser.uid}`,
},
body: JSON.stringify({
roomId: roomCode.trim(),
userName,
}),
})
.then(res => {
if (res.status === 200) return res.json();
if (res.status === 404) message.error("That party doesn't exist");
return {};
})
.then(res => {
res.roomId && window.localStorage.setItem('roomId', res.roomId);
window.localStorage.setItem('watchPartyUserName', userName);
window.localStorage.setItem('watchPartyUser', firebase.auth().currentUser.uid);
props.changePage('chat');
props.updateLoading(false);
});
});
};
return (
<div
style={{
padding: 20,
flex: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
position: 'relative',
}}
>
<Typography.Title style={{ position: 'absolute', top: 20, left: 20 }} level={3}>
Watch Party
</Typography.Title>
<Input
style={{ borderRadius: 23, marginBottom: 15 }}
value={userName}
onChange={e => setUserName(e.target.value)}
placeholder="Your Name"
size="large"
/>
<Divider />
<Input
style={{ borderRadius: 23, marginBottom: 15 }}
value={roomName}
onChange={e => setRoomName(e.target.value)}
placeholder="Room Name"
size="large"
/>
<Button onClick={requestCreateRoom} block size="large" type="primary" shape="round">
<PlusCircleFilled style={{ color: 'white' }} />
Start Watch Party
</Button>
<Divider />
<Input
style={{ borderRadius: 23, marginBottom: 15 }}
value={roomCode}
onChange={e => setRoomCode(e.target.value)}
placeholder="Room Code"
size="large"
/>
<Button onClick={requestJoinRoom} block size="large" shape="round">
<PlayCircleFilled />
Join Watch Party
</Button>
</div>
);
};
export default Create;
+10
View File
@@ -0,0 +1,10 @@
import React from 'react';
import { LoadingOutlined } from '@ant-design/icons';
const Loading = () => (
<div id="loading_container">
<LoadingOutlined />
</div>
);
export default Loading;
-19
View File
@@ -1,19 +0,0 @@
import React, { Component } from 'react';
import './netflix.css';
export default class Netflix extends Component {
render() {
return (
<div className="content__wrapper">
<h3>WATCH PARTY</h3>
<div className="party_chat_container">CHAT WINDOW HERE</div>
<div className="party_chat_input">
<textarea
placeholder={`Write a message...\nPress Ctrl+Enter to send`}
onChange={e => console.log(e.target.value)}
/>
</div>
</div>
);
}
}
+10
View File
@@ -0,0 +1,10 @@
.thread-item {
width: 100%;
}
.system-message {
display: block;
text-align: center;
width: 100%;
text-align: center;
color: rgba(100,100,100,0.8) !important;
}
+13
View File
@@ -0,0 +1,13 @@
import React from 'react';
import { Typography } from 'antd';
import './Thread.css';
const Thread = props =>
props.messages.map(m => {
console.log(m);
if (m.uid === '<system>') {
return <Typography.Text className="thread-item system-message" key={m.id} disabled>{m.text}</Typography.Text>
}
});
export default Thread;
+7
View File
@@ -0,0 +1,7 @@
@import '~antd/dist/antd.css';
.party-content-wrapper {
position: relative;
height: 100%;
display: flex;
width: 100%;
}
+19 -8
View File
@@ -1,7 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Netflix from './Netflix';
import firebase from 'firebase/app';
import 'antd/dist/antd.css';
import './netflix.css';
import './hulu.css';
import './index.css';
import App from './App';
const firebaseConfig = {
apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU',
@@ -20,17 +24,24 @@ const PartyWrapper = document.createElement('div');
PartyWrapper.id = 'party_wrapper';
document.body.appendChild(PartyWrapper);
// STYLINGS
if (window.location.href.startsWith('https://www.hulu.com')) {
// HULU
} else if (window.location.href.startsWith('https://www.netflix.com')) {
// NETFLIX
PartyWrapper.classList.add('netflix');
document.body.classList.add('netflix');
ReactDOM.render(
<React.StrictMode>
<Netflix />
</React.StrictMode>,
PartyWrapper
);
} else if (window.location.href.includes('amazon.com')) {
// PRIME VIDEO
} else {
ReactDOM.render(<div>Watch Party not supported on this site</div>, PartyWrapper);
// LOCAL HOST
PartyWrapper.classList.add('netflix');
document.body.classList.add('netflix');
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
PartyWrapper
);
+42 -11
View File
@@ -25,6 +25,7 @@ body.netflix #appMountPoint {
position: relative !important;
}
#party_wrapper.netflix {
position: relative;
z-index: 999;
flex-shrink: 3;
flex-basis: 1;
@@ -42,15 +43,45 @@ body.netflix #appMountPoint {
#party_wrapper.netflix .party_chat_container {
flex: 1;
}
#party_wrapper.netflix .party_chat_input > textarea {
width: 100%;
margin: 0px;
height: 4rem;
max-height: 4rem;
resize: none;
color: black;
border-radius: 13px;
padding: 5px;
outline: none;
border: none;
#party_wrapper.netflix h3{
color: white;
}
.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;
}