mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-11 08:34:52 -07:00
Structure change (#4220)
* Structure change * Remove duplicate folders from previous structure * Cleanup websocket protocol * Updating from based off PR * Fixup - remove wrong files during conflict and get the websocket working * renaming tsx to ts Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
This commit is contained in:
parent
a0deb73df6
commit
1ddc9cc929
123 changed files with 424 additions and 228 deletions
30
webclient/src/components/Room/Games.css
Normal file
30
webclient/src/components/Room/Games.css
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
.games {
|
||||
}
|
||||
|
||||
.games-header,
|
||||
.game {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
.games-header__cell {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.games-header__label,
|
||||
.game__detail {
|
||||
width: 10%;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.games-header__label.description,
|
||||
.game__detail.description {
|
||||
width: 20%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.games-header__label.creator,
|
||||
.game__detail.creator {
|
||||
width: 20%;
|
||||
}
|
||||
143
webclient/src/components/Room/Games.tsx
Normal file
143
webclient/src/components/Room/Games.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import * as _ from "lodash";
|
||||
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableHead from "@material-ui/core/TableHead";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import TableSortLabel from "@material-ui/core/TableSortLabel";
|
||||
import Tooltip from "@material-ui/core/Tooltip";
|
||||
|
||||
// import { RoomsService } from "AppShell/common/services";
|
||||
|
||||
import { SortUtil, RoomsDispatch, RoomsSelectors } from "store";
|
||||
import { UserDisplay } from "components";
|
||||
|
||||
import "./Games.css";
|
||||
|
||||
// @TODO run interval to update timeSinceCreated
|
||||
class Games extends Component<GamesProps> {
|
||||
private headerCells = [
|
||||
{
|
||||
label: "Age",
|
||||
field: "startTime"
|
||||
},
|
||||
{
|
||||
label: "Description",
|
||||
field: "description"
|
||||
},
|
||||
{
|
||||
label: "Creator",
|
||||
field: "creatorInfo.name"
|
||||
},
|
||||
{
|
||||
label: "Type",
|
||||
field: "gameType"
|
||||
},
|
||||
{
|
||||
label: "Restrictions",
|
||||
// field: "?"
|
||||
},
|
||||
{
|
||||
label: "Players",
|
||||
// field: ["maxPlayers", "playerCount"]
|
||||
},
|
||||
{
|
||||
label: "Spectators",
|
||||
field: "spectatorsCount"
|
||||
},
|
||||
];
|
||||
|
||||
handleSort(sortByField) {
|
||||
const { room: { roomId }, sortBy } = this.props;
|
||||
const { field, order } = SortUtil.toggleSortBy(sortByField, sortBy);
|
||||
RoomsDispatch.sortGames(roomId, field, order);
|
||||
}
|
||||
|
||||
private isUnavailableGame({ started, maxPlayers, playerCount }) {
|
||||
return !started && playerCount < maxPlayers;
|
||||
}
|
||||
|
||||
private isPasswordProtectedGame({ withPassword }) {
|
||||
return !withPassword;
|
||||
}
|
||||
|
||||
private isBuddiesOnlyGame({ onlyBuddies }) {
|
||||
return !onlyBuddies;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { room, sortBy } = this.props;
|
||||
|
||||
const games = room.gameList.filter(game => (
|
||||
this.isUnavailableGame(game) &&
|
||||
this.isPasswordProtectedGame(game) &&
|
||||
this.isBuddiesOnlyGame(game)
|
||||
));
|
||||
|
||||
return (
|
||||
<div className="games">
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{ _.map(this.headerCells, ({ label, field }) => {
|
||||
const active = field === sortBy.field;
|
||||
const order = sortBy.order.toLowerCase();
|
||||
const sortDirection = active ? order : false;
|
||||
|
||||
return (
|
||||
<TableCell sortDirection={sortDirection} key={label}>
|
||||
{!field ? label : (
|
||||
<TableSortLabel
|
||||
active={active}
|
||||
direction={order}
|
||||
onClick={() => this.handleSort(field)}
|
||||
>
|
||||
{label}
|
||||
</TableSortLabel>
|
||||
)}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{ _.map(games, ({ description, gameId, gameType, creatorInfo, maxPlayers, playerCount, spectatorsCount, startTime }) => (
|
||||
<TableRow key={gameId}>
|
||||
<TableCell className="games-header__cell single-line-ellipsis">{startTime}</TableCell>
|
||||
<TableCell className="games-header__cell">
|
||||
<Tooltip title={description} placement="bottom-start" enterDelay={500}>
|
||||
<div className="single-line-ellipsis">
|
||||
{description}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell className="games-header__cell">
|
||||
<UserDisplay user={ creatorInfo } />
|
||||
</TableCell>
|
||||
<TableCell className="games-header__cell single-line-ellipsis">{gameType}</TableCell>
|
||||
<TableCell className="games-header__cell single-line-ellipsis">?</TableCell>
|
||||
<TableCell className="games-header__cell single-line-ellipsis">{`${playerCount}/${maxPlayers}`}</TableCell>
|
||||
<TableCell className="games-header__cell single-line-ellipsis">{spectatorsCount}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface GamesProps {
|
||||
room: any;
|
||||
sortBy: any;
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
sortBy: RoomsSelectors.getSortGamesBy(state)
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(Games);
|
||||
17
webclient/src/components/Room/Messages.css
Normal file
17
webclient/src/components/Room/Messages.css
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.messages {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 5px 0;
|
||||
margin: 2px 0;
|
||||
border-bottom: 1px dashed rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.message:last-of-type {
|
||||
border: 0;
|
||||
}
|
||||
31
webclient/src/components/Room/Messages.tsx
Normal file
31
webclient/src/components/Room/Messages.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// eslint-disable-next-line
|
||||
import React from "react";
|
||||
|
||||
import "./Messages.css";
|
||||
|
||||
const Messages = ({ messages }) => (
|
||||
<div className="messages">
|
||||
{
|
||||
messages && messages.map(({ message, messageType, timeOf, timeReceived }) => (
|
||||
<div className="message" key={timeReceived}>
|
||||
<div className="message__detail">{ParsedMessage(message)}</div>
|
||||
</div>
|
||||
) )
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
const ParsedMessage = (message) => {
|
||||
const name = message.match("^[^:]+:");
|
||||
|
||||
if (name && name.length) {
|
||||
message = message.slice(name[0].length, message.length);
|
||||
}
|
||||
|
||||
return <div>
|
||||
<strong>{name}</strong>
|
||||
{message}
|
||||
</div>
|
||||
};
|
||||
|
||||
export default Messages;
|
||||
39
webclient/src/components/Room/Room.css
Normal file
39
webclient/src/components/Room/Room.css
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
.room-view,
|
||||
.room-view__games,
|
||||
.room-view__messages,
|
||||
.room-view__messages-content,
|
||||
.room-view__side {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.room-view__messages,
|
||||
.room-view__side {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.room-view__messages-sayMessage {
|
||||
width: 100%;
|
||||
margin: 10px auto 2px;
|
||||
}
|
||||
|
||||
.room-view__side-label {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding: 10px;
|
||||
background: white;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.room-view__side-list,
|
||||
.room-view__side-list .room-view__side-list__item {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.room-view__side-list .room-view__side-list__item {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.room-view__side-list .room-view__side-list__item .user-display__details {
|
||||
padding: 0 10px;
|
||||
}
|
||||
99
webclient/src/components/Room/Room.tsx
Normal file
99
webclient/src/components/Room/Room.tsx
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter /*, RouteComponentProps */ } from "react-router-dom";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
import { RoomsStateMessages, RoomsStateRooms, RoomsSelectors } from "store";
|
||||
import { RoomsService } from "websocket";
|
||||
import { ScrollToBottomOnChanges, ThreePaneLayout, UserDisplay, VirtualList, AuthGuard} from "components";
|
||||
|
||||
|
||||
|
||||
import Games from "./Games";
|
||||
import Messages from "./Messages";
|
||||
import SayMessage from "./SayMessage";
|
||||
|
||||
import "./Room.css";
|
||||
|
||||
// @TODO (3)
|
||||
class Room extends Component<any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleRoomSay = this.handleRoomSay.bind(this);
|
||||
}
|
||||
|
||||
handleRoomSay({ message }) {
|
||||
if (message) {
|
||||
const { roomId } = this.props.match.params;
|
||||
RoomsService.roomSay(roomId, message);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match, rooms} = this.props;
|
||||
const { roomId } = match.params;
|
||||
const room = rooms[roomId];
|
||||
|
||||
const messages = this.props.messages[roomId];
|
||||
const users = room.userList;
|
||||
|
||||
return (
|
||||
<div className="room-view">
|
||||
<AuthGuard />
|
||||
<ThreePaneLayout
|
||||
fixedHeight
|
||||
|
||||
top={(
|
||||
<Paper className="room-view__games overflow-scroll">
|
||||
<Games room={room} />
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
bottom={(
|
||||
<div className="room-view__messages">
|
||||
<Paper className="room-view__messages-content overflow-scroll">
|
||||
<ScrollToBottomOnChanges changes={messages} content={(
|
||||
<Messages messages={messages} />
|
||||
)} />
|
||||
</Paper>
|
||||
<Paper className="room-view__messages-sayMessage">
|
||||
<SayMessage onSubmit={this.handleRoomSay} />
|
||||
</Paper>
|
||||
</div>
|
||||
)}
|
||||
|
||||
side={(
|
||||
<Paper className="room-view__side overflow-scroll">
|
||||
<div className="room-view__side-label">
|
||||
Users in this room: {users.length}
|
||||
</div>
|
||||
<VirtualList
|
||||
className="room-view__side-list"
|
||||
itemKey={(index, data) => users[index].name }
|
||||
items={ users.map(user => (
|
||||
<ListItem button className="room-view__side-list__item">
|
||||
<UserDisplay user={user} />
|
||||
</ListItem>
|
||||
) ) }
|
||||
/>
|
||||
</Paper>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface RoomProps {
|
||||
messages: RoomsStateMessages;
|
||||
rooms: RoomsStateRooms;
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
messages: RoomsSelectors.getMessages(state),
|
||||
rooms: RoomsSelectors.getRooms(state)
|
||||
});
|
||||
|
||||
export default withRouter(connect(mapStateToProps)(Room));
|
||||
18
webclient/src/components/Room/SayMessage.tsx
Normal file
18
webclient/src/components/Room/SayMessage.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// eslint-disable-next-line
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Form, reduxForm } from "redux-form"
|
||||
|
||||
import { InputAction } from 'components';
|
||||
|
||||
const SayMessage = ({ handleSubmit }) => (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<InputAction action="Say" label="Chat" name="message" />
|
||||
</Form>
|
||||
);
|
||||
|
||||
const propsMap = {
|
||||
form: "sayMessage"
|
||||
};
|
||||
|
||||
export default connect()(reduxForm(propsMap)(SayMessage));
|
||||
Loading…
Add table
Add a link
Reference in a new issue