Webatrice updates (#4366)

This commit is contained in:
Jeremy Letto 2021-05-21 20:23:30 -05:00 committed by GitHub
parent 8db9475804
commit 0d05f9097d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 5517 additions and 20090 deletions

25253
webclient/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -3,11 +3,12 @@
"version": "1.0.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.6.1",
"@material-ui/core": "^4.11.4",
"@material-ui/icons": "^4.11.2",
"@types/jest": "24.0.20",
"@types/jquery": "^3.3.31",
"@types/lodash": "^4.14.145",
"@types/material-ui": "^0.21.7",
"@types/material-ui": "^0.21.8",
"@types/node": "12.11.7",
"@types/prop-types": "^15.7.3",
"@types/protobufjs": "^6.0.0",

View file

@ -14,7 +14,9 @@ export default class AuthenticationService {
}
static isModerator(user) {
return user.userLevel >= webClient.pb.ServerInfo_User.UserLevelFlag.IsModerator;
const moderatorLevel = webClient.pb.ServerInfo_User.UserLevelFlag.IsModerator;
// @TODO tell cockatrice not to do this so shittily
return (user.userLevel & moderatorLevel) === moderatorLevel;
}
static isAdmin() {

View file

@ -1,4 +1,4 @@
import React, { Component } from "react";
import React from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
@ -7,12 +7,10 @@ import { RouteEnum } from "types";
import { AuthenticationService } from "api";
class AuthGuard extends Component<AuthGuardProps> {
render() {
return !AuthenticationService.isConnected(this.props.state)
? <Redirect from="*" to={RouteEnum.SERVER} />
: "";
}
const AuthGuard = ({ state }: AuthGuardProps) => {
return !AuthenticationService.isConnected(state)
? <Redirect from="*" to={RouteEnum.SERVER} />
: <div></div>;
};
interface AuthGuardProps {

View file

@ -25,13 +25,13 @@
width: 100%;
display: flex;
align-items: center;
margin-right: 10px;
}
.Header-nav__items {
width: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
}
.Header-nav__item {
@ -39,9 +39,13 @@
margin: 0 10px;
}
.Header-nav__menu {
margin-left: 10px;
}
.Header-account {
display: flex;
align-items: center;
display: flex;
align-items: center;
}
.Header-account__name {

View file

@ -3,7 +3,11 @@ import { connect } from "react-redux";
import { NavLink, withRouter, generatePath } from "react-router-dom";
import AppBar from "@material-ui/core/AppBar";
import Chip from "@material-ui/core/Chip";
import IconButton from "@material-ui/core/IconButton";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";
import Toolbar from "@material-ui/core/Toolbar";
import MenuRoundedIcon from '@material-ui/icons/MenuRounded';
import * as _ from "lodash";
import { AuthenticationService, RoomsService } from "api";
@ -14,6 +18,24 @@ import "./Header.css";
import logo from "./logo.png";
class Header extends Component<HeaderProps> {
state: HeaderState;
options: string[] = [
'Decks',
'Replays',
];
constructor(props) {
super(props);
this.state = {
anchorEl: null,
};
this.handleMenuClick = this.handleMenuClick.bind(this);
this.handleMenuItemClick = this.handleMenuItemClick.bind(this);
this.handleMenuClose = this.handleMenuClose.bind(this);
}
componentDidUpdate(prevProps) {
const currentRooms = this.props.joinedRooms;
const previousRooms = prevProps.joinedRooms;
@ -24,8 +46,30 @@ class Header extends Component<HeaderProps> {
}
}
handleMenuClick({ target }) {
this.setState({ anchorEl: target });
}
handleMenuItemClick(option: string) {
const route = RouteEnum[option.toUpperCase()];
this.props.history.push(generatePath(route));
this.handleMenuClose();
}
handleMenuClose() {
this.setState({ anchorEl: null });
}
render() {
const { joinedRooms, server, state, user } = this.props;
const anchorEl = this.state.anchorEl;
const options = [ ...this.options ];
if (user && AuthenticationService.isModerator(user)) {
options.push('Administration');
options.push('Logs');
}
return (
<div>
@ -39,20 +83,6 @@ class Header extends Component<HeaderProps> {
<div className="Header-content">
<nav className="Header-nav">
<ul className="Header-nav__items">
{
AuthenticationService.isModerator(user) && (
<li className="Header-nav__item">
<NavLink to={RouteEnum.LOGS}>
<button>Logs</button>
</NavLink>
</li>
)
}
<li className="Header-nav__item">
<NavLink to={RouteEnum.SERVER} className="plain-link">
Server ({server})
</NavLink>
</li>
<NavLink to={RouteEnum.ACCOUNT} className="plain-link">
<div className="Header-account">
<span className="Header-account__name">
@ -61,6 +91,35 @@ class Header extends Component<HeaderProps> {
<span className="Header-account__indicator"></span>
</div>
</NavLink>
<div className="Header-nav__menu">
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={this.handleMenuClick}
>
<MenuRoundedIcon fontSize="large" />
</IconButton>
<Menu
id="long-menu"
anchorEl={anchorEl}
keepMounted
open={!!anchorEl}
onClose={this.handleMenuClose}
PaperProps={{
style: {
marginTop: '53px',
width: '20ch',
},
}}
>
{options.map((option) => (
<MenuItem key={option} onClick={() => this.handleMenuItemClick(option)}>
{option}
</MenuItem>
))}
</Menu>
</div>
</ul>
</nav>
</div>
@ -113,11 +172,15 @@ interface HeaderProps {
history: any;
}
interface HeaderState {
anchorEl: Element;
}
const mapStateToProps = state => ({
state: ServerSelectors.getState(state),
server: ServerSelectors.getName(state),
user: ServerSelectors.getUser(state),
joinedRooms: RoomsSelectors.getJoinedRooms(state)
joinedRooms: RoomsSelectors.getJoinedRooms(state),
});
export default withRouter(connect(mapStateToProps)(Header));

View file

@ -4,6 +4,7 @@ import { connect } from "react-redux";
import { Form, reduxForm } from "redux-form"
import { InputAction } from 'components';
import { FormKey } from 'types';
const AddToBuddies = ({ handleSubmit }) => (
<Form onSubmit={handleSubmit}>
@ -12,7 +13,7 @@ const AddToBuddies = ({ handleSubmit }) => (
);
const propsMap = {
form: "addToBuddies"
form: FormKey.ADD_TO_BUDDIES
};
export default connect()(reduxForm(propsMap)(AddToBuddies));

View file

@ -4,6 +4,7 @@ import { connect } from "react-redux";
import { Form, reduxForm } from "redux-form"
import { InputAction } from 'components';
import { FormKey } from 'types';
const AddToIgnore = ({ handleSubmit }) => (
<Form onSubmit={handleSubmit}>
@ -12,7 +13,7 @@ const AddToIgnore = ({ handleSubmit }) => (
);
const propsMap = {
form: "addToIgnore"
form: FormKey.ADD_TO_IGNORE,
};
export default connect()(reduxForm(propsMap)(AddToIgnore));

View file

@ -1,39 +1,37 @@
// eslint-disable-next-line
import React, { Component } from "react";
import { connect } from "react-redux";
import { Form, Field, InjectedFormProps, reduxForm } from "redux-form"
import { Form, Field, reduxForm } from "redux-form"
import Button from "@material-ui/core/Button";
import { InputField } from "../../components";
import { InputField } from "components";
import { FormKey } from 'types';
import "./ConnectForm.css";
class ConnectForm extends Component<InjectedFormProps> {
render() {
return (
<Form className="connectForm" onSubmit={this.props.handleSubmit}>
<div className="connectForm-item">
<Field label="Host" name="host" component={InputField} />
</div>
<div className="connectForm-item">
<Field label="Port" name="port" component={InputField} />
</div>
<div className="connectForm-item">
<Field label="User" name="user" component={InputField} autoComplete="username" />
</div>
<div className="connectForm-item">
<Field label="Pass" name="pass" type="password" component={InputField} autoComplete="current-password" />
</div>
<Button className="connectForm-submit" color="primary" variant="contained" type="submit">
Connect
</Button>
</Form>
);
}
}
const ConnectForm = ({ handleSubmit }) => (
<Form className="connectForm" onSubmit={handleSubmit}>
<div className="connectForm-item">
<Field label="Host" name="host" component={InputField} />
</div>
<div className="connectForm-item">
<Field label="Port" name="port" component={InputField} />
</div>
<div className="connectForm-item">
<Field label="User" name="user" component={InputField} autoComplete="username" />
</div>
<div className="connectForm-item">
<Field label="Pass" name="pass" type="password" component={InputField} autoComplete="current-password" />
</div>
<Button className="connectForm-submit" color="primary" variant="contained" type="submit">
Connect
</Button>
</Form>
);
const propsMap = {
form: "connect"
form: FormKey.CONNECT
};
const mapStateToProps = () => ({

View file

@ -1,52 +1,49 @@
// eslint-disable-next-line
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Form, Field, InjectedFormProps, reduxForm } from 'redux-form'
import { Form, Field, reduxForm } from 'redux-form'
import Button from '@material-ui/core/Button';
import { InputField } from '../../components';
import { InputField } from 'components';
import { FormKey } from 'types';
import './RegisterForm.css';
class RegisterForm extends Component<InjectedFormProps> {
render() {
return (
<Form className="registerForm" onSubmit={this.props.handleSubmit} autoComplete="off">
<div className="registerForm-item">
<Field label="Host" name="host" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Port" name="port" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Player Name" name="userName" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Password" name="password" type="password" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Password (again)" name="passwordConfirm" type="password" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Email" name="email" type="email" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Email (again)" name="emailConfirm" type="email" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Real Name" name="realName" component={InputField} />
</div>
<Button className="registerForm-submit" color="primary" variant="contained" type="submit">
Register
</Button>
</Form>
);
}
}
const RegisterForm = ({ handleSubmit }) => (
<Form className="registerForm" onSubmit={handleSubmit} autoComplete="off">
<div className="registerForm-item">
<Field label="Host" name="host" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Port" name="port" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Player Name" name="userName" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Password" name="password" type="password" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Password (again)" name="passwordConfirm" type="password" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Email" name="email" type="email" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Email (again)" name="emailConfirm" type="email" component={InputField} />
</div>
<div className="registerForm-item">
<Field label="Real Name" name="realName" component={InputField} />
</div>
<Button className="registerForm-submit" color="primary" variant="contained" type="submit">
Register
</Button>
</Form>
);
const propsMap = {
form: 'register'
form: FormKey.REGISTER,
};
const mapStateToProps = () => ({

View file

@ -1,62 +1,59 @@
// eslint-disable-next-line
import React, { Component } from "react";
import { connect } from "react-redux";
import { Form, Field, InjectedFormProps, reduxForm } from "redux-form"
import { Form, Field, reduxForm } from "redux-form"
import Button from "@material-ui/core/Button";
import Divider from "@material-ui/core/Divider";
import Paper from "@material-ui/core/Paper";
import { InputField, CheckboxField } from "../../components";
import { InputField, CheckboxField } from "components";
import { FormKey } from "types";
import "./SearchForm.css";
class SearchForm extends Component<InjectedFormProps> {
render() {
return (
<Paper className="log-search">
<Form className="log-search__form" onSubmit={this.props.handleSubmit}>
<div className="log-search__form-item">
<Field label="Username" name="userName" component={InputField} />
</div>
<div className="log-search__form-item">
<Field label="IP Address" name="ipAddress" component={InputField} />
</div>
<div className="log-search__form-item">
<Field label="Game Name" name="gameName" component={InputField} />
</div>
<div className="log-search__form-item">
<Field label="GameID" name="gameId" component={InputField} />
</div>
<div className="log-search__form-item">
<Field label="Message" name="message" component={InputField} />
</div>
<Divider />
<div className="log-search__form-item log-location">
<Field label="Rooms" name="logLocation.room" component={CheckboxField} />
<Field label="Games" name="logLocation.game" component={CheckboxField} />
<Field label="Chats" name="logLocation.chat" component={CheckboxField} />
</div>
<Divider />
<div className="log-search__form-item">
<span>Date Range: Coming Soon</span>
</div>
<Divider />
<div className="log-search__form-item">
<span>Maximum Results: 1000</span>
</div>
<Divider />
<Button className="log-search__form-submit" color="primary" variant="contained" type="submit">
Search Logs
</Button>
</Form>
</Paper>
)
}
}
const SearchForm = ({ handleSubmit }) => (
<Paper className="log-search">
<Form className="log-search__form" onSubmit={handleSubmit}>
<div className="log-search__form-item">
<Field label="Username" name="userName" component={InputField} />
</div>
<div className="log-search__form-item">
<Field label="IP Address" name="ipAddress" component={InputField} />
</div>
<div className="log-search__form-item">
<Field label="Game Name" name="gameName" component={InputField} />
</div>
<div className="log-search__form-item">
<Field label="GameID" name="gameId" component={InputField} />
</div>
<div className="log-search__form-item">
<Field label="Message" name="message" component={InputField} />
</div>
<Divider />
<div className="log-search__form-item log-location">
<Field label="Rooms" name="logLocation.room" component={CheckboxField} />
<Field label="Games" name="logLocation.game" component={CheckboxField} />
<Field label="Chats" name="logLocation.chat" component={CheckboxField} />
</div>
<Divider />
<div className="log-search__form-item">
<span>Date Range: Coming Soon</span>
</div>
<Divider />
<div className="log-search__form-item">
<span>Maximum Results: 1000</span>
</div>
<Divider />
<Button className="log-search__form-submit" color="primary" variant="contained" type="submit">
Search Logs
</Button>
</Form>
</Paper>
);
const propsMap = {
form: "logs"
form: FormKey.SEARCH_LOGS,
};
const mapStateToProps = () => ({
@ -64,4 +61,3 @@ const mapStateToProps = () => ({
});
export default connect(mapStateToProps)(reduxForm(propsMap)(SearchForm));

View file

@ -1,4 +1,3 @@
export { default as ConnectForm } from './ConnectForm/ConnectForm';
export { default as RegisterForm } from './RegisterForm/RegisterForm';
export { default as SearchForm } from './SearchForm/SearchForm';

View file

@ -0,0 +1,7 @@
export enum FormKey {
ADD_TO_BUDDIES = "ADD_TO_BUDDIES",
ADD_TO_IGNORE = "ADD_TO_IGNORE",
CONNECT = "CONNECT",
REGISTER = "REGISTER",
SEARCH_LOGS = "SEARCH_LOGS",
}

View file

@ -5,3 +5,4 @@ export * from "./sort";
export * from "./user";
export * from "./routes";
export * from "./sort";
export * from "./forms";