From 2ba2026e212f17d41847e146014605a6d98e240b Mon Sep 17 00:00:00 2001 From: Dennis Brentjes Date: Sun, 29 May 2022 00:22:30 +0200 Subject: [PATCH] Gamenights also return their game list. --- backend/Cargo.lock | 25 +++++++++++++ backend/Cargo.toml | 3 +- backend/src/api.rs | 50 ++++++++++++++++++-------- backend/src/schema/gamenight.rs | 28 +++++++++++---- frontend/src/App.js | 31 +++++++++++++--- frontend/src/components/Gamenight.jsx | 27 ++++++++++++++ frontend/src/components/Gamenights.jsx | 2 +- 7 files changed, 139 insertions(+), 27 deletions(-) create mode 100644 frontend/src/components/Gamenight.jsx diff --git a/backend/Cargo.lock b/backend/Cargo.lock index f456cc9..cd953d3 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -518,6 +518,7 @@ checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -540,12 +541,34 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.21" @@ -567,6 +590,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -584,6 +608,7 @@ dependencies = [ "diesel", "diesel-derive-enum", "diesel_migrations", + "futures", "jsonwebtoken", "local-ip-address", "password-hash", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index ca9cf16..8d7c32d 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -22,4 +22,5 @@ jsonwebtoken = "8.1" validator = { version = "0.14", features = ["derive"] } rocket_cors = "0.6.0-alpha1" local-ip-address = "0.4" -uuid = { version = "0.8.2", features = ["v4", "serde"] } \ No newline at end of file +uuid = { version = "0.8.2", features = ["v4", "serde"] } +futures = "0.3.21" \ No newline at end of file diff --git a/backend/src/api.rs b/backend/src/api.rs index 6bb69cc..260fea7 100644 --- a/backend/src/api.rs +++ b/backend/src/api.rs @@ -1,3 +1,4 @@ +use crate::schema::DatabaseError; use crate::schema::gamenight::*; use crate::schema::users::*; use crate::schema::DbConn; @@ -12,6 +13,7 @@ use serde::{Deserialize, Serialize}; use std::borrow::Cow; use uuid::Uuid; use validator::ValidateArgs; +use futures::future::join_all; #[derive(Debug, Responder)] pub enum ApiResponseVariant { @@ -27,7 +29,7 @@ struct ApiResponse { #[serde(skip_serializing_if = "Option::is_none")] user: Option, #[serde(skip_serializing_if = "Option::is_none")] - gamenights: Option>, + gamenights: Option>, #[serde(skip_serializing_if = "Option::is_none")] games: Option>, } @@ -67,7 +69,7 @@ impl ApiResponse { } } - fn gamenight_response(gamenights: Vec) -> Self { + fn gamenight_response(gamenights: Vec) -> Self { Self { result: Self::SUCCES_RESULT, message: None, @@ -130,14 +132,34 @@ impl<'r> FromRequest<'r> for User { } } +#[derive(Debug, Serialize, Deserialize)] +pub struct GamenightOutput { + #[serde(flatten)] + gamenight: Gamenight, + game_list: Vec, +} + #[get("/gamenights")] pub async fn gamenights(conn: DbConn, _user: User) -> ApiResponseVariant { - match get_all_gamenights(conn).await { - Ok(gamenights) => { - ApiResponseVariant::Value(json!(ApiResponse::gamenight_response(gamenights))) - } - Err(error) => ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string()))), - } + let gamenights = match get_all_gamenights(&conn).await { + Ok(result) => result, + Err(err) => return ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))) + }; + + let conn_ref = &conn; + + let game_results : Result, DatabaseError> = join_all(gamenights.iter().map(|gn| async move { + let games = get_games_of_gamenight(conn_ref, gn.id).await?; + Ok(GamenightOutput{ + gamenight: gn.clone(), + game_list: games + }) + })).await.into_iter().collect(); + + match game_results { + Ok(result) => ApiResponseVariant::Value(json!(ApiResponse::gamenight_response(result))), + Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))) + } } #[get("/gamenights", rank = 2)] @@ -146,16 +168,16 @@ pub async fn gamenights_unauthorized() -> ApiResponseVariant { } #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct GameNightInput { +pub struct GamenightInput { pub name: String, pub datetime: String, pub owner_id: Option, pub game_list: Vec, } -impl Into for GameNightInput { - fn into(self) -> GameNight { - GameNight { +impl Into for GamenightInput { + fn into(self) -> Gamenight { + Gamenight { id: Uuid::new_v4(), name: self.name, datetime: self.datetime, @@ -168,7 +190,7 @@ impl Into for GameNightInput { pub async fn gamenights_post_json( conn: DbConn, user: User, - gamenight_json: Json, + gamenight_json: Json, ) -> ApiResponseVariant { let mut gamenight = gamenight_json.into_inner(); gamenight.owner_id = Some(user.id); @@ -205,7 +227,7 @@ pub async fn gamenights_post_json_unauthorized() -> ApiResponseVariant { pub async fn gamenights_delete_json( conn: DbConn, user: User, - delete_gamenight_json: Json, + delete_gamenight_json: Json, ) -> ApiResponseVariant { if user.role == Role::Admin { if let Err(error) = delete_gamenight(&conn, delete_gamenight_json.game_id).await { diff --git a/backend/src/schema/gamenight.rs b/backend/src/schema/gamenight.rs index 1a3d417..5177515 100644 --- a/backend/src/schema/gamenight.rs +++ b/backend/src/schema/gamenight.rs @@ -40,9 +40,9 @@ pub struct Game { pub name: String, } -#[derive(Serialize, Deserialize, Debug, Queryable, Insertable)] +#[derive(Serialize, Deserialize, Debug, Queryable, Insertable, Clone)] #[table_name = "gamenight"] -pub struct GameNight { +pub struct Gamenight { pub id: Uuid, pub name: String, pub datetime: String, @@ -65,7 +65,7 @@ pub struct GamenightParticipantsEntry { } #[derive(Serialize, Deserialize, Debug, Queryable)] -pub struct DeleteGameNight { +pub struct DeleteGamenight { pub game_id: Uuid, } @@ -74,13 +74,13 @@ pub struct GamenightId { pub gamenight_id: Uuid, } -pub async fn get_all_gamenights(conn: DbConn) -> Result, DatabaseError> { - Ok(conn.run(|c| gamenight::table.load::(c)).await?) +pub async fn get_all_gamenights(conn: &DbConn) -> Result, DatabaseError> { + Ok(conn.run(|c| gamenight::table.load::(c)).await?) } pub async fn insert_gamenight( conn: &DbConn, - new_gamenight: GameNight, + new_gamenight: Gamenight, game_list: Vec, ) -> Result { Ok(conn @@ -109,7 +109,7 @@ pub async fn insert_gamenight( ) } -pub async fn get_gamenight(conn: &DbConn, game_id: Uuid) -> Result { +pub async fn get_gamenight(conn: &DbConn, game_id: Uuid) -> Result { Ok(conn .run(move |c| gamenight::table.find(game_id).first(c)) .await?) @@ -125,6 +125,20 @@ pub async fn get_all_known_games(conn: &DbConn) -> Result, DatabaseErr Ok(conn.run(|c| known_games::table.load::(c)).await?) } +pub async fn get_games_of_gamenight(conn: &DbConn, gamenight_id: Uuid) -> Result, DatabaseError> { + Ok(conn.run::<_, Result, _>>(move |c| { + let linked_game_ids: Vec = gamenight_gamelist::table + .filter(gamenight_gamelist::gamenight_id.eq(gamenight_id)) + .load::(c)?; + + linked_game_ids.iter().map(|l| { + known_games::table + .filter(known_games::id.eq(l.game_id)) + .first::(c) + }).collect() + }).await?) +} + pub async fn add_game(conn: &DbConn, game: Game) -> Result { Ok(conn .run(|c| { diff --git a/frontend/src/App.js b/frontend/src/App.js index f1f8416..f710b24 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -2,8 +2,9 @@ import './App.css'; import React, { useState, useEffect } from 'react'; import MenuBar from './components/MenuBar'; import Login from './components/Login'; -import Gamenights from './components/Gamenights' -import AddGameNight from './components/AddGameNight' +import Gamenights from './components/Gamenights'; +import AddGameNight from './components/AddGameNight'; +import Gamenight from './components/Gamenight'; const localStorageUserKey = 'user'; @@ -13,6 +14,7 @@ function App() { const [gamenights, setGamenights] = useState([]); const [flashData, setFlashData] = useState({}); const [games, setGames] = useState([]); + const [activeGamenight, setActiveGamenight] = useState(null); const handleLogin = (input) => { const requestOptions = { @@ -94,6 +96,9 @@ function App() { setUser(JSON.parse(localStorage.getItem(localStorageUserKey))); }, []); + + console.log(activeGamenight); + if(user === null) { return (
@@ -101,11 +106,29 @@ function App() {
); } else { + + let mainview; + if(activeGamenight === null) { + mainview = <> + + + + } else { + mainview = + } + return ( <> - - + {mainview} ); } diff --git a/frontend/src/components/Gamenight.jsx b/frontend/src/components/Gamenight.jsx new file mode 100644 index 0000000..35f688f --- /dev/null +++ b/frontend/src/components/Gamenight.jsx @@ -0,0 +1,27 @@ +import * as React from 'react'; + +function Gamenight(props) { + + console.log(props.gamenight); + + let games = props.gamenight.game_list.map(g => + ( +
  • + {g.name} +
  • + ) + ); + + return ( +
    + +

    {props.gamenight.name}

    + {props.gamenight.datetime} +
      + {games} +
    +
    + ) +} + +export default Gamenight diff --git a/frontend/src/components/Gamenights.jsx b/frontend/src/components/Gamenights.jsx index 3a5d35a..de6d76a 100644 --- a/frontend/src/components/Gamenights.jsx +++ b/frontend/src/components/Gamenights.jsx @@ -33,7 +33,7 @@ function Gamenights(props) { let gamenights = props.gamenights.map(g => ( -
  • +
  • {console.log(g); props.setActiveGamenight(g);}}> {g.name} {(props.user.id === g.owner_id || props.user.role === "Admin") &&