Redone UI, add leave and delete logic
This commit is contained in:
+64
@@ -0,0 +1,64 @@
|
||||
import firebase from 'firebase/app';
|
||||
import 'firebase/auth';
|
||||
|
||||
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 isAuthed = () => true && firebase.auth().currentUser;
|
||||
|
||||
export const doAuth = () => firebase.auth().signInAnonymously();
|
||||
|
||||
export const fetcher = async ({ ENDPOINT, METHOD, ...args }) => {
|
||||
const idToken = await firebase.auth().currentUser.getIdToken();
|
||||
return fetch(`${API_HOST}/${ENDPOINT}`, {
|
||||
method: METHOD,
|
||||
headers: {
|
||||
Authorization: `Token ${idToken}:${firebase.auth().currentUser.uid}`,
|
||||
},
|
||||
...args,
|
||||
});
|
||||
};
|
||||
|
||||
export const CreateRoomApi = async (data) => {
|
||||
if (!isAuthed()) await doAuth();
|
||||
return fetcher({
|
||||
ENDPOINT: 'create',
|
||||
METHOD: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
};
|
||||
|
||||
export const JoinRoomApi = async (data) => {
|
||||
if (!isAuthed()) await doAuth();
|
||||
return fetcher({
|
||||
ENDPOINT: 'join',
|
||||
METHOD: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
};
|
||||
|
||||
export const SendMessageApi = async (data) => {
|
||||
if (!isAuthed()) await doAuth();
|
||||
return fetcher({
|
||||
ENDPOINT: 'message',
|
||||
METHOD: 'post',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
};
|
||||
|
||||
export const LeaveRoomApi = async (data) => {
|
||||
if (!isAuthed()) await doAuth();
|
||||
return fetcher({
|
||||
ENDPOINT: `leave/${data.roomId}/users/${data.uid}`,
|
||||
METHOD: 'DELETE',
|
||||
});
|
||||
};
|
||||
|
||||
export const DeleteRoomApi = async (data) => {
|
||||
if (!isAuthed()) await doAuth();
|
||||
return fetcher({
|
||||
ENDPOINT: `delete/${data.roomId}`,
|
||||
METHOD: 'DELETE',
|
||||
});
|
||||
};
|
||||
+40
-60
@@ -1,65 +1,45 @@
|
||||
import React, { Component } from 'react';
|
||||
import firebase from 'firebase/app';
|
||||
import Chat from './Chat';
|
||||
import React, { useState } from 'react';
|
||||
import Router from 'route-lite';
|
||||
import { Fab, createMuiTheme, ThemeProvider } from '@material-ui/core';
|
||||
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);
|
||||
const darkTheme = createMuiTheme({
|
||||
palette: {
|
||||
type: 'dark',
|
||||
primary: {
|
||||
main: '#a6d4fa',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Routes = (props) => (
|
||||
<Router>
|
||||
<Create />
|
||||
</Router>
|
||||
);
|
||||
|
||||
const App = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const PartyRoutesWrapper = document.getElementById('party-routes-wrapper');
|
||||
if (visible) {
|
||||
document.body.classList.add('visible');
|
||||
PartyRoutesWrapper && PartyRoutesWrapper.classList.add('visible');
|
||||
} else {
|
||||
document.body.classList.remove('visible');
|
||||
PartyRoutesWrapper && PartyRoutesWrapper.classList.remove('visible');
|
||||
}
|
||||
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 (
|
||||
return (
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<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 id="party-routes-wrapper">
|
||||
<Routes />
|
||||
</div>
|
||||
<Fab onClick={() => setVisible(!visible)} className="watch-party-fab">
|
||||
<span />
|
||||
</Fab>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
+149
-40
@@ -1,61 +1,170 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Typography, Input, message } from 'antd';
|
||||
import {
|
||||
Typography,
|
||||
TextField,
|
||||
InputAdornment,
|
||||
IconButton,
|
||||
ButtonGroup,
|
||||
Button,
|
||||
} from '@material-ui/core';
|
||||
import firebase from 'firebase/app';
|
||||
import 'firebase/firestore';
|
||||
import { RightCircleFilled } from '@ant-design/icons';
|
||||
import 'firebase/auth';
|
||||
import Thread from './Thread';
|
||||
import Send from '@material-ui/icons/SendRounded';
|
||||
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 { goTo } from 'route-lite';
|
||||
import Create from './Create';
|
||||
|
||||
export default class Chat extends Component {
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
messages: []
|
||||
name: '',
|
||||
messages: [],
|
||||
newMessage: '',
|
||||
admin: false,
|
||||
};
|
||||
this.sendMessage = this.sendMessage.bind(this);
|
||||
}
|
||||
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);
|
||||
window.addEventListener('keyup', (ev) => {
|
||||
if (ev.code === 'Enter') {
|
||||
if (ev.target === document.getElementById('chat-message-box-input')) {
|
||||
if (ev.shiftKey === false) {
|
||||
const trim = this.state.newMessage.replace(/^\s+|\s+$/g, '');
|
||||
if (trim !== '') this.sendMessage(trim);
|
||||
this.setState({ newMessage: '' });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
firebase
|
||||
.firestore()
|
||||
.collection('rooms')
|
||||
.doc(window.localStorage.getItem('watchPartyRoomId'))
|
||||
.onSnapshot(
|
||||
(snap) => {
|
||||
this.setState(snap.data());
|
||||
},
|
||||
(err) => console.error(err)
|
||||
);
|
||||
firebase
|
||||
.firestore()
|
||||
.collection('rooms')
|
||||
.doc(window.localStorage.getItem('watchPartyRoomId'))
|
||||
.collection('users')
|
||||
.onSnapshot((snap) => {
|
||||
const isAdmin = snap.docs
|
||||
.filter((user) => user.id === firebase.auth().currentUser.uid)[0]
|
||||
.data().admin;
|
||||
this.setState({ admin: isAdmin });
|
||||
});
|
||||
firebase
|
||||
.firestore()
|
||||
.collection('rooms')
|
||||
.doc(window.localStorage.getItem('watchPartyRoomId'))
|
||||
.collection('messages')
|
||||
.orderBy('time')
|
||||
.onSnapshot((snap) => {
|
||||
this.setState({ messages: snap.docs.map((v) => ({ ...v.data(), id: v.id })) });
|
||||
const pcc = document.getElementsByClassName('thread-container')[0];
|
||||
if (pcc) pcc.scrollTop = pcc.scrollHeight;
|
||||
});
|
||||
}
|
||||
endParty() {
|
||||
const data = {
|
||||
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
||||
};
|
||||
DeleteRoomApi(data).then((res) => {
|
||||
if (res.status === 200) {
|
||||
window.localStorage.removeItem('watchPartyRoomId');
|
||||
goTo(Create);
|
||||
}
|
||||
})
|
||||
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}) )});
|
||||
});
|
||||
}
|
||||
leaveParty() {
|
||||
const data = {
|
||||
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
||||
uid: firebase.auth().currentUser.uid,
|
||||
};
|
||||
LeaveRoomApi(data).then((res) => {
|
||||
if (res.status === 200) {
|
||||
window.localStorage.removeItem('watchPartyRoomId');
|
||||
goTo(Create);
|
||||
}
|
||||
});
|
||||
}
|
||||
shareParty() {
|
||||
const el = document.createElement('textarea');
|
||||
el.value = window.localStorage.getItem('watchPartyRoomId');
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
}
|
||||
sendMessage(message) {
|
||||
const data = {
|
||||
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
||||
name: window.localStorage.getItem('watchPartyUserName'),
|
||||
uid: firebase.auth().currentUser.uid,
|
||||
text: message,
|
||||
time: new Date().toISOString(),
|
||||
};
|
||||
SendMessageApi(data);
|
||||
}
|
||||
render() {
|
||||
const {messages, name} = this.state;
|
||||
const { admin, name, messages, newMessage } = 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">
|
||||
<div className="party-chat-container">
|
||||
<Typography className="brand-title" variant="overline" display="block">
|
||||
WATCH PARTY
|
||||
</Typography>
|
||||
<Typography className="room-title" variant="h4" gutterBottom>
|
||||
{name}
|
||||
</Typography>
|
||||
<ButtonGroup className="party-controls" size="small" color="secondary" fullWidth>
|
||||
<Button onClick={this.shareParty} startIcon={<Share />}>
|
||||
Share
|
||||
</Button>
|
||||
<Button startIcon={<Sync />}>Sync</Button>
|
||||
<Button onClick={this.leaveParty} startIcon={<Exit />}>
|
||||
Leave
|
||||
</Button>
|
||||
{admin && (
|
||||
<Button onClick={this.endParty} startIcon={<Delete />}>
|
||||
End Party
|
||||
</Button>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
<div className="thread-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>
|
||||
<TextField
|
||||
onSubmit={(e) => console.log(e)}
|
||||
multiline
|
||||
rowsMax="3"
|
||||
size="small"
|
||||
placeholder="New message... (<Enter> to send)"
|
||||
variant="outlined"
|
||||
className="chat-message-box"
|
||||
value={newMessage}
|
||||
onChange={(e) => this.setState({ newMessage: e.target.value })}
|
||||
InputProps={{
|
||||
id: 'chat-message-box-input',
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton edge="end">
|
||||
<Send />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+117
-131
@@ -1,143 +1,129 @@
|
||||
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';
|
||||
import {
|
||||
Button,
|
||||
InputAdornment,
|
||||
TextField,
|
||||
Typography,
|
||||
IconButton,
|
||||
Paper,
|
||||
} from '@material-ui/core';
|
||||
import Save from '@material-ui/icons/Save';
|
||||
import { JoinRoomApi, CreateRoomApi } from './Api';
|
||||
import { goTo } from 'route-lite';
|
||||
import Chat from './Chat';
|
||||
|
||||
const login = async () => firebase.auth().signInAnonymously();
|
||||
const saveName = (name) => window.localStorage.setItem('watchPartyUserName', name);
|
||||
const getName = () => window.localStorage.getItem('watchPartyUserName');
|
||||
|
||||
const Create = props => {
|
||||
const [userName, setUserName] = useState(window.localStorage.getItem('watchPartyUserName') || '');
|
||||
const [roomName, setRoomName] = useState('');
|
||||
const [roomCode, setRoomCode] = useState(window.localStorage.getItem('roomId') || '');
|
||||
const saveRoomId = (rid) => window.localStorage.setItem('watchPartyRoomId', rid);
|
||||
const getRoomId = () => window.localStorage.getItem('watchPartyRoomId');
|
||||
|
||||
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 joinRoom = async (data) => {
|
||||
const apidata = {
|
||||
roomId: data.joinName.trim(),
|
||||
userName: data.name.trim(),
|
||||
};
|
||||
const confirmedRoomId = await JoinRoomApi(apidata)
|
||||
.then((res) => res.json())
|
||||
.then((res) => res.roomId)
|
||||
.catch((err) => console.error('Join Room API error', err));
|
||||
if (confirmedRoomId === apidata.roomId) {
|
||||
saveRoomId(confirmedRoomId);
|
||||
saveName(apidata.userName);
|
||||
goTo(Chat);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
const createRoom = async (data) => {
|
||||
const apidata = {
|
||||
roomName: data.createName,
|
||||
userName: data.name,
|
||||
};
|
||||
CreateRoomApi(apidata)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
saveRoomId(res.roomId);
|
||||
saveName(data.name);
|
||||
goTo(Chat);
|
||||
});
|
||||
};
|
||||
|
||||
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(),
|
||||
});
|
||||
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 className="party-create-container">
|
||||
<Typography className="brand-title" variant="h6" display="block" gutterBottom>
|
||||
WATCH PARTY
|
||||
</Typography>
|
||||
<div className="party-create-inputs">
|
||||
<TextField
|
||||
fullWidth
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="name-field"
|
||||
label="Name"
|
||||
variant="filled"
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton onClick={() => saveName(name)}>
|
||||
<Save />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Paper className="join-card" elevation={3}>
|
||||
<Typography variant="button" display="block" gutterBottom>
|
||||
CREATE PARTY
|
||||
</Typography>
|
||||
<TextField
|
||||
fullWidth
|
||||
value={createName}
|
||||
className="card-field"
|
||||
onChange={(e) => setCreateName(e.target.value)}
|
||||
label="Party Name"
|
||||
/>
|
||||
<Button
|
||||
onClick={() => createRoom({ name, createName })}
|
||||
disabled={createName === ''}
|
||||
color="primary"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
>
|
||||
CREATE
|
||||
</Button>
|
||||
</Paper>
|
||||
<Paper className="join-card" elevation={3}>
|
||||
<Typography variant="button" display="block" gutterBottom>
|
||||
JOIN PARTY
|
||||
</Typography>
|
||||
<TextField
|
||||
color="secondary"
|
||||
fullWidth
|
||||
value={joinName}
|
||||
className="card-field"
|
||||
onChange={(e) => setJoinName(e.target.value)}
|
||||
label="Party Name"
|
||||
/>
|
||||
<Button
|
||||
onClick={() => joinRoom({ joinName, name })}
|
||||
disabled={joinName === ''}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
fullWidth
|
||||
>
|
||||
JOIN
|
||||
</Button>
|
||||
</Paper>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+56
-2
@@ -1,10 +1,64 @@
|
||||
.thread-item {
|
||||
width: 100%;
|
||||
white-space: pre-wrap;
|
||||
margin-top: 25px !important;
|
||||
font-size: 12px !important;
|
||||
position: relative;
|
||||
}
|
||||
.thread-item.no-space {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
.system-message {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: rgba(100,100,100,0.8) !important;
|
||||
}
|
||||
font-size: 12px;
|
||||
margin-top: 7.5px !important;
|
||||
color: rgba(100, 100, 100, 0.8) !important;
|
||||
}
|
||||
.my-message {
|
||||
display: block;
|
||||
width: fit-content;
|
||||
text-align: right;
|
||||
margin-left: auto !important;
|
||||
margin-right: 5px !important;
|
||||
margin-top: 5px !important;
|
||||
padding: 2.5px 10px !important;
|
||||
border-radius: 13px;
|
||||
max-width: 60%;
|
||||
word-wrap: break-word;
|
||||
background-color: rgb(49, 49, 49);
|
||||
}
|
||||
.party-message {
|
||||
display: block;
|
||||
width: fit-content;
|
||||
max-width: 60%;
|
||||
word-wrap: break-word;
|
||||
margin-top: 5px !important;
|
||||
padding: 2.5px 10px !important;
|
||||
border-radius: 13px;
|
||||
background-color: rgb(88, 88, 88);
|
||||
}
|
||||
.thread-item .name {
|
||||
color: rgb(149, 149, 149);
|
||||
font-size: 10px !important;
|
||||
}
|
||||
.time {
|
||||
position: absolute;
|
||||
bottom: 50%;
|
||||
opacity: 0;
|
||||
transition: all ease-in-out 0.3s;
|
||||
}
|
||||
.party-message ~ .time {
|
||||
right: 0;
|
||||
}
|
||||
.party-message:hover ~ .time {
|
||||
opacity: 1;
|
||||
}
|
||||
.my-message ~ .time {
|
||||
left: 0;
|
||||
}
|
||||
.my-message:hover ~ .time {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
+42
-6
@@ -1,13 +1,49 @@
|
||||
import React from 'react';
|
||||
import { Typography } from 'antd';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import './Thread.css';
|
||||
import firebase from 'firebase/app';
|
||||
import 'firebase/auth';
|
||||
|
||||
const Thread = props =>
|
||||
props.messages.map(m => {
|
||||
console.log(m);
|
||||
const Thread = (props) =>
|
||||
props.messages.map((m, midx) => {
|
||||
if (m.uid === '<system>') {
|
||||
return <Typography.Text className="thread-item system-message" key={m.id} disabled>{m.text}</Typography.Text>
|
||||
return (
|
||||
<Typography variant="body2" className="thread-item system-message" key={m.id} disabled>
|
||||
{m.text}
|
||||
</Typography>
|
||||
);
|
||||
} else if (m.uid === firebase.auth().currentUser.uid) {
|
||||
return (
|
||||
<div
|
||||
className={`thread-item ${
|
||||
midx > 0 ? (props.messages[midx - 1].uid !== m.uid ? '' : 'no-space') : ''
|
||||
}`}
|
||||
key={m.id}
|
||||
>
|
||||
<Typography variant="body1" className="my-message">
|
||||
{m.text}
|
||||
</Typography>
|
||||
<span className="time">{new Date(m.time).toLocaleTimeString()}</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
className={`thread-item ${
|
||||
midx > 0 ? (props.messages[midx - 1].uid !== m.uid ? '' : 'no-space') : ''
|
||||
}`}
|
||||
key={m.id}
|
||||
>
|
||||
<Typography variant="caption" className="name">
|
||||
{midx > 0 ? (props.messages[midx - 1].uid !== m.uid ? m.name : '') : m.name}
|
||||
</Typography>
|
||||
<Typography variant="body1" className="party-message">
|
||||
{m.text}
|
||||
</Typography>
|
||||
<span className="time">{new Date(m.time).toLocaleTimeString()}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default Thread;
|
||||
export default Thread;
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/* #party_close {
|
||||
display: inline;
|
||||
margin-right: 0.625rem;
|
||||
}
|
||||
#__player__ .Player__container {
|
||||
width: 75%;
|
||||
resize: horizontal;
|
||||
}
|
||||
#__next {
|
||||
width: 75%;
|
||||
resize: horizontal;
|
||||
}
|
||||
#party_create {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
} */
|
||||
+137
-6
@@ -1,7 +1,138 @@
|
||||
@import '~antd/dist/antd.css';
|
||||
.party-content-wrapper {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
body {
|
||||
margin: 0 !important;
|
||||
background-color: black;
|
||||
transition: max-width ease-in-out 0.35s;
|
||||
}
|
||||
body.visible {
|
||||
width: 65%;
|
||||
}
|
||||
body.visible .watch-party-fab {
|
||||
right: calc(35% - 4rem) !important;
|
||||
top: 1.5rem;
|
||||
}
|
||||
|
||||
body.netflix.visible .AkiraPlayer, /* NETFLIX */
|
||||
body.hulu.visible .Player__container.addFocus,/* HULU */
|
||||
body.amazon-prime.visible #dv-web-player.dv-player-fullscreen /* AMAZON PRIME */ {
|
||||
max-width: 65% !important;
|
||||
}
|
||||
/* HULU MINIMIZED VIDEO */
|
||||
body.hulu.visible #__player__.Player__container.Player__container--modal {
|
||||
right: calc(35% + 2rem) !important;
|
||||
}
|
||||
/* HULU & AMAZON PRIME FIX */
|
||||
body.hulu #party-routes-wrapper,
|
||||
body.amazon-prime #party-routes-wrapper {
|
||||
width: 35% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
/* NETFLIX HEADER FIX */
|
||||
body.netflix .pinning-header-container {
|
||||
right: 35% !important;
|
||||
}
|
||||
|
||||
#party-routes-wrapper {
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
background-color: rgb(27, 27, 27);
|
||||
bottom: 0;
|
||||
width: calc(35% - 2rem);
|
||||
right: -35%;
|
||||
transition: all ease-in-out 0.3s;
|
||||
height: calc(100% - 2rem);
|
||||
padding: 1rem;
|
||||
color: white;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
flex-direction: column;
|
||||
}
|
||||
#party-routes-wrapper.visible {
|
||||
right: 0;
|
||||
}
|
||||
.watch-party-fab {
|
||||
z-index: 9999;
|
||||
position: fixed !important;
|
||||
bottom: 1rem !important;
|
||||
right: -2rem !important;
|
||||
background-color: black !important;
|
||||
/* background-image: url('/logo192.png'); */
|
||||
background-image: url('chrome-extension://__MSG_@@extension_id__/logo192.png');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 2rem;
|
||||
transition: all ease-in-out 0.3s !important;
|
||||
}
|
||||
.watch-party-fab:hover {
|
||||
right: 1rem !important;
|
||||
}
|
||||
.watch-party-fab .fab-logo {
|
||||
max-width: 65%;
|
||||
max-height: 65%;
|
||||
}
|
||||
|
||||
.party-create-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
.party-create-container .brand-title {
|
||||
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;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.party-chat-container {
|
||||
display: flex;
|
||||
max-height: 100%;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.thread-container {
|
||||
max-height: 100%;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.room-title,
|
||||
.brand-title {
|
||||
padding-left: 3.5rem !important;
|
||||
}
|
||||
.thread-container::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
.thread-container::-webkit-scrollbar-track {
|
||||
background-color: rgb(90, 90, 90);
|
||||
border-radius: 1rem;
|
||||
}
|
||||
.thread-container::-webkit-scrollbar-thumb {
|
||||
background-color: rgb(118, 182, 255);
|
||||
border-radius: 13px;
|
||||
}
|
||||
textarea::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.chat-message-box > div {
|
||||
border-radius: 26px;
|
||||
}
|
||||
|
||||
.party-controls {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
+10
-18
@@ -1,11 +1,8 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import firebase from 'firebase/app';
|
||||
import 'antd/dist/antd.css';
|
||||
import './netflix.css';
|
||||
import './hulu.css';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
|
||||
const firebaseConfig = {
|
||||
apiKey: 'AIzaSyCJLuYpzXLDRmu2ttzFuSGmYcpFxqToWFU',
|
||||
@@ -24,20 +21,15 @@ 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');
|
||||
} else if (window.location.href.includes('amazon.com')) {
|
||||
// PRIME VIDEO
|
||||
} else {
|
||||
// LOCAL HOST
|
||||
PartyWrapper.classList.add('netflix');
|
||||
document.body.classList.add('netflix');
|
||||
}
|
||||
const HREF = window.location.href;
|
||||
|
||||
// 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');
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
|
||||
+24
-3
@@ -2,6 +2,7 @@ body.netflix {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: black;
|
||||
}
|
||||
body.netflix #appMountPoint {
|
||||
z-index: 999;
|
||||
@@ -41,11 +42,28 @@ body.netflix #appMountPoint {
|
||||
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{
|
||||
#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;
|
||||
@@ -80,8 +98,11 @@ body.netflix #appMountPoint {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
#loading_container svg{
|
||||
#loading_container svg {
|
||||
color: wheat;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
.ant-popover-inner-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user