496 lines
17 KiB
JavaScript
496 lines
17 KiB
JavaScript
/************************************************************************
|
|
Copyright (C) Ayushya Amitabh - All Rights Reserved
|
|
Unauthorized copying of this file, via any medium is strictly prohibited
|
|
Proprietary and confidential
|
|
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
|
|
https://www.chromewatchparty.com/copyright
|
|
=========================================================================
|
|
ALL CONTENT ON IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
|
|
CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
|
|
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
|
|
|
|
Netflix logo is a trademark and intellectual property of Netflix, Inc.
|
|
Disney+ logo is a trademark and intellectual property of The Walt Disney Company
|
|
Prime logo is a trademark and intellectual property of Amazon.com, Inc.
|
|
YouTube logo is a trademark and intellectual property of Google, Inc.
|
|
Hulu logo is a trademark and intellectual property of Hulu, LLC.
|
|
HBO Now and HBO Max logos are trademark and intellectual property of WarnerMedia Direct,LLC.
|
|
************************************************************************/
|
|
/*global chrome */
|
|
import React, { Component } from 'react';
|
|
import { Button, FormControlLabel, Switch, TextField, Typography, Paper, ButtonGroup, ExpansionPanel, ExpansionPanelSummary, ExpansionPanelDetails, MobileStepper, IconButton, Card, CardContent, CardActions } from '@material-ui/core';
|
|
import {
|
|
JoinRoomApi,
|
|
CreateRoomApi,
|
|
PLAYER_STATUS,
|
|
getPlayerSource,
|
|
LocalData,
|
|
updateLocalData,
|
|
getUserUid,
|
|
emptyLocalData,
|
|
} from './Api';
|
|
import { goTo } from 'route-lite';
|
|
import Chat from './Chat';
|
|
import {
|
|
ChevronRight,
|
|
InfoRounded,
|
|
MonetizationOnRounded,
|
|
RateReviewRounded,
|
|
Settings,
|
|
ExpandLess,
|
|
KeyboardArrowLeft,
|
|
KeyboardArrowRight,
|
|
Notifications
|
|
} from '@material-ui/icons';
|
|
import { Autocomplete } from "@material-ui/lab";
|
|
|
|
const CONSTANTS = {
|
|
USER_NAME_LENGTH: 12,
|
|
NAME_LENGTH: 24,
|
|
PIN_LENGTH: 9,
|
|
};
|
|
export default class Create extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
const defaultMode = LocalData().partyName ? 'join' : 'create';
|
|
this.state = {
|
|
userName: LocalData().userName || '',
|
|
name: LocalData().partyName || '',
|
|
pin: LocalData().partyPin || '',
|
|
mode: defaultMode,
|
|
parties: [],
|
|
adminOnly: false,
|
|
alertStep: this.props.status && this.props.status.importantAlert ? this.props.status.importantAlert : 0,
|
|
allowAutoRejoin:
|
|
LocalData().allowAutoRejoin !== undefined ? LocalData().allowAutoRejoin : true,
|
|
allowChatNotification:
|
|
LocalData().allowChatNotification !== undefined ? LocalData().allowChatNotification : true,
|
|
};
|
|
this.createCall = this.createCall.bind(this);
|
|
this.joinCall = this.joinCall.bind(this);
|
|
this.success = this.success.bind(this);
|
|
this.partyNameAutocompleteProcessor = this.partyNameAutocompleteProcessor.bind(this);
|
|
this.updateAllowAutoRejoin = this.updateAllowAutoRejoin.bind(this);
|
|
this.updateAllowChatNotification = this.updateAllowChatNotification.bind(this);
|
|
this.clearPartyHistory = this.clearPartyHistory.bind(this);
|
|
this.handleSteps = this.handleSteps.bind(this);
|
|
}
|
|
componentDidMount() {
|
|
const { lastActive, userName, partyName, partyPin, allowAutoRejoin } = LocalData();
|
|
if (allowAutoRejoin === true && lastActive && userName && partyName && partyPin) {
|
|
if (Math.abs(new Date(parseInt(lastActive)) - new Date().getTime()) <= 60000) {
|
|
this.props.updateLoading(true);
|
|
this.joinCall(userName, partyName, partyPin);
|
|
}
|
|
}
|
|
chrome.runtime.sendMessage(
|
|
{
|
|
type: 'GET_PARTIES'
|
|
},
|
|
(response) => {
|
|
this.setState({ parties: response });
|
|
}
|
|
);
|
|
}
|
|
partyNameChange(value) {
|
|
if (value.length <= CONSTANTS.NAME_LENGTH) this.setState({ name: value });
|
|
}
|
|
partyPinChange(value) {
|
|
if (value.length <= CONSTANTS.PIN_LENGTH) this.setState({ pin: value });
|
|
}
|
|
userNameChange(value) {
|
|
if (value.length <= CONSTANTS.USER_NAME_LENGTH) this.setState({ userName: value });
|
|
}
|
|
createOrJoin() {
|
|
this.props.updateLoading(true);
|
|
getUserUid();
|
|
const { userName, name, pin, mode } = this.state;
|
|
if (!userName.replace(/\s+/g, '')) {
|
|
this.props.notify('Missing user name');
|
|
this.props.updateLoading(false);
|
|
return;
|
|
}
|
|
if (!name.replace(/\s+/g, '')) {
|
|
this.props.notify('Missing party name');
|
|
this.props.updateLoading(false);
|
|
return;
|
|
}
|
|
if (!pin.replace(/\s+/g, '')) {
|
|
this.props.notify('Missing party pin');
|
|
this.props.updateLoading(false);
|
|
return;
|
|
}
|
|
if (userName && name && pin) {
|
|
switch (mode) {
|
|
case 'create':
|
|
this.createCall(userName.trim(), name.trim(), pin);
|
|
break;
|
|
case 'join':
|
|
this.joinCall(userName.trim(), name.trim(), pin);
|
|
break;
|
|
default:
|
|
this.props.updateLoading(false);
|
|
break;
|
|
}
|
|
} else {
|
|
this.props.notify('Double check your inputs');
|
|
}
|
|
}
|
|
createCall(userName, partyName, partyPin) {
|
|
this.props.updateLoading(true);
|
|
getUserUid();
|
|
const apidata = {
|
|
userName,
|
|
partyName,
|
|
partyPin,
|
|
player: {
|
|
source: getPlayerSource(),
|
|
status: PLAYER_STATUS.PAUSED,
|
|
time: 0,
|
|
url: window.location.href,
|
|
},
|
|
adminOnly: this.state.adminOnly,
|
|
};
|
|
CreateRoomApi(apidata)
|
|
.then((res) => res.json())
|
|
.then((createResult) => {
|
|
if (createResult.status <= 299) {
|
|
this.success(userName, partyName, partyPin);
|
|
} else {
|
|
this.props.updateLoading(false);
|
|
this.props.notify(createResult.message || createResult.statusText);
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
console.error('Create Party API Error', err);
|
|
this.props.notify(err.message || 'Something went wrong');
|
|
this.props.updateLoading(false);
|
|
});
|
|
}
|
|
joinCall(userName, partyName, partyPin) {
|
|
const apidata = {
|
|
userName,
|
|
partyName,
|
|
partyPin,
|
|
source: getPlayerSource(),
|
|
};
|
|
getUserUid();
|
|
JoinRoomApi(apidata)
|
|
.then((res) => res.json())
|
|
.then((joinResult) => {
|
|
if (joinResult.status <= 299) {
|
|
this.success(userName, partyName, partyPin);
|
|
} else {
|
|
this.props.updateLoading(false);
|
|
this.props.notify(joinResult.message || joinResult.statusText);
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
emptyLocalData();
|
|
console.error('Join Party API Error', err);
|
|
this.props.notify(err.message || 'Something went wrong');
|
|
this.props.updateLoading(false);
|
|
});
|
|
}
|
|
success(userName, partyName, partyPin) {
|
|
updateLocalData({ userName, partyName, partyPin });
|
|
this.props.updateLoading(false);
|
|
goTo(Chat, this.props);
|
|
}
|
|
partyNameAutocompleteProcessor(v) {
|
|
if (v !== null && v !== undefined && typeof (v) === 'object') {
|
|
if (v.name) this.partyNameChange(v.name);
|
|
else this.partyNameChange('');
|
|
if (v.pin) this.partyPinChange(v.pin);
|
|
else this.partyPinChange('');
|
|
} else if (typeof (v) === 'object' && (v === null || v === undefined)) {
|
|
this.partyNameChange('');
|
|
this.partyPinChange('');
|
|
} else if (v !== undefined && v !== null && typeof (v) === 'string') {
|
|
this.partyNameChange(v);
|
|
}
|
|
}
|
|
updateAllowChatNotification() {
|
|
updateLocalData({ allowChatNotification: !this.state.allowChatNotification });
|
|
this.setState({ allowChatNotification: !this.state.allowChatNotification });
|
|
}
|
|
updateAllowAutoRejoin() {
|
|
updateLocalData({ allowAutoRejoin: !this.state.allowAutoRejoin });
|
|
this.setState({ allowAutoRejoin: !this.state.allowAutoRejoin });
|
|
}
|
|
clearPartyHistory () {
|
|
this.setState({
|
|
parties: [],
|
|
});
|
|
chrome.runtime.sendMessage({type: 'EMPTY_PARTIES'});
|
|
}
|
|
handleSteps (direction) {
|
|
const { alertStep } = this.state;
|
|
if (alertStep + direction < 0) return;
|
|
if (alertStep + direction >= this.props.status.alerts.length) return;
|
|
this.setState({ alertStep: alertStep + direction });
|
|
}
|
|
render() {
|
|
const {
|
|
mode,
|
|
name,
|
|
pin,
|
|
userName,
|
|
parties,
|
|
adminOnly,
|
|
allowAutoRejoin,
|
|
allowChatNotification,
|
|
alertStep
|
|
} = this.state;
|
|
const { status } = this.props;
|
|
return (
|
|
<div className="party-create-container">
|
|
<Typography
|
|
color="secondary"
|
|
className="brand-title"
|
|
variant="h6"
|
|
display="block"
|
|
gutterBottom
|
|
>
|
|
WATCH PARTY
|
|
</Typography>
|
|
<Paper className="create-input-card" elevation={3}>
|
|
<ButtonGroup className="create-mode-select" size="small" fullWidth>
|
|
<Button
|
|
id="mode-select-left"
|
|
variant={mode === 'create' ? 'contained' : null}
|
|
color={mode === 'create' ? 'secondary' : null}
|
|
disableElevation={mode === 'create'}
|
|
onClick={() => this.setState({ mode: 'create' })}
|
|
>
|
|
Create
|
|
</Button>
|
|
<Button
|
|
id="mode-select-right"
|
|
variant={mode === 'join' ? 'contained' : null}
|
|
color={mode === 'join' ? 'secondary' : null}
|
|
disableElevation={mode === 'join'}
|
|
onClick={() => this.setState({ mode: 'join' })}
|
|
>
|
|
Join
|
|
</Button>
|
|
</ButtonGroup>
|
|
<TextField
|
|
color="secondary"
|
|
size="small"
|
|
className="create-name addFocus"
|
|
label="Name"
|
|
variant="outlined"
|
|
value={userName}
|
|
onChange={(e) => this.userNameChange(e.target.value)}
|
|
helperText={
|
|
userName ? `${userName.length}/${CONSTANTS.USER_NAME_LENGTH} Characters` : null
|
|
}
|
|
/>
|
|
<Autocomplete
|
|
color="secondary"
|
|
options={parties}
|
|
getOptionLabel={o => o.name}
|
|
autoComplete
|
|
freeSolo
|
|
value={{name:name, pin: pin}}
|
|
onInputChange={(e,v,r) => this.partyNameAutocompleteProcessor({name: v, pin: pin})}
|
|
onChange={(e, v) => this.partyNameAutocompleteProcessor(v)}
|
|
renderInput={(params) =>
|
|
<TextField
|
|
color="secondary"
|
|
size="small"
|
|
{...params}
|
|
className="create-name addFocus"
|
|
label="Party Name"
|
|
variant="outlined"
|
|
helperText={name ? `${name.length}/${CONSTANTS.NAME_LENGTH} Characters` : null}
|
|
/>}
|
|
/>
|
|
<TextField
|
|
color="secondary"
|
|
size="small"
|
|
className="create-pin addFocus"
|
|
label="Party Pin"
|
|
variant="outlined"
|
|
type="number"
|
|
value={pin}
|
|
onChange={(e) => this.partyPinChange(e.target.value)}
|
|
helperText={pin ? `${pin.length}/${CONSTANTS.PIN_LENGTH} Digits` : null}
|
|
/>
|
|
<FormControlLabel
|
|
className={`notification-switch admin-only-switch ${mode !== 'create' ? 'admin-only-switch-hidden' : ''}`}
|
|
control={
|
|
<Switch
|
|
checked={adminOnly}
|
|
onChange={() => this.setState({ adminOnly: !adminOnly })}
|
|
name="adminOnly"
|
|
color={adminOnly ? 'secondary' : 'primary'}
|
|
/>
|
|
}
|
|
label={
|
|
<span>
|
|
Admin Only Mode (can be changed later)
|
|
<br />
|
|
<span style={{ color: '#8a8a8a' }}>
|
|
{adminOnly ? 'Only admin will update currently playing and start calls' : 'All members will update currently playing and start calls'}
|
|
</span>
|
|
</span>
|
|
}
|
|
labelPlacement="end"
|
|
/>
|
|
<Button
|
|
size="small"
|
|
variant="contained"
|
|
color="secondary"
|
|
onClick={() => this.createOrJoin()}
|
|
>
|
|
<span>{mode === 'create' ? 'Start Party' : 'Join Party'}</span> <ChevronRight />
|
|
</Button>
|
|
</Paper>
|
|
<ExpansionPanel defaultExpanded className="create-settings-card" >
|
|
<ExpansionPanelSummary expandIcon={<ExpandLess />}>
|
|
<Notifications />
|
|
<Typography className="create-settings-title" variant="button">
|
|
Alerts
|
|
</Typography>
|
|
</ExpansionPanelSummary>
|
|
<ExpansionPanelDetails>
|
|
{status ?
|
|
<div className="alerts-stepper-container">
|
|
<Card className="alerts-stepper-card" elevation={10}>
|
|
<CardContent>
|
|
<Typography color="textSecondary" gutterBottom variant="overline">
|
|
{status.alerts[alertStep].title}
|
|
</Typography>
|
|
<Typography variant="body2" component="p">
|
|
{status.alerts[alertStep].description}
|
|
</Typography>
|
|
</CardContent>
|
|
{
|
|
status.alerts[alertStep].link &&
|
|
status.alerts[alertStep].linkText &&
|
|
<CardActions>
|
|
<Button href={status.alerts[alertStep].link} target="_blank" size="small">{status.alerts[alertStep].linkText}</Button>
|
|
</CardActions>
|
|
}
|
|
</Card>
|
|
<MobileStepper
|
|
className="alerts-stepper-controls"
|
|
variant="dots"
|
|
color="secondary"
|
|
activeStep={alertStep}
|
|
backButton={<IconButton disabled={alertStep === 0} onClick={() => this.handleSteps(-1)}> <KeyboardArrowLeft /> </IconButton>}
|
|
nextButton={<IconButton disabled={alertStep === (status.alerts.length - 1)} onClick={() => this.handleSteps(1)}> <KeyboardArrowRight /> </IconButton>}
|
|
steps={status.alerts ? status.alerts.length : 0}
|
|
/>
|
|
</div>
|
|
: (<Typography color="textSecondary" gutterBottom variant="overline">
|
|
No alerts right now
|
|
</Typography>)
|
|
}
|
|
</ExpansionPanelDetails>
|
|
</ExpansionPanel>
|
|
<ExpansionPanel className="create-settings-card" >
|
|
<ExpansionPanelSummary expandIcon={<ExpandLess />}>
|
|
<Settings />
|
|
<Typography className="create-settings-title" variant="button">
|
|
Settings
|
|
</Typography>
|
|
</ExpansionPanelSummary>
|
|
<ExpansionPanelDetails>
|
|
<FormControlLabel
|
|
className="notification-switch"
|
|
control={
|
|
<Switch
|
|
checked={allowChatNotification}
|
|
onChange={this.updateAllowChatNotification}
|
|
name="allowChatNotification"
|
|
/>
|
|
}
|
|
label={
|
|
<span>
|
|
Allow chat notification when side panel is closed
|
|
<br />
|
|
<span style={{ color: '#8a8a8a' }}>
|
|
{allowChatNotification ? 'Notifications Enabled' : 'Notifications Disabled'}
|
|
</span>
|
|
</span>
|
|
}
|
|
labelPlacement="end"
|
|
/>
|
|
<FormControlLabel
|
|
className="notification-switch"
|
|
control={
|
|
<Switch
|
|
checked={allowAutoRejoin}
|
|
onChange={this.updateAllowAutoRejoin}
|
|
name="allowAutoRejoin"
|
|
/>
|
|
}
|
|
label={
|
|
<span>
|
|
Allow Watch Party to auto rejoin (within 1 minute)
|
|
<br />
|
|
<span style={{ color: '#8a8a8a' }}>
|
|
{allowAutoRejoin ? 'Auto Rejoin Enabled' : 'Auto Rejoin Disabled'}
|
|
</span>
|
|
</span>
|
|
}
|
|
labelPlacement="end"
|
|
/>
|
|
<Button
|
|
fullWidth
|
|
variant="contained"
|
|
onClick={this.clearPartyHistory}
|
|
size="small"
|
|
>
|
|
CLEAR SAVED PARTY DETAILS
|
|
</Button>
|
|
</ExpansionPanelDetails>
|
|
</ExpansionPanel>
|
|
<Button
|
|
target="_blank"
|
|
href={`http://www.chromewatchparty.com/changes/v${this.props.appVersion}`}
|
|
fullWidth
|
|
color="secondary"
|
|
variant="contained"
|
|
size="small"
|
|
className="whats-new-button"
|
|
>
|
|
See what's new in V{this.props.appVersion}
|
|
</Button>
|
|
<div className="create-bottom-card">
|
|
<Button
|
|
color="secondary"
|
|
target="_blank"
|
|
startIcon={<InfoRounded />}
|
|
size="small"
|
|
href="https://www.chromewatchparty.com/welcome/#tips"
|
|
>
|
|
FAQ
|
|
</Button>
|
|
<Button
|
|
color="secondary"
|
|
target="_blank"
|
|
startIcon={<RateReviewRounded />}
|
|
size="small"
|
|
href="https://get.chromewatchparty.com"
|
|
>
|
|
Review
|
|
</Button>
|
|
<Button
|
|
color="secondary"
|
|
target="_blank"
|
|
startIcon={<MonetizationOnRounded />}
|
|
size="small"
|
|
href="https://paypal.me/Ayushya"
|
|
>
|
|
Donate
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|