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