From 72daeddb9db91100b83ff2496ec909c369471116 Mon Sep 17 00:00:00 2001 From: djmil Date: Sat, 14 Oct 2023 10:01:20 +0200 Subject: [PATCH] front: gamestate polling + gameproposal tab - gamestate controller - css for gameproposal tab --- .../api/GameStateController.java | 37 +++++++++++++ .../api/LeaderboardController.java | 2 +- webapp/src/App.js | 38 ++++++++++++-- webapp/src/GameProposal.css | 26 ++++++++++ webapp/src/GameProposal.js | 52 +++++++++++++++++++ webapp/src/Header.css | 3 ++ webapp/src/Header.js | 8 +-- webapp/src/Leaderboard.css | 3 +- webapp/src/Leaderboard.js | 25 ++++----- 9 files changed, 169 insertions(+), 25 deletions(-) create mode 100644 backend/src/main/java/djmil/cordacheckers/api/GameStateController.java create mode 100644 webapp/src/GameProposal.css create mode 100644 webapp/src/GameProposal.js create mode 100644 webapp/src/Header.css diff --git a/backend/src/main/java/djmil/cordacheckers/api/GameStateController.java b/backend/src/main/java/djmil/cordacheckers/api/GameStateController.java new file mode 100644 index 0000000..e550fc6 --- /dev/null +++ b/backend/src/main/java/djmil/cordacheckers/api/GameStateController.java @@ -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> gameStateList( + @AuthenticationPrincipal User player + ) { + final List gsList = cordaClient.gameStateList(player.getHoldingIdentity()); + + return ResponseEntity.ok(gsList); + } + +} diff --git a/backend/src/main/java/djmil/cordacheckers/api/LeaderboardController.java b/backend/src/main/java/djmil/cordacheckers/api/LeaderboardController.java index cd219a1..f6df567 100644 --- a/backend/src/main/java/djmil/cordacheckers/api/LeaderboardController.java +++ b/backend/src/main/java/djmil/cordacheckers/api/LeaderboardController.java @@ -31,4 +31,4 @@ public class LeaderboardController { return ResponseEntity.ok(leaderboard); } -} \ No newline at end of file +} diff --git a/webapp/src/App.js b/webapp/src/App.js index e4e4fc2..7b2264e 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -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 (
-
} /> + } />
diff --git a/webapp/src/GameProposal.css b/webapp/src/GameProposal.css new file mode 100644 index 0000000..19482db --- /dev/null +++ b/webapp/src/GameProposal.css @@ -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; +} diff --git a/webapp/src/GameProposal.js b/webapp/src/GameProposal.js new file mode 100644 index 0000000..9d39e3b --- /dev/null +++ b/webapp/src/GameProposal.js @@ -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

Loading..

+ + // for (const [key, value] of Object.entries(games)) + // console.log(key, value); + + const waitForYou = games + .filter(game => game.status === State.WaitForYou) + .map(game => { return ( +
+

+ from {game.opponentName}, opponentColor {game.opponentColor}
+ {game.message} +

+
+ )}); + + const WaitForOpponent = games + .filter(game => game.status === State.WaitForOpponent) + .map(game => { return ( +
+

+ to {game.opponentName}, opponentColor ⛀ ⛂{game.opponentColor}
+ {game.message} +

+
+ )}); + + return ( +

+ {waitForYou} + {WaitForOpponent.length > 0 && +

+ waiting for opponent ({WaitForOpponent.length}) +
+ } + {WaitForOpponent} +

+ ); +}; + +export default GameProposal; diff --git a/webapp/src/Header.css b/webapp/src/Header.css new file mode 100644 index 0000000..a165fe7 --- /dev/null +++ b/webapp/src/Header.css @@ -0,0 +1,3 @@ +p { + margin-bottom: 30px; +} \ No newline at end of file diff --git a/webapp/src/Header.js b/webapp/src/Header.js index 5b67f68..f9dc7dc 100644 --- a/webapp/src/Header.js +++ b/webapp/src/Header.js @@ -1,15 +1,17 @@ import { Link } from "react-router-dom"; +import './Header.css'; export default function Header() { return ( -
+

CordaCheckers

-
+

); } \ No newline at end of file diff --git a/webapp/src/Leaderboard.css b/webapp/src/Leaderboard.css index 6fc8ea6..cf658c6 100644 --- a/webapp/src/Leaderboard.css +++ b/webapp/src/Leaderboard.css @@ -2,5 +2,4 @@ display: flex; justify-content: center; align-items: center; - margin-top: 25px - } +} diff --git a/webapp/src/Leaderboard.js b/webapp/src/Leaderboard.js index 9e6aea6..f215451 100644 --- a/webapp/src/Leaderboard.js +++ b/webapp/src/Leaderboard.js @@ -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
  • -// {playerName}: played {rank.gamesPlayed}, won {rank.gamesWon}, draw {rank.gamesDraw} -//
  • -// }); - -// return
      {listItems}
    ; const Leaderboard = () => { @@ -28,7 +17,16 @@ const Leaderboard = () => { }, []); if (data == null) - return Loading... + return

    Loading...

    + +// var listItems = Object.keys(data).map(playerName => { +// var rank = data[playerName]; +// +// return
  • +// {playerName}: played {rank.gamesPlayed}, won {rank.gamesWon}, draw {rank.gamesDraw} +//
  • +// }); +// return
      {listItems}
    ; const tableRows = Object.keys(data).map(playerName => { var rank = data[playerName]; @@ -48,7 +46,7 @@ const Leaderboard = () => { - + @@ -62,5 +60,4 @@ const Leaderboard = () => { ); }; - export default Leaderboard;
    Name Played Won Draw