281 lines
9.4 KiB
JavaScript
281 lines
9.4 KiB
JavaScript
/*global chrome */
|
|
import React, { Component } from 'react';
|
|
import {
|
|
Typography,
|
|
TextField,
|
|
InputAdornment,
|
|
IconButton,
|
|
ButtonGroup,
|
|
Button,
|
|
} from '@material-ui/core';
|
|
import Thread from './Thread';
|
|
import Send from '@material-ui/icons/Send';
|
|
import Exit from '@material-ui/icons/ExitToApp';
|
|
import Share from '@material-ui/icons/Share';
|
|
import Delete from '@material-ui/icons/Delete';
|
|
import Gif from '@material-ui/icons/Gif';
|
|
import { Carousel } from '@giphy/react-components';
|
|
import { GiphyFetch } from '@giphy/js-fetch-api';
|
|
import {
|
|
SendMessageApi,
|
|
LeaveRoomApi,
|
|
DeleteRoomApi,
|
|
UpdatePlayerApi,
|
|
getPlayerSource,
|
|
PLAYER_STATUS,
|
|
} from './Api';
|
|
import { goTo } from 'route-lite';
|
|
import Create from './Create';
|
|
import VideoListener from './VideoListener';
|
|
|
|
const giphyFetch = new GiphyFetch('meKO7qbULHC9Q26DRGt2yZguIn3LB6z7');
|
|
export default class Chat extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
name: '',
|
|
messages: [],
|
|
newMessage: '',
|
|
admin: null,
|
|
player: {},
|
|
localPlayer: {},
|
|
gif: false,
|
|
};
|
|
this.sendMessage = this.sendMessage.bind(this);
|
|
this.sendMessageInvoker = this.sendMessageInvoker.bind(this);
|
|
this.endParty = this.endParty.bind(this);
|
|
this.leaveParty = this.leaveParty.bind(this);
|
|
this.shareParty = this.shareParty.bind(this);
|
|
this.syncPlayer = this.syncPlayer.bind(this);
|
|
this.videoEventListener = this.videoEventListener.bind(this);
|
|
this.mVideoListener = new VideoListener(this.videoEventListener);
|
|
this.port = null;
|
|
this.getCarousel = this.getCarousel.bind(this);
|
|
this.gifClick = this.gifClick.bind(this);
|
|
}
|
|
componentDidMount() {
|
|
window.onkeypress = (ev) => {
|
|
if (ev.code === 'Enter') {
|
|
if (ev.target === document.getElementById('chat-message-box-input')) {
|
|
if (ev.shiftKey === false && this.state.gif === true) ev.preventDefault();
|
|
if (ev.shiftKey === false && this.state.gif === false) {
|
|
ev.preventDefault();
|
|
this.sendMessageInvoker();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
this.port = chrome.runtime.connect({ name: window.localStorage.getItem('watchPartyRoomId') });
|
|
this.port.postMessage({
|
|
type: 'INIT',
|
|
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
|
});
|
|
this.port.onMessage.addListener((response) => {
|
|
if (response.isError === true) {
|
|
this.props.notify('Uh oh. Got lost from the party.');
|
|
goTo(Create, this.props);
|
|
return;
|
|
}
|
|
this.setState(response);
|
|
if (response.player && this.state.localPlayer.status !== response.player.status) {
|
|
if (response.player.source !== getPlayerSource()) {
|
|
this.props.updateLoading(true);
|
|
this.props.notify('This party is for a different website');
|
|
goTo(Create, this.props);
|
|
this.props.updateLoading(false);
|
|
} else {
|
|
if (this.mVideoListener.isUpdating === false)
|
|
this.mVideoListener.doUpdate(response.player);
|
|
}
|
|
}
|
|
const pcc = document.getElementsByClassName('thread-container')[0];
|
|
if (pcc) pcc.scrollTop = pcc.scrollHeight;
|
|
});
|
|
this.mVideoListener.start();
|
|
this.props.updateLoading(false);
|
|
}
|
|
videoEventListener(video) {
|
|
this.setState({ localPlayer: video });
|
|
if (
|
|
this.state.player.status !== video.status &&
|
|
video.showName &&
|
|
video.status !== PLAYER_STATUS.NOT_PLAYING
|
|
)
|
|
this.syncPlayer(video);
|
|
}
|
|
syncPlayer(video) {
|
|
const { status, showName, time, source, element } = video;
|
|
if (element) {
|
|
UpdatePlayerApi({
|
|
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
|
showChanged: showName !== this.state.player.showName,
|
|
userName: window.localStorage.getItem('watchPartyUserName'),
|
|
player: {
|
|
status: showName !== this.state.player.showName ? PLAYER_STATUS.PAUSED : status,
|
|
showName,
|
|
time,
|
|
source,
|
|
updatedBy: window.localStorage.getItem('watchPartyUserUid'),
|
|
},
|
|
}).catch((err) => {
|
|
this.props.updateLoading(false);
|
|
this.props.notify(err.message || err.statusText);
|
|
console.error('Sync Party API error', err);
|
|
});
|
|
} else {
|
|
this.mVideoListener.start();
|
|
}
|
|
}
|
|
endParty() {
|
|
this.props.notify('Ending party...');
|
|
this.props.updateLoading(true);
|
|
const data = {
|
|
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
|
};
|
|
DeleteRoomApi(data)
|
|
.then((res) => {
|
|
this.props.notify('Ended party');
|
|
window.localStorage.removeItem('watchPartyRoomId');
|
|
goTo(Create, this.props);
|
|
this.props.updateLoading(false);
|
|
})
|
|
.catch((err) => {
|
|
this.props.updateLoading(false);
|
|
this.props.notify(err.message || err.statusText);
|
|
console.error('End Party API error', err);
|
|
});
|
|
}
|
|
leaveParty() {
|
|
this.props.notify('Leaving party...');
|
|
this.props.updateLoading(true);
|
|
const data = {
|
|
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
|
uid: window.localStorage.getItem('watchPartyUserUid'),
|
|
};
|
|
LeaveRoomApi(data)
|
|
.then((res) => {
|
|
this.props.notify('Left party');
|
|
window.localStorage.removeItem('watchPartyRoomId');
|
|
goTo(Create, this.props);
|
|
this.props.updateLoading(false);
|
|
})
|
|
.catch((err) => {
|
|
this.props.updateLoading(false);
|
|
this.props.notify(err.message || err.statusText);
|
|
console.error('End Party API error', err);
|
|
});
|
|
}
|
|
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);
|
|
this.props.notify('Copied room code');
|
|
}
|
|
sendMessage(message) {
|
|
const data = {
|
|
roomId: window.localStorage.getItem('watchPartyRoomId'),
|
|
name: window.localStorage.getItem('watchPartyUserName'),
|
|
uid: window.localStorage.getItem('watchPartyUserUid'),
|
|
text: message,
|
|
time: new Date().toISOString(),
|
|
type: '',
|
|
};
|
|
if (this.state.gif === true) data.type = 'giphy';
|
|
SendMessageApi(data).catch((err) => {
|
|
this.props.notify(err.message || err.statusText);
|
|
console.error('Send Message API error', err);
|
|
});
|
|
}
|
|
sendMessageInvoker(overrideMessage) {
|
|
if (overrideMessage) this.sendMessage(overrideMessage);
|
|
else {
|
|
const trim = this.state.newMessage.replace(/^\s+|\s+$/g, '');
|
|
if (trim !== '') this.sendMessage(trim);
|
|
}
|
|
this.setState({ newMessage: '' });
|
|
}
|
|
gifClick(iGif, ev) {
|
|
ev.preventDefault();
|
|
this.sendMessageInvoker(iGif.images.original.url);
|
|
}
|
|
getCarousel() {
|
|
const trendingFetch = (offset) => giphyFetch.trending({ offset, limit: 5 });
|
|
const searchFetch = (offset) => giphyFetch.search(this.state.newMessage, { offset, limit: 5 });
|
|
return (
|
|
<Carousel
|
|
fetchGifs={this.state.newMessage === '' ? trendingFetch : searchFetch}
|
|
gifHeight={200}
|
|
gutter={6}
|
|
onGifClick={this.gifClick}
|
|
hideAttribution
|
|
/>
|
|
);
|
|
}
|
|
render() {
|
|
const { admin, name, messages, newMessage, gif } = this.state;
|
|
return (
|
|
<div className="party-chat-container">
|
|
<div className="party-chat-header">
|
|
<div className="party-chat-header-text">
|
|
<Typography className="brand-title" variant="overline" display="block">
|
|
WATCH PARTY
|
|
</Typography>
|
|
<Typography className="room-title" variant="h4" gutterBottom>
|
|
{name}
|
|
</Typography>
|
|
</div>
|
|
<ButtonGroup className="party-controls" size="small" color="secondary">
|
|
<Button onClick={this.shareParty} startIcon={<Share />}>
|
|
<span className="party-control-button-text">Share</span>
|
|
</Button>
|
|
<Button onClick={this.leaveParty} startIcon={<Exit />}>
|
|
<span className="party-control-button-text">Leave</span>
|
|
</Button>
|
|
{admin && (
|
|
<Button onClick={this.endParty} startIcon={<Delete />}>
|
|
{/* <span className="party-control-button-text">End Party</span> */}
|
|
</Button>
|
|
)}
|
|
</ButtonGroup>
|
|
</div>
|
|
<div className="thread-container">
|
|
<Thread
|
|
messages={messages.sort((a, b) => new Date(b).getTime() - new Date(a).getTime())}
|
|
/>
|
|
</div>
|
|
<TextField
|
|
multiline
|
|
rowsMax="3"
|
|
size="small"
|
|
placeholder="New message... (Enter to send)"
|
|
variant="outlined"
|
|
className="chat-message-box addFocus"
|
|
value={newMessage}
|
|
onChange={(e) => this.setState({ newMessage: e.target.value, gif: false })}
|
|
InputProps={{
|
|
id: 'chat-message-box-input',
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<IconButton onClick={this.sendMessageInvoker} edge="end">
|
|
<Send />
|
|
</IconButton>
|
|
</InputAdornment>
|
|
),
|
|
startAdornment: (
|
|
<InputAdornment position="start">
|
|
<IconButton onClick={() => this.setState({ gif: !gif })} edge="start">
|
|
<Gif />
|
|
</IconButton>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
/>
|
|
{gif ? this.getCarousel() : null}
|
|
</div>
|
|
);
|
|
}
|
|
}
|