From 4959e197bb632d9701b4345decb4146a77baf93c Mon Sep 17 00:00:00 2001 From: DTieman Date: Sun, 21 Apr 2024 16:28:55 +0200 Subject: [PATCH] Mostly design update + - Chat cleaning - Player hand size visibility --- src/layout/components/Chat.tsx | 31 ++++ src/layout/components/Game.tsx | 58 +++++--- src/layout/components/Lobby.tsx | 9 ++ src/layout/pages/Room.tsx | 133 +++++++++--------- src/styles/App.scss | 3 +- src/styles/layout/components/_components.scss | 2 + src/styles/layout/components/chat.scss | 28 ++++ src/styles/layout/components/game.scss | 25 ++++ src/styles/layout/components/hand.scss | 5 +- src/styles/layout/pages/_pages.scss | 3 +- src/styles/layout/pages/room.scss | 12 ++ src/styles/utils/_utils.scss | 3 +- src/styles/utils/theme.scss | 8 -- 13 files changed, 222 insertions(+), 98 deletions(-) create mode 100644 src/layout/components/Chat.tsx create mode 100644 src/layout/components/Lobby.tsx create mode 100644 src/styles/layout/components/chat.scss create mode 100644 src/styles/layout/components/game.scss create mode 100644 src/styles/layout/pages/room.scss delete mode 100644 src/styles/utils/theme.scss diff --git a/src/layout/components/Chat.tsx b/src/layout/components/Chat.tsx new file mode 100644 index 0000000..bc5182c --- /dev/null +++ b/src/layout/components/Chat.tsx @@ -0,0 +1,31 @@ +import {GHButton} from "./Button"; +import React, {FormEvent, FunctionComponent, RefObject} from "react"; + +interface Props{ + chatRef: RefObject; + handleSend: (message: string) => void; +} + +const Chat: FunctionComponent = ({chatRef, handleSend}) => { + + const handleChat = (form: FormEvent) => { + form.preventDefault(); + const data = new FormData(form.currentTarget); + const chatInput = data.get("chat-input") as string; + if(!chatInput) return; + handleSend(chatInput); + form.currentTarget.reset(); + } + + return ( + <> +
    +
    + + Send +
    + + ) +} + +export default Chat; \ No newline at end of file diff --git a/src/layout/components/Game.tsx b/src/layout/components/Game.tsx index cde4e88..206dcb1 100644 --- a/src/layout/components/Game.tsx +++ b/src/layout/components/Game.tsx @@ -2,7 +2,7 @@ import React, {FunctionComponent} from "react"; import Deck from "./Deck"; import Hand from "./Hand"; import {GHButton} from "./Button"; -import {Player} from "../pages/Room"; +import {GameAction, Player} from "../pages/Room"; export interface GameState { Me: Player; @@ -15,16 +15,52 @@ export interface GameState { interface Props { gameState: GameState - handleCardSend: (cardString: string) => void; - handleDraw: () => void; - handleChoice: (choice: string) => void; + handleGameAction: (action: GameAction) => void; } const CHOICES = ['SPADES', 'HEARTS', 'DIAMONDS', 'CLUBS']; -const Game: FunctionComponent = ({gameState, handleCardSend, handleDraw, handleChoice}) => { +const Game: FunctionComponent = ({gameState, handleGameAction}) => { + + const handleChoice = (choice: string) => { + handleGameAction({Action: 'CHOOSE', Data: choice}); + } + + const handleDraw = () => { + handleGameAction({Action: 'DRAW', Data: ""}); + } + + const handleCardSend = (cardString: string) => { + handleGameAction({Action: "PLAYCARD", Data: JSON.stringify({ + CardType: cardString.split(' ')[0], + CardValue: cardString.split(' ')[1] + })}) + } + return (
    +
      + { + gameState?.Me?.Id && + gameState.Players.map((player) => { + const isCurrentPlayer = player.Id === gameState.CurrentPlayer.Id; + const isMe = player.Id === gameState.Me.Id; + + const style = + { + fontWeight: isMe ? 'bold' : 'normal', + outline: isCurrentPlayer ? '2px solid red' : 'none' + } + + return
    1. +
      +

      {player.Name} {isMe && '(You)'}

      +

      {player.CardsLeft} left

      +
      +
    2. + }) + } +
    { gameState.CurrentCard && @@ -37,18 +73,6 @@ const Game: FunctionComponent = ({gameState, handleCardSend, handleDraw, gameState.CurrentState === 'CHOOSE' && CHOICES.map(choice => handleChoice(choice)}>{choice}) } -
      - { - gameState?.Me?.Id && - gameState.Players.map((player) => { - const isCurrentPlayer = player.Id === gameState.CurrentPlayer.Id; - const isMe = player.Id === gameState.Me.Id; - return
    • - {player.Name} {isMe && '(You)'} -
    • - }) - } -
    ) } diff --git a/src/layout/components/Lobby.tsx b/src/layout/components/Lobby.tsx new file mode 100644 index 0000000..e35cb13 --- /dev/null +++ b/src/layout/components/Lobby.tsx @@ -0,0 +1,9 @@ +const Lobby = () => { + return ( +
    +

    Lobby

    +
    + ); +} + +export default Lobby; diff --git a/src/layout/pages/Room.tsx b/src/layout/pages/Room.tsx index b20875e..bb72bdc 100644 --- a/src/layout/pages/Room.tsx +++ b/src/layout/pages/Room.tsx @@ -1,19 +1,16 @@ import useWebSocket from "react-use-websocket"; -import React, {FormEvent} from "react"; +import React, {useEffect} from "react"; import {useNavigate, useParams} from "react-router"; import {GHButton} from "../components/Button"; import useTitle from "../../utils/hooks/TitleHook"; import Game, {GameState} from "../components/Game"; - -interface ChatMessage { - Time: Date; - Sender: string; - Message: string; -} +import Lobby from "../components/Lobby"; +import Chat from "../components/Chat"; export interface Player { Name: string; Id: string; + CardsLeft: number; } interface SocketMessage { @@ -21,6 +18,11 @@ interface SocketMessage { Payload: any; } +export interface GameAction { + Action: string; + Data: string; +} + const Room = () => { useTitle('Mau!'); @@ -33,15 +35,31 @@ const Room = () => { const WS_URL = `${process.env.REACT_APP_WEBSOCKET_URL}/room/${roomId}/${playerName}`; const [gameState, setGameState] = React.useState({ - Me: {Name: playerName, Id: ''}, + Me: {Name: playerName, Id: '', CardsLeft: 0}, CurrentState: '', Hand: [], CurrentCard: '', - CurrentPlayer: {Name: "", Id: ''}, + CurrentPlayer: {Name: "", Id: '', CardsLeft: 0}, Players: [] }); - const [chatMessages, setChatMessages] = React.useState([]); - const [chatInput, setChatInput] = React.useState(''); + + const [chatScroll, setChatScroll] = React.useState(true); + const chatRef = React.useRef(null); + + const addChatMessage = (message: {Sender: string, Message: string}) => { + if(!chatRef.current) return; + + const { scrollTop, scrollHeight, clientHeight } = chatRef.current; + + const newChatElement = document.createElement('li'); + newChatElement.innerHTML = `${message.Sender}: ${message.Message}`; + chatRef.current.appendChild(newChatElement); + + const isNearBottom = scrollTop + clientHeight + newChatElement.scrollHeight * 2 >= scrollHeight; + if (chatScroll && isNearBottom) { + newChatElement.scrollIntoView({behavior: 'smooth'}); + } + } const websocket = useWebSocket(WS_URL, { onOpen: () => { @@ -51,17 +69,11 @@ const Room = () => { const data = JSON.parse(event.data); const payload = JSON.parse(data.Payload); if (data.Type === 'GAME') setGameState(payload); - if (data.Type === 'CHAT') setChatMessages(prev => [...prev, { - Time: new Date(payload.Time), - Sender: payload.Sender, - Message: payload.Message - }]); + if (data.Type === 'CHAT') addChatMessage({Sender: payload.Sender, Message: payload.Message}) } }); const handleLeaveRoom = () => { - const socket = websocket.getWebSocket(); - if (socket) socket.close(); navigateTo('/'); } @@ -69,62 +81,51 @@ const Room = () => { websocket.sendMessage(JSON.stringify(message)); } - const handleCardSend = (card: string) => { - handleSend({ - Type: "GAME", - Payload: JSON.stringify({ - Action: "PLAYCARD", - Data: JSON.stringify({ - CardType: card.split(' ')[0], - CardValue: card.split(' ')[1] - }) - }) - }) + const handleGameAction = (action: GameAction) => { + handleSend({Type: "GAME", Payload: JSON.stringify(action)}); } - const handleChoice = (choice: string) => { - handleSend({ - Type: "GAME", - Payload: JSON.stringify({ - Action: "CHOOSE", - Data: choice - }) - }); - } - - const handleDraw = () => { - handleSend({ - Type: "GAME", - Payload: JSON.stringify({ - Action: "DRAW", - Data: "" - }) - }); - } - - const handleChat = (e: FormEvent) => { - e.preventDefault(); + const handleChatMessage = (chatMessage: string) => { handleSend({ Type: "CHAT", - Payload: chatInput + Payload: chatMessage }); - setChatInput(''); } + const isLobby = false; + const isGame = true; + + useEffect(() => { + return () => { + const socket = websocket.getWebSocket(); + if (socket) socket.close(); + } + // this is a cleanup function and should not include any dependencies that would cause it to run more than once + // eslint-disable-next-line + }, []); + return ( -
    -

    Room {roomId}

    - Leave Room - -
    - setChatInput(e.target.value)} /> - Send -
    -
      - {chatMessages.map((message) => ( -
    • ({message.Time.toLocaleTimeString()}) {message.Sender}: {message.Message}
    • - ))} -
    +
    + +
    + { + isLobby && + + } + { + isGame && + + } +
    ) } diff --git a/src/styles/App.scss b/src/styles/App.scss index 2a810b9..91b4b2e 100644 --- a/src/styles/App.scss +++ b/src/styles/App.scss @@ -19,7 +19,8 @@ body { } #root, .app { - @extend .dark; + background-color: #131313; + color: #ffffff; padding: 1rem; height: 100%; } diff --git a/src/styles/layout/components/_components.scss b/src/styles/layout/components/_components.scss index b053990..55c56f4 100644 --- a/src/styles/layout/components/_components.scss +++ b/src/styles/layout/components/_components.scss @@ -1,4 +1,6 @@ @import "button"; @import "card"; +@import "chat"; @import "hand"; +@import "game"; @import "deck"; \ No newline at end of file diff --git a/src/styles/layout/components/chat.scss b/src/styles/layout/components/chat.scss new file mode 100644 index 0000000..732b436 --- /dev/null +++ b/src/styles/layout/components/chat.scss @@ -0,0 +1,28 @@ +.chat { + + &__list { + overflow-y: hidden; + word-wrap: break-word; + list-style-type: none; + padding: 0; + margin: 1rem 0; + + &:hover { + overflow-y: scroll; + } + } + + &-form { + display: flex; + flex-direction: column; + + &__input { + background-color: #2e2e2e; + border: none; + color: #fff; + font-size: 1rem; + padding: 0.5rem; + border-radius: 0.5rem; + } + } +} \ No newline at end of file diff --git a/src/styles/layout/components/game.scss b/src/styles/layout/components/game.scss new file mode 100644 index 0000000..3737ee5 --- /dev/null +++ b/src/styles/layout/components/game.scss @@ -0,0 +1,25 @@ +.game { + height: 100%; + display: grid; + grid-template-rows: 1fr 1fr 1fr; + place-items: center; + + &__players { + list-style-type: none; + padding: 0; + margin: 0; + display: flex; + justify-content: space-around; + flex-direction: row; + width: 100%; + + &__info { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + } + } +} \ No newline at end of file diff --git a/src/styles/layout/components/hand.scss b/src/styles/layout/components/hand.scss index 6eed4cf..1651805 100644 --- a/src/styles/layout/components/hand.scss +++ b/src/styles/layout/components/hand.scss @@ -1,8 +1,7 @@ .hand { display: flex; - flex-direction: row; - //justify-content: center; - flex-flow: nowrap !important; + max-width: 100%; + flex-flow: row nowrap !important; overflow-x: scroll; padding: 0.5rem; gap: 0.5rem; diff --git a/src/styles/layout/pages/_pages.scss b/src/styles/layout/pages/_pages.scss index 00335b4..ebfd0f7 100644 --- a/src/styles/layout/pages/_pages.scss +++ b/src/styles/layout/pages/_pages.scss @@ -1 +1,2 @@ -@import "mainlobby"; \ No newline at end of file +@import "mainlobby"; +@import "room"; \ No newline at end of file diff --git a/src/styles/layout/pages/room.scss b/src/styles/layout/pages/room.scss new file mode 100644 index 0000000..5c11822 --- /dev/null +++ b/src/styles/layout/pages/room.scss @@ -0,0 +1,12 @@ +.room { + display: grid; + grid-template-columns: auto 1fr; + height: 100%; + + &-aside { + display: grid; + min-width: 20%; + grid-template-rows: auto minmax(0, 1fr) auto; + overflow: hidden auto; + } +} \ No newline at end of file diff --git a/src/styles/utils/_utils.scss b/src/styles/utils/_utils.scss index 647d032..04972e8 100644 --- a/src/styles/utils/_utils.scss +++ b/src/styles/utils/_utils.scss @@ -1,2 +1 @@ -@import "pointer"; -@import "theme"; \ No newline at end of file +@import "pointer"; \ No newline at end of file diff --git a/src/styles/utils/theme.scss b/src/styles/utils/theme.scss deleted file mode 100644 index 8895a7e..0000000 --- a/src/styles/utils/theme.scss +++ /dev/null @@ -1,8 +0,0 @@ -.dark { - background-color: #333; - color: #fff; -} -.light { - background-color: #fff; - color: #333; -} \ No newline at end of file