Merge remote-tracking branch 'refs/remotes/origin/player-join'
This commit is contained in:
commit
698271ae9f
|
@ -4,7 +4,7 @@
|
|||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --host 0.0.0.0 --port 8543",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
.TankWelcome {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.JoinForm {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
border: 2px solid rgb(76, 76, 76);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.JoinElems {
|
||||
padding: 8px 8px;
|
||||
margin: 8px 8px;
|
||||
}
|
|
@ -1,24 +1,8 @@
|
|||
import {useEffect, useState} from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import './JoinForm.css';
|
||||
import { PlayerResponse, postPlayer } from './serverCalls';
|
||||
|
||||
type PlayerResponse = {
|
||||
readonly name: string;
|
||||
readonly id: string;
|
||||
};
|
||||
|
||||
export async function fetchPlayer(name: string, options: RequestInit) {
|
||||
const url = new URL(import.meta.env.VITE_TANK_PLAYER_URL);
|
||||
url.searchParams.set('name', name);
|
||||
|
||||
const response = await fetch(url, options);
|
||||
if (!response.ok)
|
||||
return null;
|
||||
|
||||
const json = await response.json() as PlayerResponse;
|
||||
return json.id;
|
||||
}
|
||||
|
||||
export default function JoinForm({onDone}: { onDone: (id: string) => void }) {
|
||||
export default function JoinForm({ onDone }: { onDone: (id: string) => void }) {
|
||||
const [name, setName] = useState('');
|
||||
const [clicked, setClicked] = useState(false);
|
||||
const [data, setData] = useState<PlayerResponse | null>(null);
|
||||
|
@ -28,12 +12,13 @@ export default function JoinForm({onDone}: { onDone: (id: string) => void }) {
|
|||
return;
|
||||
|
||||
try {
|
||||
fetchPlayer(name, {}).then((value: string | null) => {
|
||||
if (value)
|
||||
onDone(value);
|
||||
else
|
||||
setClicked(false);
|
||||
});
|
||||
postPlayer(name)
|
||||
.then((value: PlayerResponse | null) => {
|
||||
if (value)
|
||||
onDone(value.id);
|
||||
else
|
||||
setClicked(false);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
alert(e);
|
||||
|
@ -41,17 +26,27 @@ export default function JoinForm({onDone}: { onDone: (id: string) => void }) {
|
|||
}, [clicked, setData, data]);
|
||||
|
||||
const disableButtons = clicked || name.trim() === '';
|
||||
return <div className="JoinForm">
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
onClick={() => setClicked(true)}
|
||||
disabled={disableButtons}
|
||||
>
|
||||
join
|
||||
</button>
|
||||
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>;
|
||||
}
|
||||
|
|
21
tank-frontend/src/PlayerInfo.css
Normal file
21
tank-frontend/src/PlayerInfo.css
Normal file
|
@ -0,0 +1,21 @@
|
|||
.Player {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
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;
|
||||
}
|
38
tank-frontend/src/PlayerInfo.tsx
Normal file
38
tank-frontend/src/PlayerInfo.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import './PlayerInfo.css'
|
||||
import { PlayerResponse, getPlayer } from './serverCalls';
|
||||
|
||||
export default function PlayerInfo({ playerId }: { playerId: string }) {
|
||||
const [player, setPlayer] = useState<PlayerResponse | null>();
|
||||
|
||||
const refresh = () => {
|
||||
getPlayer(playerId).then(setPlayer);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(refresh, 5000);
|
||||
return () => clearInterval(timer);
|
||||
});
|
||||
|
||||
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'></div>
|
||||
<p className='Elems' style={{ "color": "white" }}>
|
||||
{JSON.stringify(player)}
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div >;
|
||||
}
|
|
@ -1,22 +1,24 @@
|
|||
import React, {useState} from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import './index.css';
|
||||
import ClientScreen from './ClientScreen';
|
||||
import Controls from './Controls.tsx';
|
||||
import JoinForm from './JoinForm.tsx';
|
||||
import {createRoot} from 'react-dom/client';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import PlayerInfo from './PlayerInfo.tsx';
|
||||
|
||||
function App() {
|
||||
const [id, setId] = useState<string | null>(null);
|
||||
|
||||
return <>
|
||||
{id === null && <JoinForm onDone={name => setId(name)}/>}
|
||||
<ClientScreen/>
|
||||
{id == null || <Controls playerId={id}/>}
|
||||
{id === null && <JoinForm onDone={name => setId(name)} />}
|
||||
{id == null || <PlayerInfo playerId={id} />}
|
||||
<ClientScreen />
|
||||
{id == null || <Controls playerId={id} />}
|
||||
</>;
|
||||
}
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App/>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
|
23
tank-frontend/src/serverCalls.tsx
Normal file
23
tank-frontend/src/serverCalls.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
export type PlayerResponse = {
|
||||
readonly name: string;
|
||||
readonly id: string;
|
||||
};
|
||||
|
||||
export async function fetchTyped<T>({ url, method }: { url: URL; method: string; }) {
|
||||
const response = await fetch(url, { method });
|
||||
if (!response.ok)
|
||||
return null;
|
||||
return await response.json() as T;
|
||||
}
|
||||
|
||||
export function postPlayer(name: string) {
|
||||
const url = new URL(import.meta.env.VITE_TANK_PLAYER_URL);
|
||||
url.searchParams.set('name', name);
|
||||
return fetchTyped<PlayerResponse>({ url, method: 'POST' });
|
||||
}
|
||||
|
||||
export function getPlayer(id: string) {
|
||||
const url = new URL(import.meta.env.VITE_TANK_PLAYER_URL);
|
||||
url.searchParams.set('id', id);
|
||||
return fetchTyped<PlayerResponse>({ url, method: 'GET' });
|
||||
}
|
Loading…
Reference in a new issue