wip scores, components
This commit is contained in:
		
							parent
							
								
									64a61ef2b3
								
							
						
					
					
						commit
						b604c01e22
					
				
					 20 changed files with 250 additions and 115 deletions
				
			
		| 
						 | 
				
			
			@ -3,5 +3,7 @@
 | 
			
		|||
    aspect-ratio: calc(352 / 160);
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
    object-fit: contain;
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
    max-width: 100vw;
 | 
			
		||||
    max-height: 100vh;
 | 
			
		||||
    flex-shrink: 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
import useWebSocket from 'react-use-websocket';
 | 
			
		||||
import {useEffect, useRef} from 'react';
 | 
			
		||||
import './ClientScreen.css';
 | 
			
		||||
import {statusTextForReadyState} from './statusTextForReadyState.tsx';
 | 
			
		||||
 | 
			
		||||
const pixelsPerRow = 352;
 | 
			
		||||
const pixelsPerCol = 160;
 | 
			
		||||
| 
						 | 
				
			
			@ -41,11 +40,10 @@ function drawPixelsToCanvas(pixels: Uint8Array, canvas: HTMLCanvasElement) {
 | 
			
		|||
    drawContext.putImageData(imageData, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function ClientScreen({logout}: {logout: () => void}) {
 | 
			
		||||
export default function ClientScreen({logout}: { logout: () => void }) {
 | 
			
		||||
    const canvasRef = useRef<HTMLCanvasElement>(null);
 | 
			
		||||
 | 
			
		||||
    const {
 | 
			
		||||
        readyState,
 | 
			
		||||
        lastMessage,
 | 
			
		||||
        sendMessage,
 | 
			
		||||
        getWebSocket
 | 
			
		||||
| 
						 | 
				
			
			@ -67,8 +65,5 @@ export default function ClientScreen({logout}: {logout: () => void}) {
 | 
			
		|||
        sendMessage('');
 | 
			
		||||
    }, [lastMessage, canvasRef.current]);
 | 
			
		||||
 | 
			
		||||
    return <>
 | 
			
		||||
        <span>The screen is currently {statusTextForReadyState[readyState]}</span>
 | 
			
		||||
        <canvas ref={canvasRef} id="screen" width={pixelsPerRow} height={pixelsPerCol}/>
 | 
			
		||||
    </>;
 | 
			
		||||
    return <canvas ref={canvasRef} id="screen" width={pixelsPerRow} height={pixelsPerCol}/>;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,7 @@
 | 
			
		|||
.Controls * {
 | 
			
		||||
    box-sizing: content-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
kbd {
 | 
			
		||||
    background: hsl(0, 0%, 96%);
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import './Controls.css';
 | 
			
		||||
import useWebSocket from 'react-use-websocket';
 | 
			
		||||
import {useEffect} from 'react';
 | 
			
		||||
import {statusTextForReadyState} from './statusTextForReadyState.tsx';
 | 
			
		||||
import Column from "./components/Column.tsx";
 | 
			
		||||
 | 
			
		||||
export default function Controls({playerId, logout}: {
 | 
			
		||||
    playerId: string,
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +12,6 @@ export default function Controls({playerId, logout}: {
 | 
			
		|||
    const {
 | 
			
		||||
        sendMessage,
 | 
			
		||||
        getWebSocket,
 | 
			
		||||
        readyState
 | 
			
		||||
    } = useWebSocket(url.toString(), {
 | 
			
		||||
        onError: logout
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +43,7 @@ export default function Controls({playerId, logout}: {
 | 
			
		|||
        if (!value)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
        const message = new Uint8Array([typeCode, value]);
 | 
			
		||||
        sendMessage(message);
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			@ -60,8 +60,7 @@ export default function Controls({playerId, logout}: {
 | 
			
		|||
    }, [sendMessage]);
 | 
			
		||||
 | 
			
		||||
    return <>
 | 
			
		||||
        <span>The controller is currently {statusTextForReadyState[readyState]}</span>
 | 
			
		||||
        <div className="controls">
 | 
			
		||||
        <Column className="Controls">
 | 
			
		||||
            <div className="control">
 | 
			
		||||
                <div className="row">
 | 
			
		||||
                    <kbd className="up">▲</kbd>
 | 
			
		||||
| 
						 | 
				
			
			@ -71,12 +70,12 @@ export default function Controls({playerId, logout}: {
 | 
			
		|||
                    <kbd>▼</kbd>
 | 
			
		||||
                    <kbd>▶</kbd>
 | 
			
		||||
                </div>
 | 
			
		||||
                <h3>Move</h3>
 | 
			
		||||
            </div>
 | 
			
		||||
            <h3>Move</h3>
 | 
			
		||||
            <div className="control">
 | 
			
		||||
                <kbd className="space">Space</kbd>
 | 
			
		||||
                <h3>Fire</h3>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
            <h3>Fire</h3>
 | 
			
		||||
        </Column>
 | 
			
		||||
    </>;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,4 @@
 | 
			
		|||
.TankWelcome {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.JoinForm {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    border: 2px solid rgb(76, 76, 76);
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -13,4 +6,4 @@
 | 
			
		|||
.JoinElems {
 | 
			
		||||
    padding: 8px 8px;
 | 
			
		||||
    margin: 8px 8px;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ import {useEffect, useState} from 'react';
 | 
			
		|||
import './JoinForm.css';
 | 
			
		||||
import {NameId, PlayerResponse, postPlayer} from './serverCalls';
 | 
			
		||||
import {Guid} from './Guid.ts';
 | 
			
		||||
import Column from "./components/Column.tsx";
 | 
			
		||||
 | 
			
		||||
export default function JoinForm({setNameId, clientId}: {
 | 
			
		||||
    setNameId: (mutator: (oldState: NameId) => NameId) => void,
 | 
			
		||||
| 
						 | 
				
			
			@ -30,27 +31,25 @@ export default function JoinForm({setNameId, clientId}: {
 | 
			
		|||
    }, [clicked, setData, data, clientId, setClicked, setNameId]);
 | 
			
		||||
 | 
			
		||||
    const disableButtons = clicked || name.trim() === '';
 | 
			
		||||
    return <div className="TankWelcome">
 | 
			
		||||
        <h1 className="JoinElems" style={{'color': 'white'}}>
 | 
			
		||||
            Tanks
 | 
			
		||||
        </h1>
 | 
			
		||||
        <p className="JoinElems" style={{'color': 'white'}}> Welcome and have fun!</p>
 | 
			
		||||
        <div className="JoinForm">
 | 
			
		||||
            <p className="JoinElems" style={{'color': 'white'}}>
 | 
			
		||||
                Enter your name to join the game!
 | 
			
		||||
            </p>
 | 
			
		||||
            <input className="JoinElems"
 | 
			
		||||
                   type="text"
 | 
			
		||||
                   value={name}
 | 
			
		||||
                   placeholder="player name"
 | 
			
		||||
                   onChange={e => setName(e.target.value)}
 | 
			
		||||
            />
 | 
			
		||||
            <button className="JoinElems"
 | 
			
		||||
                    onClick={() => setClicked(true)}
 | 
			
		||||
                    disabled={disableButtons}
 | 
			
		||||
            >
 | 
			
		||||
                join
 | 
			
		||||
            </button>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>;
 | 
			
		||||
    return <Column className='JoinForm'>
 | 
			
		||||
        <p className="JoinElems"> Enter your name to join the game! </p>
 | 
			
		||||
        <input
 | 
			
		||||
            className="JoinElems"
 | 
			
		||||
            type="text"
 | 
			
		||||
            value={name}
 | 
			
		||||
            placeholder="player name"
 | 
			
		||||
            onChange={e => setName(e.target.value)}
 | 
			
		||||
            onKeyUp={event => {
 | 
			
		||||
                if (event.key === 'Enter')
 | 
			
		||||
                    setClicked(true);
 | 
			
		||||
            }}
 | 
			
		||||
        />
 | 
			
		||||
        <button
 | 
			
		||||
            className="JoinElems"
 | 
			
		||||
            onClick={() => setClicked(true)}
 | 
			
		||||
            disabled={disableButtons}
 | 
			
		||||
        >
 | 
			
		||||
            join
 | 
			
		||||
        </button>
 | 
			
		||||
    </Column>;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,21 +1,14 @@
 | 
			
		|||
.Player {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
.PlayerInfo {
 | 
			
		||||
    border: 2px solid rgb(76, 76, 76);
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ScoreForm {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ElemGroup {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.Elems {
 | 
			
		||||
    padding: 8px 8px;
 | 
			
		||||
    margin: 8px 8px;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.PlayerInfo-Reset {
 | 
			
		||||
    height: 4em;
 | 
			
		||||
    width: 4em;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,53 +1,46 @@
 | 
			
		|||
import {useEffect, useState} from 'react';
 | 
			
		||||
import './PlayerInfo.css'
 | 
			
		||||
import {PlayerResponse, getPlayer} from './serverCalls';
 | 
			
		||||
import {Guid} from "./Guid.ts";
 | 
			
		||||
import Column from "./components/Column.tsx";
 | 
			
		||||
import Row from "./components/Row.tsx";
 | 
			
		||||
import Button from "./components/Button.tsx";
 | 
			
		||||
 | 
			
		||||
export default function PlayerInfo({playerId, logout}: { playerId: string, logout: () => void }) {
 | 
			
		||||
export default function PlayerInfo({playerId, logout, reset}: {
 | 
			
		||||
    playerId: Guid,
 | 
			
		||||
    logout: () => void,
 | 
			
		||||
    reset: () => void
 | 
			
		||||
}) {
 | 
			
		||||
    const [player, setPlayer] = useState<PlayerResponse | null>();
 | 
			
		||||
 | 
			
		||||
    const refresh = () => {
 | 
			
		||||
        getPlayer(playerId).then(value => {
 | 
			
		||||
            if (value)
 | 
			
		||||
                setPlayer(value);
 | 
			
		||||
            else
 | 
			
		||||
                logout();
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const refresh = () => {
 | 
			
		||||
            getPlayer(playerId).then(value => {
 | 
			
		||||
                if (value)
 | 
			
		||||
                    setPlayer(value);
 | 
			
		||||
                else
 | 
			
		||||
                    logout();
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const timer = setInterval(refresh, 5000);
 | 
			
		||||
        return () => clearInterval(timer);
 | 
			
		||||
    });
 | 
			
		||||
    }, [playerId]);
 | 
			
		||||
 | 
			
		||||
    return <div className='TankWelcome'>
 | 
			
		||||
        <h1 className='Elems' style={{"color": "white"}}>
 | 
			
		||||
            Tanks
 | 
			
		||||
        </h1>
 | 
			
		||||
        <div className="ScoreForm">
 | 
			
		||||
            <div className='ElemGroup'>
 | 
			
		||||
                <p className='Elems' style={{"color": "white"}}>
 | 
			
		||||
                    name:
 | 
			
		||||
                </p>
 | 
			
		||||
                <p className='Elems' style={{"color": "white"}}>
 | 
			
		||||
                    {player?.name}
 | 
			
		||||
                </p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className='ElemGroup'>
 | 
			
		||||
                <p className='Elems' style={{"color": "white"}}>
 | 
			
		||||
                    kills:
 | 
			
		||||
                </p>
 | 
			
		||||
                <p className='Elems' style={{"color": "white"}}>
 | 
			
		||||
                    {player?.scores.kills}
 | 
			
		||||
                </p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className='ElemGroup'>
 | 
			
		||||
                <p className='Elems' style={{"color": "white"}}>
 | 
			
		||||
                    deaths:
 | 
			
		||||
                </p>
 | 
			
		||||
                <p className='Elems' style={{"color": "white"}}>
 | 
			
		||||
                    {player?.scores.deaths}
 | 
			
		||||
                </p>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>;
 | 
			
		||||
    return <Column className='PlayerInfo'>
 | 
			
		||||
        <Row>
 | 
			
		||||
            <h3 className='grow'>
 | 
			
		||||
                {player ? `Playing as "${player?.name}"` : 'loading...'}
 | 
			
		||||
            </h3>
 | 
			
		||||
            <Button className='PlayerInfo-Reset' onClick={() => reset()} text='x'/>
 | 
			
		||||
        </Row>
 | 
			
		||||
        <Row>
 | 
			
		||||
            <p className='Elems'> kills: </p>
 | 
			
		||||
            <p className='Elems'>{player?.scores.kills}</p>
 | 
			
		||||
        </Row>
 | 
			
		||||
        <Row>
 | 
			
		||||
            <p className='Elems'>deaths: </p>
 | 
			
		||||
            <p className='Elems'>{player?.scores.deaths}</p>
 | 
			
		||||
        </Row>
 | 
			
		||||
    </Column>;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										26
									
								
								tank-frontend/src/Scoreboard.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								tank-frontend/src/Scoreboard.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
import {getScores, PlayerResponse} from "./serverCalls.tsx";
 | 
			
		||||
import {useEffect, useState} from "react";
 | 
			
		||||
import DataTable from "./components/DataTable.tsx";
 | 
			
		||||
 | 
			
		||||
export default function Scoreboard({}: {}) {
 | 
			
		||||
    const [players, setPlayers] = useState<PlayerResponse[]>([]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const refresh = () => {
 | 
			
		||||
            getScores().then(value => {
 | 
			
		||||
                if (value)
 | 
			
		||||
                    setPlayers(value);
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const timer = setInterval(refresh, 5000);
 | 
			
		||||
        return () => clearInterval(timer);
 | 
			
		||||
    }, [players]);
 | 
			
		||||
 | 
			
		||||
    return <DataTable<PlayerResponse> data={players} columns={[
 | 
			
		||||
        {field: 'name'},
 | 
			
		||||
        {field: 'kills', visualize: p => p.scores.kills},
 | 
			
		||||
        {field: 'deaths', visualize: p => p.scores.deaths},
 | 
			
		||||
        {field: 'k/d', visualize: p => p.scores.kills / p.scores.deaths}
 | 
			
		||||
    ]}/>
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								tank-frontend/src/components/Button.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tank-frontend/src/components/Button.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
.Button {
 | 
			
		||||
    border: solid 1px green;
 | 
			
		||||
    border-radius: 12px;
 | 
			
		||||
 | 
			
		||||
    color: green;
 | 
			
		||||
    background: transparent;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								tank-frontend/src/components/Button.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tank-frontend/src/components/Button.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
import './Button.css';
 | 
			
		||||
import {MouseEventHandler} from "react";
 | 
			
		||||
 | 
			
		||||
export default function Button({text, onClick, className}: {
 | 
			
		||||
    text: string,
 | 
			
		||||
    onClick?: MouseEventHandler<HTMLButtonElement>,
 | 
			
		||||
    className?: string
 | 
			
		||||
}) {
 | 
			
		||||
    return <button
 | 
			
		||||
        className={'Button ' + (className ?? '')}
 | 
			
		||||
        onClick={onClick}
 | 
			
		||||
    >
 | 
			
		||||
        {text}
 | 
			
		||||
    </button>
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								tank-frontend/src/components/Column.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tank-frontend/src/components/Column.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
.Column {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    flex: auto;
 | 
			
		||||
    max-height: 100%;
 | 
			
		||||
    align-items: stretch;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								tank-frontend/src/components/Column.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tank-frontend/src/components/Column.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
import {ReactNode} from "react";
 | 
			
		||||
 | 
			
		||||
import './Column.css'
 | 
			
		||||
 | 
			
		||||
export default function Column({children, className}: {
 | 
			
		||||
    children: ReactNode,
 | 
			
		||||
    className?: string
 | 
			
		||||
}) {
 | 
			
		||||
    return <div className={'Column ' + (className ?? '')}>
 | 
			
		||||
        {children}
 | 
			
		||||
    </div>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								tank-frontend/src/components/DataTable.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tank-frontend/src/components/DataTable.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
.DataTable {
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								tank-frontend/src/components/DataTable.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								tank-frontend/src/components/DataTable.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
import {ReactNode} from "react";
 | 
			
		||||
import './DataTable.css';
 | 
			
		||||
 | 
			
		||||
export type DataTableColumnDefinition<T> = {
 | 
			
		||||
    field: string,
 | 
			
		||||
    label?: string,
 | 
			
		||||
    visualize?: (value: T) => ReactNode
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function TableHead({columns}: { columns: DataTableColumnDefinition<any>[] }) {
 | 
			
		||||
    return <thead className='DataTableHead'>
 | 
			
		||||
    <tr>
 | 
			
		||||
        {
 | 
			
		||||
            columns.map(column =>
 | 
			
		||||
                <th key={column.field}>
 | 
			
		||||
                    {column.label ?? column.field}
 | 
			
		||||
                </th>)
 | 
			
		||||
        }
 | 
			
		||||
    </tr>
 | 
			
		||||
    </thead>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function DataTableRow({rowData, columns}: {
 | 
			
		||||
    rowData: any,
 | 
			
		||||
    columns: DataTableColumnDefinition<any>[]
 | 
			
		||||
}) {
 | 
			
		||||
    return <tr>
 | 
			
		||||
        {
 | 
			
		||||
            columns.map(column => {
 | 
			
		||||
                return <td key={column.field}>
 | 
			
		||||
                    {
 | 
			
		||||
                        column.visualize
 | 
			
		||||
                            ? column.visualize(rowData)
 | 
			
		||||
                            : rowData[column.field]
 | 
			
		||||
                    }
 | 
			
		||||
                </td>;
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    </tr>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function DataTable<T>({data, columns}: {
 | 
			
		||||
    data: T[],
 | 
			
		||||
    columns: DataTableColumnDefinition<any>[]
 | 
			
		||||
}) {
 | 
			
		||||
    return <table className='DataTable'>
 | 
			
		||||
        <TableHead columns={columns}/>
 | 
			
		||||
        <tbody>
 | 
			
		||||
        {data.map((element, index) => <DataTableRow key={index} rowData={element} columns={columns}/>)}
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								tank-frontend/src/components/Row.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tank-frontend/src/components/Row.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
.Row {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    flex: auto;
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
    align-items: stretch;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								tank-frontend/src/components/Row.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tank-frontend/src/components/Row.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import {ReactNode} from "react";
 | 
			
		||||
 | 
			
		||||
import './Row.css';
 | 
			
		||||
 | 
			
		||||
export default function Row({children, className}: { children: ReactNode, className?: string }) {
 | 
			
		||||
    return <div className={'Row ' + (className ?? '')}>
 | 
			
		||||
        {children}
 | 
			
		||||
    </div>
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,6 @@
 | 
			
		|||
* {
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
html, body {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
| 
						 | 
				
			
			@ -11,12 +14,17 @@ body {
 | 
			
		|||
    justify-content: center;
 | 
			
		||||
 | 
			
		||||
    background-color: black;
 | 
			
		||||
    color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#root {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    width: 100vw;
 | 
			
		||||
    height: 100vh;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.grow {
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,12 +7,17 @@ import {createRoot} from 'react-dom/client';
 | 
			
		|||
import PlayerInfo from './PlayerInfo.tsx';
 | 
			
		||||
import {useStoredObjectState} from './useStoredState.ts';
 | 
			
		||||
import {NameId, postPlayer} from './serverCalls.tsx';
 | 
			
		||||
import Column from "./components/Column.tsx";
 | 
			
		||||
import Row from "./components/Row.tsx";
 | 
			
		||||
import Scoreboard from "./Scoreboard.tsx";
 | 
			
		||||
 | 
			
		||||
const getNewNameId = () => ({
 | 
			
		||||
    id: crypto.randomUUID(),
 | 
			
		||||
    name: ''
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function App() {
 | 
			
		||||
    const [nameId, setNameId] = useStoredObjectState<NameId>('access', () => ({
 | 
			
		||||
        id: crypto.randomUUID(),
 | 
			
		||||
        name: ''
 | 
			
		||||
    }));
 | 
			
		||||
    const [nameId, setNameId] = useStoredObjectState<NameId>('access', getNewNameId);
 | 
			
		||||
 | 
			
		||||
    const [isLoggedIn, setLoggedIn] = useState<boolean>(false);
 | 
			
		||||
    const logout = () => setLoggedIn(false);
 | 
			
		||||
| 
						 | 
				
			
			@ -24,12 +29,17 @@ function App() {
 | 
			
		|||
        setLoggedIn(result !== null);
 | 
			
		||||
    }, [nameId, isLoggedIn])();
 | 
			
		||||
 | 
			
		||||
    return <>
 | 
			
		||||
    return <Column className='grow'>
 | 
			
		||||
        <h1>Tanks!</h1>
 | 
			
		||||
        {nameId.name === '' && <JoinForm setNameId={setNameId} clientId={nameId.id}/>}
 | 
			
		||||
        {isLoggedIn && <PlayerInfo playerId={nameId.id} logout={logout}/>}
 | 
			
		||||
        <ClientScreen logout={logout}/>
 | 
			
		||||
        {isLoggedIn && <Controls playerId={nameId.id} logout={logout}/>}
 | 
			
		||||
    </>;
 | 
			
		||||
        {isLoggedIn && <Row>
 | 
			
		||||
            <Controls playerId={nameId.id} logout={logout}/>
 | 
			
		||||
            <PlayerInfo playerId={nameId.id} logout={logout} reset={() => setNameId(getNewNameId)}/>
 | 
			
		||||
            <Scoreboard/>
 | 
			
		||||
        </Row>
 | 
			
		||||
        }
 | 
			
		||||
    </Column>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
createRoot(document.getElementById('root')!).render(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ import {Guid} from './Guid.ts';
 | 
			
		|||
 | 
			
		||||
export type PlayerResponse = {
 | 
			
		||||
    readonly name: string;
 | 
			
		||||
    readonly id: string;
 | 
			
		||||
    readonly id: Guid;
 | 
			
		||||
    readonly scores: {
 | 
			
		||||
        readonly kills: number;
 | 
			
		||||
        readonly deaths: number;
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ export function postPlayer({name, id}: NameId) {
 | 
			
		|||
    return fetchTyped<NameId>({url, method: 'POST'});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getPlayer(id: string) {
 | 
			
		||||
export function getPlayer(id: Guid) {
 | 
			
		||||
    const url = new URL(import.meta.env.VITE_TANK_PLAYER_URL);
 | 
			
		||||
    url.searchParams.set('id', id);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue