Merge remote-tracking branch 'refs/remotes/origin/player-join'
This commit is contained in:
		
						commit
						698271ae9f
					
				
					 7 changed files with 136 additions and 45 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter