front: gamestate polling + gameproposal tab

- gamestate controller
- css for gameproposal tab
This commit is contained in:
djmil 2023-10-14 10:01:20 +02:00
parent 6c885bfa68
commit 72daeddb9d
9 changed files with 169 additions and 25 deletions

View File

@ -0,0 +1,37 @@
package djmil.cordacheckers.api;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import djmil.cordacheckers.cordaclient.CordaClient;
import djmil.cordacheckers.cordaclient.dao.GameState;
import djmil.cordacheckers.user.HoldingIdentityResolver;
import djmil.cordacheckers.user.User;
@RestController
@RequestMapping("api/gamestate")
public class GameStateController {
@Autowired
CordaClient cordaClient;
@Autowired
HoldingIdentityResolver holdingIdentityResolver;
@GetMapping
public ResponseEntity<List<GameState>> gameStateList(
@AuthenticationPrincipal User player
) {
final List<GameState> gsList = cordaClient.gameStateList(player.getHoldingIdentity());
return ResponseEntity.ok(gsList);
}
}

View File

@ -31,4 +31,4 @@ public class LeaderboardController {
return ResponseEntity.ok(leaderboard);
}
}
}

View File

@ -1,23 +1,51 @@
import './App.css';
import Leaderboard from "./Leaderboard";
import React from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import {
BrowserRouter,
Routes, //replaces "Switch" used till v5
Route,
} from "react-router-dom";
import Header from "./Header"
function App() {
import Header from "./Header"
import Leaderboard from "./Leaderboard";
import GameProposal from "./GameProposal";
function App() {
const [games, setGames] = useState(null);
const [polling, setPolling] = useState(false);
const pollGames = useCallback(() => {
console.log('start polling..');
if (polling) {
console.log(' ..already in progress');
return;
}
setPolling(true);
fetch('/api/gamestate')
.then(response => response.json())
.then(data => {
console.log('poooled');
setGames(data);
setPolling(false);
})
.catch(err => console.log(err.message));
}, [polling]);
useEffect(() => {
const timer = setInterval(pollGames(), 35 * 1000);
return clearInterval(timer);
}, [pollGames])
return (
<div className="App">
<BrowserRouter>
<Header/>
<div className="Container">
<Routes>
<Route path="/leaderboard" element={<Leaderboard/>} />
<Route path="/gameproposal" element={<GameProposal games={games}/>} />
</Routes>
</div>

View File

@ -0,0 +1,26 @@
.GameProposal {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.GameProposal .li {
width: 50%;
border: 1px solid black;
margin-bottom: 5px;
}
.GameProposal .li p {
margin: 5px;
}
.separator {
width: 20%;
/* height: 20px; */
border-bottom: 1px dotted black;
text-align: center;
font-size: 8px;
padding-left: 50%;
margin-bottom: 7px;
}

View File

@ -0,0 +1,52 @@
import React from 'react';
import './GameProposal.css';
const State = {
WaitForOpponent: "GAME_PROPOSAL_WAIT_FOR_OPPONENT",
WaitForYou: "GAME_PROPOSAL_WAIT_FOR_YOU",
}
const GameProposal = ({games}) => {
if (games == null)
return <p>Loading..</p>
// for (const [key, value] of Object.entries(games))
// console.log(key, value);
const waitForYou = games
.filter(game => game.status === State.WaitForYou)
.map(game => { return (
<div class="li" key={game.uuid}>
<p>
from {game.opponentName}, opponentColor {game.opponentColor}<br/>
<q color="grey">{game.message}</q>
</p>
</div>
)});
const WaitForOpponent = games
.filter(game => game.status === State.WaitForOpponent)
.map(game => { return (
<div class="li" key={game.uuid}>
<p>
to {game.opponentName}, opponentColor {game.opponentColor}<br/>
<q color="grey">{game.message}</q>
</p>
</div>
)});
return (
<p className="GameProposal">
{waitForYou}
{WaitForOpponent.length > 0 &&
<div class="separator">
waiting for opponent ({WaitForOpponent.length})
</div>
}
{WaitForOpponent}
</p>
);
};
export default GameProposal;

3
webapp/src/Header.css Normal file
View File

@ -0,0 +1,3 @@
p {
margin-bottom: 30px;
}

View File

@ -1,15 +1,17 @@
import { Link } from "react-router-dom";
import './Header.css';
export default function Header() {
return (
<div>
<p>
<h1>CordaCheckers</h1>
<nav>
<Link to="/leaderboard">Leaderboard</Link> {"| "}
<Link to="/gameproposal">Game Proposal</Link> {"| "}
<Link to="/game">Games</Link> {"| "}
<Link to="/game">Active Games</Link> {"| "}
<Link to="/archive">Archive</Link> {"| "}
<Link to="about">About</Link>
</nav>
</div>
</p>
);
}

View File

@ -2,5 +2,4 @@
display: flex;
justify-content: center;
align-items: center;
margin-top: 25px
}
}

View File

@ -1,16 +1,5 @@
import React, { useState, useEffect } from 'react';
import './Leaderboard.css';
//const Leaderboard = ({hashmap}) => {
// var listItems = Object.keys(hashmap).map(playerName => {
// var rank = hashmap[playerName];
// return <li key={playerName}>
// {playerName}: played {rank.gamesPlayed}, won {rank.gamesWon}, draw {rank.gamesDraw}
// </li>
// });
// return <ul>{listItems}</ul>;
const Leaderboard = () => {
@ -28,7 +17,16 @@ const Leaderboard = () => {
}, []);
if (data == null)
return <span>Loading...</span>
return <p>Loading...</p>
// var listItems = Object.keys(data).map(playerName => {
// var rank = data[playerName];
//
// return <li key={playerName}>
// {playerName}: played {rank.gamesPlayed}, won {rank.gamesWon}, draw {rank.gamesDraw}
// </li>
// });
// return <ul>{listItems}</ul>;
const tableRows = Object.keys(data).map(playerName => {
var rank = data[playerName];
@ -48,7 +46,7 @@ const Leaderboard = () => {
<table>
<thead>
<tr>
<th>Name</th>
<th></th>
<th>Played</th>
<th>Won</th>
<th>Draw</th>
@ -62,5 +60,4 @@ const Leaderboard = () => {
);
};
export default Leaderboard;