diff --git a/packages/server/play-node-go/src/components/Button/Game/Game.js b/packages/server/play-node-go/src/components/Button/Game/Game.js index 00df7a3..e0258ca 100644 --- a/packages/server/play-node-go/src/components/Button/Game/Game.js +++ b/packages/server/play-node-go/src/components/Button/Game/Game.js @@ -3,12 +3,53 @@ import { Link } from 'react-router-dom'; import './Game.scss'; const GameButton = (props) => { - const gameData = props.game; + const { game, dispatch } = props; + + const requestJoinGame = () => { + console.log(`request to Join Game ${game.id}!`) + const requestAction = { + type: 'GAMES', + message: 'JOIN_REQUEST', + body: {id: game.id} + } + dispatch(requestAction); + } + + const renderOpenGame = () => { + return ( + <> + requestJoinGame()} >Request to Join Game + +
+ {game.playerBlack} + {game.playerBlackRank} +
+ + ) + } + + const renderInProgressGame = () => { + const gameLinkText = game.winType ? 'Study Game' : 'Watch Game' + return ( + <> + {gameLinkText} + +
+ {game.playerBlack} + {game.playerBlackRank} +
+ +
+ {game.playerWhite} + {game.playerWhiteRank} +
+ + ) + } + return (
- View Game -

{gameData.playerBlack} - {gameData.playerBlackRank}

-

{gameData.playerWhite} - {gameData.playerWhiteRank}

+ {game.open ? renderOpenGame() : renderInProgressGame()}
); } diff --git a/packages/server/play-node-go/src/components/Button/Game/Game.scss b/packages/server/play-node-go/src/components/Button/Game/Game.scss index e69de29..f0b722c 100644 --- a/packages/server/play-node-go/src/components/Button/Game/Game.scss +++ b/packages/server/play-node-go/src/components/Button/Game/Game.scss @@ -0,0 +1,5 @@ +div.Game__playerData { + width: 100%; + display: flex; + justify-content: space-between; +} \ No newline at end of file diff --git a/packages/server/play-node-go/src/components/Error/ActionError/ActionError.js b/packages/server/play-node-go/src/components/Error/ActionError/ActionError.js new file mode 100644 index 0000000..1d73746 --- /dev/null +++ b/packages/server/play-node-go/src/components/Error/ActionError/ActionError.js @@ -0,0 +1,13 @@ +import React from 'react'; +import './ActionError.scss'; + +const ActionError = (props) => { + const errorMessage = props.error; + return ( + + {errorMessage} + + ); +} + +export default ActionError; \ No newline at end of file diff --git a/packages/server/play-node-go/src/components/Error/ActionError/ActionError.scss b/packages/server/play-node-go/src/components/Error/ActionError/ActionError.scss new file mode 100644 index 0000000..6ae3f66 --- /dev/null +++ b/packages/server/play-node-go/src/components/Error/ActionError/ActionError.scss @@ -0,0 +1,5 @@ +@import '../../../../public/stylesheets/partials/variables'; + +span.FormError { + color: map-get($colors, "error");; +} \ No newline at end of file diff --git a/packages/server/play-node-go/src/components/Error/ActionError/ActionError.test.js b/packages/server/play-node-go/src/components/Error/ActionError/ActionError.test.js new file mode 100644 index 0000000..49e9c14 --- /dev/null +++ b/packages/server/play-node-go/src/components/Error/ActionError/ActionError.test.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import ActionError from './ActionError'; + +test('renders ActionError without crashing', () => { + const { getByTestId } = render(); + const ActionErrorSpan = getByTestId('ActionError'); + expect(ActionErrorSpan).toBeInTheDocument(); +}); + +test('renders ActionError with error message', () => { + const errorMessage = "User already exists!"; + const { getByTestId } = render(); + const ActionErrorSpan = getByTestId('ActionError'); + expect(ActionErrorSpan).toHaveTextContent(errorMessage); + +}) diff --git a/packages/server/play-node-go/src/pages/Room/Room.js b/packages/server/play-node-go/src/pages/Room/Room.js index c8d8c3c..421bbbf 100644 --- a/packages/server/play-node-go/src/pages/Room/Room.js +++ b/packages/server/play-node-go/src/pages/Room/Room.js @@ -6,6 +6,7 @@ import config from '../../config'; import roomsServices from '../../services/api/roomsServices'; import GameButton from '../../components/Button/Game/Game'; import Message from '../../components/Display/Message/Message'; +import ActionError from '../../components/Error/ActionError/ActionError'; import Development from '../../components/Display/Development/Development'; @@ -13,8 +14,7 @@ const Room = (props) => { const state = props.state; const dispatch = props.dispatch; const roomId = parseInt(useParams().id) || 0; - const [ socketData, setSocketData ] = useState(); - const [ messages, setMessages ] = useState(); + const [ socketData, setSocketData ] = useState(false); const fetchRoomAPI = async () => { const response = await roomsServices.getRoomService(roomId); @@ -38,15 +38,36 @@ const Room = (props) => { const roomSocketConnect = () => { roomSocket.emit('connect'); // ! dispatch data - roomSocket.on('connected', data => setSocketData('room socket connected')); - roomSocket.on('connect_error', err => console.log(err)); - roomSocket.on('error', err => console.log(err)); + roomSocket.on('connect', socket => { + setSocketData(true) + }); + roomSocket.on('join_game_request', data => { + console.log(data) + }) + roomSocket.on('connect_error', err => { + setSocketData(false) + console.log(err); + }); + roomSocket.on('error', err => { + setSocketData(false) + console.log(err); + }); } useEffect(() => { roomSocketConnect(); }, []) + useEffect(() => { + const data = { + user: state.user, + game: state.joinGame + }; + console.log('emitting request') + console.log(data) + roomSocket.emit('join_game_request', data) + }, [state.joinGame]) + // ! [end] const renderGames = () => { @@ -56,6 +77,7 @@ const Room = (props) => { )) } @@ -78,7 +100,11 @@ const Room = (props) => { return (
-

{state.currentRoom ? state.currentRoom.name : 'Loading'}

+
+

{state.currentRoom ? state.currentRoom.name : 'Loading'}

+ {socketData ? '✓' : ' ⃠'} + {state.errors.joinGame ? : <>} +
{renderGames()} diff --git a/packages/server/play-node-go/src/reducers/err/stateReducer.err.js b/packages/server/play-node-go/src/reducers/err/stateReducer.err.js index 876d1c4..d563dd4 100644 --- a/packages/server/play-node-go/src/reducers/err/stateReducer.err.js +++ b/packages/server/play-node-go/src/reducers/err/stateReducer.err.js @@ -8,6 +8,9 @@ export const errorReducer = (state: state, action: action):state => { case 'JOIN_ROOM_ERROR': return joinRoomErrorReducer(state, action); + + case 'JOIN_GAME_ERROR': + return joinGameErrorReducer(state, action); default: return state; @@ -22,4 +25,9 @@ function authErrorReducer(state: state, action: action): state { function joinRoomErrorReducer(state: state, action: action): state { const joinRoom = action.body.joinRoomError; return { ...state, errors: {joinRoom} } +} + +function joinGameErrorReducer(state: state, action: action): state { + const joinGame = action.body.joinGameError; + return { ...state, errors: {joinGame} } } \ No newline at end of file diff --git a/packages/server/play-node-go/src/reducers/games/stateReducer.games.js b/packages/server/play-node-go/src/reducers/games/stateReducer.games.js index 47d462e..3427d4d 100644 --- a/packages/server/play-node-go/src/reducers/games/stateReducer.games.js +++ b/packages/server/play-node-go/src/reducers/games/stateReducer.games.js @@ -6,11 +6,51 @@ export const gamesReducer = (state: state, action: action):state => { switch(action.message) { case 'SET_GAMES': - const games = action.body; + const games = formatGames(action);; return {...state, games}; + + case 'JOIN_REQUEST': + if (!Object.entries(state.user).length) { + const errAction = { + type: 'ERR', + message: 'JOIN_GAME_ERROR', + body: {joinGameError: 'user not logged in'} + } + return stateReducer(state, errAction) + } + const id = action.body; + return {...state, joinGame: id}; default: return state; } +} + +function parseRank(rank: string): string { + switch(rank[0]) { + case 'D': + return `${rank.slice(1)}${rank[0].toLowerCase()}` + case 'K': + return `${rank.slice(1)}${rank[0].toLowerCase()}` + case 'U': + return '?' + } +} + +function formatGames(action: action): Array<{}> { + const games = [...action.body].map(game => { + + if (game.playerBlackRank) { + game.playerBlackRank = parseRank(game.playerBlackRank) + } + + if (game.playerWhiteRank) { + game.playerWhiteRank = parseRank(game.playerWhiteRank) + } + + return game; + }) + + return games; } \ No newline at end of file diff --git a/packages/server/play-node-go/src/reducers/init/stateReducer.init.js b/packages/server/play-node-go/src/reducers/init/stateReducer.init.js index 1780d36..60d3e01 100644 --- a/packages/server/play-node-go/src/reducers/init/stateReducer.init.js +++ b/packages/server/play-node-go/src/reducers/init/stateReducer.init.js @@ -5,6 +5,10 @@ import type { state } from '../stateReducer'; export const initState = (): state => { return { user: {}, - errors: {} + errors: {}, + currentRoom: {}, + messages: {}, + games: {}, + joinGame: {} }; } \ No newline at end of file diff --git a/packages/server/play-node-go/src/services/api/roomsServices.js b/packages/server/play-node-go/src/services/api/roomsServices.js index 92ca915..89d78f6 100644 --- a/packages/server/play-node-go/src/services/api/roomsServices.js +++ b/packages/server/play-node-go/src/services/api/roomsServices.js @@ -32,6 +32,7 @@ const getRoomService = async (roomIndex) => { delete Object.assign(game, {playerBlackRank: game.player_black_rank }).player_black_rank; delete Object.assign(game, {playerWhite: game.player_white }).player_white; delete Object.assign(game, {playerWhiteRank: game.player_white_rank }).player_white_rank; + delete Object.assign(game, {winType: game.win_type }).win_type; return game; }) return obj; diff --git a/packages/server/server/controllers/api/apiRoom.js b/packages/server/server/controllers/api/apiRoom.js index 74d8195..7a108e4 100644 --- a/packages/server/server/controllers/api/apiRoom.js +++ b/packages/server/server/controllers/api/apiRoom.js @@ -18,6 +18,8 @@ const getAll = async (req, res, next) => { const show = async (req, res, next) => { try { const roomId = req.params.id; + if (!roomId) throw('missing room parameter') + // TODO eventually add check for user's private rooms enableRoomSocket(roomId); diff --git a/packages/server/server/socket.js b/packages/server/server/socket.js index f5b3195..579423a 100644 --- a/packages/server/server/socket.js +++ b/packages/server/server/socket.js @@ -11,12 +11,18 @@ io.on('connection', ()=> { enableRoomSocket = (roomId) => { const roomSocket = io.of(roomId); roomSocket.on('connection', (socket) => { - // socket.emit('connected'); - console.log(`Socket connected at room ${roomId}`); + + //! Join Game Request queries db for game, ensures unique player joining socket.on('join_game_request', async data => { const gameRequest = await logJoinGameRequest(data); + + if (gameRequest.err) { + roomSocket.emit('join_game_request_error', gameRequest.err); + } + roomSocket.emit('join_game_request', gameRequest); - }) + }); + }); return roomSocket; } @@ -29,5 +35,12 @@ module.exports = { async function logJoinGameRequest (data) { const {user, game} = data; const requestedGame = await gameQueries.findGameById(game.id); - return { user, requestedGame } + + if (requestedGame.user_black === user.id) { + return { err: 'players must be unique' } + } + + const requestingUser = {...user}; + delete requestingUser.email; + return { requestingUser, requestedGame } } \ No newline at end of file