join_gamenight #8
@ -31,6 +31,8 @@ struct ApiResponse {
 | 
				
			|||||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
					    #[serde(skip_serializing_if = "Option::is_none")]
 | 
				
			||||||
    gamenights: Option<Vec<GamenightOutput>>,
 | 
					    gamenights: Option<Vec<GamenightOutput>>,
 | 
				
			||||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
					    #[serde(skip_serializing_if = "Option::is_none")]
 | 
				
			||||||
 | 
					    gamenight: Option<GamenightOutput>,
 | 
				
			||||||
 | 
					    #[serde(skip_serializing_if = "Option::is_none")]
 | 
				
			||||||
    games: Option<Vec<Game>>,
 | 
					    games: Option<Vec<Game>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -43,6 +45,7 @@ impl ApiResponse {
 | 
				
			|||||||
        message: None,
 | 
					        message: None,
 | 
				
			||||||
        user: None,
 | 
					        user: None,
 | 
				
			||||||
        gamenights: None,
 | 
					        gamenights: None,
 | 
				
			||||||
 | 
					        gamenight: None,
 | 
				
			||||||
        games: None,
 | 
					        games: None,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -52,6 +55,7 @@ impl ApiResponse {
 | 
				
			|||||||
            message: Some(Cow::Owned(message)),
 | 
					            message: Some(Cow::Owned(message)),
 | 
				
			||||||
            user: None,
 | 
					            user: None,
 | 
				
			||||||
            gamenights: None,
 | 
					            gamenights: None,
 | 
				
			||||||
 | 
					            gamenight: None,
 | 
				
			||||||
            games: None,
 | 
					            games: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -65,16 +69,29 @@ impl ApiResponse {
 | 
				
			|||||||
                jwt: jwt,
 | 
					                jwt: jwt,
 | 
				
			||||||
            }),
 | 
					            }),
 | 
				
			||||||
            gamenights: None,
 | 
					            gamenights: None,
 | 
				
			||||||
 | 
					            gamenight: None,
 | 
				
			||||||
            games: None,
 | 
					            games: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn gamenight_response(gamenights: Vec<GamenightOutput>) -> Self {
 | 
					    fn gamenights_response(gamenights: Vec<GamenightOutput>) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            result: Self::SUCCES_RESULT,
 | 
					            result: Self::SUCCES_RESULT,
 | 
				
			||||||
            message: None,
 | 
					            message: None,
 | 
				
			||||||
            user: None,
 | 
					            user: None,
 | 
				
			||||||
            gamenights: Some(gamenights),
 | 
					            gamenights: Some(gamenights),
 | 
				
			||||||
 | 
					            gamenight: None,
 | 
				
			||||||
 | 
					            games: None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    fn gamenight_response(gamenight: GamenightOutput) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            result: Self::SUCCES_RESULT,
 | 
				
			||||||
 | 
					            message: None,
 | 
				
			||||||
 | 
					            user: None,
 | 
				
			||||||
 | 
					            gamenights: None,
 | 
				
			||||||
 | 
					            gamenight: Some(gamenight),
 | 
				
			||||||
            games: None,
 | 
					            games: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -85,6 +102,7 @@ impl ApiResponse {
 | 
				
			|||||||
            message: None,
 | 
					            message: None,
 | 
				
			||||||
            user: None,
 | 
					            user: None,
 | 
				
			||||||
            gamenights: None,
 | 
					            gamenights: None,
 | 
				
			||||||
 | 
					            gamenight: None,
 | 
				
			||||||
            games: Some(games),
 | 
					            games: Some(games),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -140,6 +158,63 @@ pub struct GamenightOutput {
 | 
				
			|||||||
    participants: Vec<User>,
 | 
					    participants: Vec<User>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct GamenightUpdate {
 | 
				
			||||||
 | 
					    action: String
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[patch("/gamenights/<gamenight_id>", format = "application/json", data = "<patch_json>")]
 | 
				
			||||||
 | 
					pub async fn patch_gamenight(conn: DbConn, user: User, gamenight_id: String, patch_json: Json<GamenightUpdate>) -> ApiResponseVariant {
 | 
				
			||||||
 | 
					    let uuid = Uuid::parse_str(&gamenight_id).unwrap();
 | 
				
			||||||
 | 
					    let patch = patch_json.into_inner();
 | 
				
			||||||
 | 
					    match patch.action.as_str() {
 | 
				
			||||||
 | 
					        "RemoveParticipant" => {
 | 
				
			||||||
 | 
					            let entry = GamenightParticipantsEntry {
 | 
				
			||||||
 | 
					                gamenight_id: uuid,
 | 
				
			||||||
 | 
					                user_id: user.id
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            match remove_participant(&conn, entry).await {
 | 
				
			||||||
 | 
					                Ok(_) => ApiResponseVariant::Value(json!(ApiResponse::SUCCES)),
 | 
				
			||||||
 | 
					                Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string())))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        "AddParticipant" => {
 | 
				
			||||||
 | 
					            let entry = GamenightParticipantsEntry {
 | 
				
			||||||
 | 
					                gamenight_id: uuid,
 | 
				
			||||||
 | 
					                user_id: user.id
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            match add_participant(&conn, entry).await {
 | 
				
			||||||
 | 
					                Ok(_) => ApiResponseVariant::Value(json!(ApiResponse::SUCCES)),
 | 
				
			||||||
 | 
					                Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string())))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        _ => ApiResponseVariant::Value(json!(ApiResponse::SUCCES))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[get("/gamenights/<gamenight_id>")]
 | 
				
			||||||
 | 
					pub async fn gamenight(conn: DbConn, _user: User, gamenight_id: String) -> ApiResponseVariant {
 | 
				
			||||||
 | 
					    let uuid = Uuid::parse_str(&gamenight_id).unwrap();
 | 
				
			||||||
 | 
					    let gamenight = match get_gamenight(&conn, uuid).await {
 | 
				
			||||||
 | 
					        Ok(result) => result,
 | 
				
			||||||
 | 
					        Err(err) => return ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let games = match get_games_of_gamenight(&conn, uuid).await {
 | 
				
			||||||
 | 
					        Ok(result) => result,
 | 
				
			||||||
 | 
					        Err(err) => return ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let participants = match load_participants(&conn, uuid).await {
 | 
				
			||||||
 | 
					        Ok(result) => result,
 | 
				
			||||||
 | 
					        Err(err) => return ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let gamenight_output = GamenightOutput {
 | 
				
			||||||
 | 
					        gamenight: gamenight,
 | 
				
			||||||
 | 
					        game_list: games,
 | 
				
			||||||
 | 
					        participants: participants
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    return ApiResponseVariant::Value(json!(ApiResponse::gamenight_response(gamenight_output)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[get("/gamenights")]
 | 
					#[get("/gamenights")]
 | 
				
			||||||
pub async fn gamenights(conn: DbConn, _user: User) -> ApiResponseVariant {
 | 
					pub async fn gamenights(conn: DbConn, _user: User) -> ApiResponseVariant {
 | 
				
			||||||
    let gamenights = match get_all_gamenights(&conn).await {
 | 
					    let gamenights = match get_all_gamenights(&conn).await {
 | 
				
			||||||
@ -164,7 +239,7 @@ pub async fn gamenights(conn: DbConn, _user: User) -> ApiResponseVariant {
 | 
				
			|||||||
        .collect();
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    match game_results {
 | 
					    match game_results {
 | 
				
			||||||
        Ok(result) => ApiResponseVariant::Value(json!(ApiResponse::gamenight_response(result))),
 | 
					        Ok(result) => ApiResponseVariant::Value(json!(ApiResponse::gamenights_response(result))),
 | 
				
			||||||
        Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
 | 
					        Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -220,7 +295,7 @@ pub async fn gamenights_post_json(
 | 
				
			|||||||
        gamenight_id: gamenight_id,
 | 
					        gamenight_id: gamenight_id,
 | 
				
			||||||
        user_id: user.id,
 | 
					        user_id: user.id,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    match insert_participant(&conn, participant).await {
 | 
					    match add_participant(&conn, participant).await {
 | 
				
			||||||
        Ok(_) => ApiResponseVariant::Value(json!(ApiResponse::SUCCES)),
 | 
					        Ok(_) => ApiResponseVariant::Value(json!(ApiResponse::SUCCES)),
 | 
				
			||||||
        Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
 | 
					        Err(err) => ApiResponseVariant::Value(json!(ApiResponse::error(err.to_string()))),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -375,7 +450,7 @@ pub async fn post_participants(
 | 
				
			|||||||
    _user: User,
 | 
					    _user: User,
 | 
				
			||||||
    entry_json: Json<GamenightParticipantsEntry>,
 | 
					    entry_json: Json<GamenightParticipantsEntry>,
 | 
				
			||||||
) -> ApiResponseVariant {
 | 
					) -> ApiResponseVariant {
 | 
				
			||||||
    match insert_participant(&conn, entry_json.into_inner()).await {
 | 
					    match add_participant(&conn, entry_json.into_inner()).await {
 | 
				
			||||||
        Ok(_) => ApiResponseVariant::Value(json!(ApiResponse::SUCCES)),
 | 
					        Ok(_) => ApiResponseVariant::Value(json!(ApiResponse::SUCCES)),
 | 
				
			||||||
        Err(error) => ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string()))),
 | 
					        Err(error) => ApiResponseVariant::Value(json!(ApiResponse::error(error.to_string()))),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -50,6 +50,8 @@ async fn rocket() -> _ {
 | 
				
			|||||||
        .mount(
 | 
					        .mount(
 | 
				
			||||||
            "/api",
 | 
					            "/api",
 | 
				
			||||||
            routes![
 | 
					            routes![
 | 
				
			||||||
 | 
					                api::gamenight,
 | 
				
			||||||
 | 
					                api::patch_gamenight,
 | 
				
			||||||
                api::gamenights,
 | 
					                api::gamenights,
 | 
				
			||||||
                api::gamenights_unauthorized,
 | 
					                api::gamenights_unauthorized,
 | 
				
			||||||
                api::gamenights_post_json,
 | 
					                api::gamenights_post_json,
 | 
				
			||||||
 | 
				
			|||||||
@ -110,9 +110,9 @@ pub async fn insert_gamenight(
 | 
				
			|||||||
        .await?)
 | 
					        .await?)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub async fn get_gamenight(conn: &DbConn, game_id: Uuid) -> Result<Gamenight, DatabaseError> {
 | 
					pub async fn get_gamenight(conn: &DbConn, gamenight_id: Uuid) -> Result<Gamenight, DatabaseError> {
 | 
				
			||||||
    Ok(conn
 | 
					    Ok(conn
 | 
				
			||||||
        .run(move |c| gamenight::table.find(game_id).first(c))
 | 
					        .run(move |c| gamenight::table.find(gamenight_id).first(c))
 | 
				
			||||||
        .await?)
 | 
					        .await?)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -190,7 +190,7 @@ pub async fn load_participants(
 | 
				
			|||||||
        .await?)
 | 
					        .await?)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub async fn insert_participant(
 | 
					pub async fn add_participant(
 | 
				
			||||||
    conn: &DbConn,
 | 
					    conn: &DbConn,
 | 
				
			||||||
    participant: GamenightParticipantsEntry,
 | 
					    participant: GamenightParticipantsEntry,
 | 
				
			||||||
) -> Result<usize, DatabaseError> {
 | 
					) -> Result<usize, DatabaseError> {
 | 
				
			||||||
@ -198,6 +198,7 @@ pub async fn insert_participant(
 | 
				
			|||||||
        .run(move |c| {
 | 
					        .run(move |c| {
 | 
				
			||||||
            diesel::insert_into(gamenight_participants::table)
 | 
					            diesel::insert_into(gamenight_participants::table)
 | 
				
			||||||
                .values(&participant)
 | 
					                .values(&participant)
 | 
				
			||||||
 | 
					                .on_conflict_do_nothing()
 | 
				
			||||||
                .execute(c)
 | 
					                .execute(c)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .await?)
 | 
					        .await?)
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,8 @@ import Login from './components/Login';
 | 
				
			|||||||
import Gamenights from './components/Gamenights';
 | 
					import Gamenights from './components/Gamenights';
 | 
				
			||||||
import Gamenight from './components/Gamenight';
 | 
					import Gamenight from './components/Gamenight';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { get_gamenights, get_games, unpack_api_result, login } from './api/Api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const localStorageUserKey = 'user';
 | 
					const localStorageUserKey = 'user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function App() {
 | 
					function App() {
 | 
				
			||||||
@ -13,26 +15,16 @@ function App() {
 | 
				
			|||||||
  const [gamenights, setGamenights] = useState([]);
 | 
					  const [gamenights, setGamenights] = useState([]);
 | 
				
			||||||
  const [flashData, setFlashData] = useState({});
 | 
					  const [flashData, setFlashData] = useState({});
 | 
				
			||||||
  const [games, setGames] = useState([]);
 | 
					  const [games, setGames] = useState([]);
 | 
				
			||||||
  const [activeGamenight, setActiveGamenight] = useState(null);
 | 
					  const [activeGamenightId, setActiveGamenightId] = useState(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const POST_HEADER = {'Content-Type': 'application/json'};
 | 
				
			||||||
 | 
					  const AUTH_HEADER = {'Authorization': `Bearer ${user?.jwt}`};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleLogin = (input) => {
 | 
					  const handleLogin = (input) => {
 | 
				
			||||||
    const requestOptions = {
 | 
					    unpack_api_result(login(input), setFlashData)
 | 
				
			||||||
      method: 'POST',
 | 
					      .then(result => {
 | 
				
			||||||
      headers: { 'Content-Type': 'application/json' },
 | 
					        setUser(result.user);
 | 
				
			||||||
      body: JSON.stringify(input)
 | 
					        localStorage.setItem(localStorageUserKey, JSON.stringify(result.user));
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    fetch('api/login', requestOptions)
 | 
					 | 
				
			||||||
      .then(response => response.json())
 | 
					 | 
				
			||||||
      .then(data => {
 | 
					 | 
				
			||||||
        if(data.result === "Ok") {
 | 
					 | 
				
			||||||
          setUser(data.user);
 | 
					 | 
				
			||||||
          localStorage.setItem(localStorageUserKey, JSON.stringify(data.user));
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          setFlashData({
 | 
					 | 
				
			||||||
            type: "Error",
 | 
					 | 
				
			||||||
            message: data.message
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,48 +42,20 @@ function App() {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const dismissActiveGamenight = () => {
 | 
					  const dismissActiveGamenight = () => {
 | 
				
			||||||
    setActiveGamenight(null);
 | 
					    setActiveGamenightId(null);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (user !== null) {
 | 
					    if (user !== null) {
 | 
				
			||||||
      const requestOptions = {
 | 
					      unpack_api_result(get_gamenights(user.jwt), setFlashData)
 | 
				
			||||||
        method: 'GET',
 | 
					        .then(result => setGamenights(result.gamenights));
 | 
				
			||||||
        headers: { 'Authorization': `Bearer ${user.jwt}` },
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
      fetch('api/gamenights', requestOptions)
 | 
					 | 
				
			||||||
        .then(response => response.json())
 | 
					 | 
				
			||||||
        .then(data => {
 | 
					 | 
				
			||||||
          if(data.result === "Ok") {
 | 
					 | 
				
			||||||
            setGamenights(data.gamenights)
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            setFlashData({
 | 
					 | 
				
			||||||
              type: "Error",
 | 
					 | 
				
			||||||
              message: data.message
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [user])
 | 
					  }, [user])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (user !== null) {
 | 
					    if (user !== null) {
 | 
				
			||||||
      const requestOptions = {
 | 
					      unpack_api_result(get_games(user.jwt), setFlashData)
 | 
				
			||||||
        method: 'GET',
 | 
					        .then(result => setGames(result.games));
 | 
				
			||||||
        headers: { 'Authorization': `Bearer ${user.jwt}` },
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
      fetch('api/games', requestOptions)
 | 
					 | 
				
			||||||
        .then(response => response.json())
 | 
					 | 
				
			||||||
        .then(data => {
 | 
					 | 
				
			||||||
          if(data.result === "Ok") {
 | 
					 | 
				
			||||||
            setGames(data.games)
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            setFlashData({
 | 
					 | 
				
			||||||
              type: "Error",
 | 
					 | 
				
			||||||
              message: data.message
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }        
 | 
					    }        
 | 
				
			||||||
  }, [user])
 | 
					  }, [user])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -108,7 +72,7 @@ function App() {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    let mainview;
 | 
					    let mainview;
 | 
				
			||||||
    if(activeGamenight === null) {
 | 
					    if(activeGamenightId === null) {
 | 
				
			||||||
      mainview = (
 | 
					      mainview = (
 | 
				
			||||||
        <Gamenights 
 | 
					        <Gamenights 
 | 
				
			||||||
          user={user}
 | 
					          user={user}
 | 
				
			||||||
@ -116,13 +80,15 @@ function App() {
 | 
				
			|||||||
          setFlash={setFlash}
 | 
					          setFlash={setFlash}
 | 
				
			||||||
          refetchGamenights={refetchGamenights}
 | 
					          refetchGamenights={refetchGamenights}
 | 
				
			||||||
          gamenights={gamenights}
 | 
					          gamenights={gamenights}
 | 
				
			||||||
          onSelectGamenight={(g) => setActiveGamenight(g)}/>
 | 
					          onSelectGamenight={(g) => setActiveGamenightId(g.id)}/>
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      mainview = (
 | 
					      mainview = (
 | 
				
			||||||
      <Gamenight
 | 
					      <Gamenight
 | 
				
			||||||
        gamenight={activeGamenight}
 | 
					        gamenightId={activeGamenightId}
 | 
				
			||||||
        onDismis={dismissActiveGamenight}
 | 
					        onDismis={dismissActiveGamenight}
 | 
				
			||||||
 | 
					        setFlash={setFlash}
 | 
				
			||||||
 | 
					        user={user}
 | 
				
			||||||
      />)
 | 
					      />)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										90
									
								
								frontend/src/api/Api.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								frontend/src/api/Api.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					import fetchResource from './FetchResource'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function unpack_api_result(promise, onError) {
 | 
				
			||||||
 | 
					  promise.then(result => {
 | 
				
			||||||
 | 
					    if(result.result !== 'Ok') {
 | 
				
			||||||
 | 
					      onError({
 | 
				
			||||||
 | 
					        type: 'Error',
 | 
				
			||||||
 | 
					        message: result.message
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .catch(error => {
 | 
				
			||||||
 | 
					      onError({
 | 
				
			||||||
 | 
					      type: 'Error',
 | 
				
			||||||
 | 
					      message: `${error.status} ${error.message}`
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  return promise;      
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function get_gamenights(token) {
 | 
				
			||||||
 | 
					  return fetchResource('api/gamenights', {
 | 
				
			||||||
 | 
					    method: 'GET',
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      'Authorization': `Bearer ${token}`
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function get_gamenight(gamenight_id, token) {
 | 
				
			||||||
 | 
					  return fetchResource(`api/gamenights/${gamenight_id}`, {
 | 
				
			||||||
 | 
					    method: 'GET',
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      'Authorization': `Bearer ${token}`
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function post_gamenight(input, token) {
 | 
				
			||||||
 | 
					  return fetchResource('api/gamenights', {
 | 
				
			||||||
 | 
					    method: 'POST',
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      'Content-Type': 'application/json',
 | 
				
			||||||
 | 
					      'Authorization': `Bearer ${token}`
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    body: JSON.stringify(input)
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function patch_gamenight(gamenight_id, input, token) {
 | 
				
			||||||
 | 
					  return fetchResource(`api/gamenights/${gamenight_id}`, {
 | 
				
			||||||
 | 
					    method: 'PATCH',
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      'Content-Type': 'application/json',
 | 
				
			||||||
 | 
					      'Authorization': `Bearer ${token}`
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    body: JSON.stringify(input)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function delete_gamenight(input, token) {
 | 
				
			||||||
 | 
					  return fetchResource('api/gamenights', {
 | 
				
			||||||
 | 
					    method: 'DELETE',
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      'Content-Type': 'application/json',
 | 
				
			||||||
 | 
					      'Authorization': `Bearer ${token}`
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    body: JSON.stringify(input)
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function get_games(token) {
 | 
				
			||||||
 | 
					  return fetchResource('api/games', {
 | 
				
			||||||
 | 
					    method: 'GET',
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      'Authorization': `Bearer ${token}`
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function login(body) {
 | 
				
			||||||
 | 
					  return fetchResource('api/login', {
 | 
				
			||||||
 | 
					    method: 'POST',
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					        'Content-Type': 'application/json'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    body: JSON.stringify(body)
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										73
									
								
								frontend/src/api/FetchResource.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								frontend/src/api/FetchResource.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					const API_URL = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function ApiError(message, data, status) {
 | 
				
			||||||
 | 
					  let response = null;
 | 
				
			||||||
 | 
					  let isObject = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    response = JSON.parse(data);
 | 
				
			||||||
 | 
					    isObject = true;
 | 
				
			||||||
 | 
					  } catch (e) {
 | 
				
			||||||
 | 
					    response = data;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this.response = response;
 | 
				
			||||||
 | 
					  this.message = message;
 | 
				
			||||||
 | 
					  this.status = status;
 | 
				
			||||||
 | 
					  this.toString = function () {
 | 
				
			||||||
 | 
					    return `${ this.message }\nResponse:\n${ isObject ? JSON.stringify(this.response, null, 2) : this.response }`;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fetchResource = (path, userOptions = {}) => {
 | 
				
			||||||
 | 
					  const defaultOptions = {};
 | 
				
			||||||
 | 
					  const defaultHeaders = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const options = {
 | 
				
			||||||
 | 
					    ...defaultOptions,
 | 
				
			||||||
 | 
					    ...userOptions,
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      ...defaultHeaders,
 | 
				
			||||||
 | 
					      ...userOptions.headers,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const url = `${ API_URL }/${ path }`;
 | 
				
			||||||
 | 
					  const isFile = options.body instanceof File;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (options.body && typeof options.body === 'object' && !isFile) {
 | 
				
			||||||
 | 
					    options.body = JSON.stringify(options.body);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let response = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fetch(url, options)
 | 
				
			||||||
 | 
					    .then(responseObject => {
 | 
				
			||||||
 | 
					      response = responseObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (response.status === 401) {
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (response.status < 200 || response.status >= 300) {
 | 
				
			||||||
 | 
					        return response.text();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return response.json();
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .then(parsedResponse => {
 | 
				
			||||||
 | 
					      if (response.status < 200 || response.status >= 300) {
 | 
				
			||||||
 | 
					        throw parsedResponse;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return parsedResponse;
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .catch(error => {
 | 
				
			||||||
 | 
					      if (response) {
 | 
				
			||||||
 | 
					        throw new ApiError(`Request failed with status ${ response.status }.`, error, response.status);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        throw new ApiError(error.toString(), null, 'REQUEST_FAILED');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default fetchResource;
 | 
				
			||||||
@ -9,6 +9,7 @@ import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
 | 
				
			|||||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
 | 
					import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import GameAdder from './GameAdder';
 | 
					import GameAdder from './GameAdder';
 | 
				
			||||||
 | 
					import { post_gamenight, unpack_api_result} from '../api/Api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function AddGameNight(props) {
 | 
					function AddGameNight(props) {
 | 
				
			||||||
  const [expanded, setExpanded] = useState(false);
 | 
					  const [expanded, setExpanded] = useState(false);
 | 
				
			||||||
@ -44,32 +45,14 @@ function AddGameNight(props) {
 | 
				
			|||||||
        game_list: gameList,
 | 
					        game_list: gameList,
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const requestOptions = {
 | 
					      unpack_api_result(post_gamenight(input, props.user.jwt), props.setFlash)
 | 
				
			||||||
        method: 'POST',
 | 
					        .then(result => {
 | 
				
			||||||
        headers: {
 | 
					 | 
				
			||||||
          'Content-Type': 'application/json',
 | 
					 | 
				
			||||||
          'Authorization': `Bearer ${props.user.jwt}`
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        body: JSON.stringify(input)
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      fetch('api/gamenights', requestOptions)
 | 
					 | 
				
			||||||
        .then(response => response.json())
 | 
					 | 
				
			||||||
        .then(data => {
 | 
					 | 
				
			||||||
          if(data.result !== "Ok") {
 | 
					 | 
				
			||||||
            props.setFlash({
 | 
					 | 
				
			||||||
              type: "Error",
 | 
					 | 
				
			||||||
              message: data.message
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
          setExpanded(false);
 | 
					          setExpanded(false);
 | 
				
			||||||
          setGameName("");
 | 
					          setGameName("");
 | 
				
			||||||
          setDate(null);
 | 
					          setDate(null);
 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .then(() => props.refetchGamenights())
 | 
					        .then(() => props.refetchGamenights())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if(expanded) {
 | 
					  if(expanded) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, { useState } from 'react';
 | 
					import React, { useState, useEffect } from 'react';
 | 
				
			||||||
import List from '@mui/material/List';
 | 
					import List from '@mui/material/List';
 | 
				
			||||||
import ListItem from '@mui/material/ListItem';
 | 
					import ListItem from '@mui/material/ListItem';
 | 
				
			||||||
import ListItemText from '@mui/material/ListItemText';
 | 
					import ListItemText from '@mui/material/ListItemText';
 | 
				
			||||||
@ -6,15 +6,26 @@ import ListSubheader from '@mui/material/ListSubheader';
 | 
				
			|||||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
 | 
					import ArrowBackIcon from '@mui/icons-material/ArrowBack';
 | 
				
			||||||
import IconButton from '@mui/material/IconButton';
 | 
					import IconButton from '@mui/material/IconButton';
 | 
				
			||||||
import Typography from '@mui/material/Typography';
 | 
					import Typography from '@mui/material/Typography';
 | 
				
			||||||
 | 
					import Button from '@mui/material/Button';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import moment from 'moment';
 | 
					import moment from 'moment';
 | 
				
			||||||
 | 
					import {unpack_api_result, get_gamenight, patch_gamenight} from '../api/Api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Gamenight(props) {
 | 
					function Gamenight(props) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [dense, setDense] = useState(true);
 | 
					  const [dense, setDense] = useState(true);
 | 
				
			||||||
 | 
					  const [gamenight, setGamenight] = useState(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let games = props.gamenight.game_list.map(g =>
 | 
					  const fetchGamenight = () => {
 | 
				
			||||||
 | 
					    if (props.user !== null) {
 | 
				
			||||||
 | 
					      unpack_api_result(get_gamenight(props.gamenightId, props.user.jwt), props.setFlash)
 | 
				
			||||||
 | 
					        .then(result => setGamenight(result.gamenight));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(fetchGamenight, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let games = gamenight?.game_list.map(g =>
 | 
				
			||||||
    (
 | 
					    (
 | 
				
			||||||
      <ListItem>
 | 
					      <ListItem>
 | 
				
			||||||
        <ListItemText
 | 
					        <ListItemText
 | 
				
			||||||
@ -24,7 +35,7 @@ function Gamenight(props) {
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let participants = props.gamenight.participants.map(p =>
 | 
					  const participants = gamenight?.participants.map(p =>
 | 
				
			||||||
    (
 | 
					    (
 | 
				
			||||||
      <ListItem>
 | 
					      <ListItem>
 | 
				
			||||||
        <ListItemText
 | 
					        <ListItemText
 | 
				
			||||||
@ -32,7 +43,46 @@ function Gamenight(props) {
 | 
				
			|||||||
        />
 | 
					        />
 | 
				
			||||||
      </ListItem>
 | 
					      </ListItem>
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Join = () => {
 | 
				
			||||||
 | 
					    const input = {
 | 
				
			||||||
 | 
					      action: 'AddParticipant'
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    unpack_api_result(patch_gamenight(gamenight.id, input, props.user.jwt), props.setFlash)
 | 
				
			||||||
 | 
					      .then(() => fetchGamenight());
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Leave = () => {
 | 
				
			||||||
 | 
					    const input = {
 | 
				
			||||||
 | 
					      action: 'RemoveParticipant',
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unpack_api_result(patch_gamenight(gamenight.id, input, props.user.jwt), props.setFlash)
 | 
				
			||||||
 | 
					      .then(() => fetchGamenight());
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let join_or_leave_button;
 | 
				
			||||||
 | 
					  if(gamenight?.participants.find(p => p.id === props.user.id) === undefined) {
 | 
				
			||||||
 | 
					    join_or_leave_button = (
 | 
				
			||||||
 | 
					      <Button 
 | 
				
			||||||
 | 
					        variant="outlined"
 | 
				
			||||||
 | 
					        color="success"
 | 
				
			||||||
 | 
					        onClick={Join}>
 | 
				
			||||||
 | 
					        Join
 | 
				
			||||||
 | 
					      </Button>
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    join_or_leave_button = (
 | 
				
			||||||
 | 
					      <Button 
 | 
				
			||||||
 | 
					        variant="outlined"
 | 
				
			||||||
 | 
					        color="error"
 | 
				
			||||||
 | 
					        onClick={Leave}>
 | 
				
			||||||
 | 
					        Leave
 | 
				
			||||||
 | 
					      </Button>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
@ -43,10 +93,10 @@ function Gamenight(props) {
 | 
				
			|||||||
      </IconButton>
 | 
					      </IconButton>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Typography type="h3">
 | 
					      <Typography type="h3">
 | 
				
			||||||
        {props.gamenight.name}
 | 
					        {gamenight?.name}
 | 
				
			||||||
      </Typography>
 | 
					      </Typography>
 | 
				
			||||||
      <Typography type="body1">
 | 
					      <Typography type="body1">
 | 
				
			||||||
        When: {moment(props.gamenight.datetime).format('LL HH:mm')}
 | 
					        When: {moment(gamenight?.datetime).format('LL HH:mm')}
 | 
				
			||||||
      </Typography>
 | 
					      </Typography>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <List 
 | 
					      <List 
 | 
				
			||||||
@ -69,6 +119,7 @@ function Gamenight(props) {
 | 
				
			|||||||
        }>
 | 
					        }>
 | 
				
			||||||
        {participants}
 | 
					        {participants}
 | 
				
			||||||
      </List>
 | 
					      </List>
 | 
				
			||||||
 | 
					      {join_or_leave_button}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,35 +9,15 @@ import GamesIcon from '@mui/icons-material/Games';
 | 
				
			|||||||
import DeleteIcon from '@mui/icons-material/Delete';
 | 
					import DeleteIcon from '@mui/icons-material/Delete';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import AddGameNight from './AddGameNight';
 | 
					import AddGameNight from './AddGameNight';
 | 
				
			||||||
 | 
					import {delete_gamenight, unpack_api_result} from '../api/Api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Gamenights(props) {
 | 
					function Gamenights(props) {
 | 
				
			||||||
  const [dense, setDense] = React.useState(false);
 | 
					  const [dense, setDense] = React.useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const DeleteGamenight = (gameId) => {
 | 
					  const DeleteGamenight = (game_id) => {
 | 
				
			||||||
    if (props.user !== null) {
 | 
					    if (props.user !== null) {
 | 
				
			||||||
      let input = {
 | 
					      const input = { game_id: game_id };
 | 
				
			||||||
        game_id: gameId,
 | 
					      unpack_api_result(delete_gamenight(input, props.user.jwt), props.setFlash)
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
      const requestOptions = {
 | 
					 | 
				
			||||||
        method: 'DELETE',
 | 
					 | 
				
			||||||
        headers: {
 | 
					 | 
				
			||||||
          'Content-Type': 'application/json',
 | 
					 | 
				
			||||||
          'Authorization': `Bearer ${props.user.jwt}`
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        body: JSON.stringify(input)
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
      fetch('api/gamenights', requestOptions)
 | 
					 | 
				
			||||||
        .then(response => response.json())
 | 
					 | 
				
			||||||
        .then(data => {
 | 
					 | 
				
			||||||
          if(data.result !== "Ok") {
 | 
					 | 
				
			||||||
            props.setFlash({
 | 
					 | 
				
			||||||
              type: "Error",
 | 
					 | 
				
			||||||
              message: data.message
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .then(() => props.refetchGamenights());
 | 
					        .then(() => props.refetchGamenights());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user