Implemented deleting games as admin.

This commit is contained in:
2026-01-02 15:24:38 +01:00
parent 0c256846f6
commit 5c928b30f3
26 changed files with 344 additions and 133 deletions

View File

@@ -72,9 +72,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
name = "cc"
version = "1.2.50"
version = "1.2.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c"
checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -193,9 +193,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
[[package]]
name = "foreign-types"
@@ -635,9 +635,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "iri-string"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397"
checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a"
dependencies = [
"memchr",
"serde",
@@ -645,9 +645,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.16"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "js-sys"
@@ -676,9 +676,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.178"
version = "0.2.179"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
[[package]]
name = "linux-raw-sys"
@@ -936,9 +936,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
version = "1.0.103"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
dependencies = [
"unicode-ident",
]
@@ -969,9 +969,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.12.26"
version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64",
"bytes",
@@ -1021,9 +1021,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
dependencies = [
"bitflags 2.10.0",
"errno",
@@ -1049,9 +1049,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.21"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea"
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
[[package]]
name = "schannel"
@@ -1123,15 +1123,15 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.146"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "217ca874ae0207aac254aa02c957ded05585a90892cc8d87f9e5fa49669dadd8"
checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
"zmij",
]
[[package]]
@@ -1186,10 +1186,11 @@ dependencies = [
[[package]]
name = "signal-hook-registry"
version = "1.4.7"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
@@ -1235,9 +1236,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.111"
version = "2.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4"
dependencies = [
"proc-macro2",
"quote",
@@ -1266,9 +1267,9 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.23.0"
version = "3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
dependencies = [
"fastrand",
"getrandom 0.3.4",
@@ -1349,9 +1350,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.48.0"
version = "1.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
dependencies = [
"bytes",
"libc",
@@ -2010,3 +2011,9 @@ dependencies = [
"quote",
"syn",
]
[[package]]
name = "zmij"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e0d8dffbae3d840f64bda38e28391faef673a7b5a6017840f2a106c8145868"

View File

@@ -1,19 +1,27 @@
use std::fmt::Display;
use std::fmt::{Display, Formatter};
use chrono::{DateTime, Local};
use uuid::Uuid;
use crate::domain::owned_games::OwnedGames;
use crate::domain::participants::Participants;
use crate::domain::user::User;
#[derive(Clone)]
pub struct Gamenight {
pub id: Uuid,
pub name: String,
pub start_time: DateTime<Local>,
pub owner_id: Uuid,
pub organizer: User,
pub participants: Participants,
pub owned_games: OwnedGames,
}
impl Display for Gamenight {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, r#"Name: {}
When: {}"#, self.name, self.start_time.format("%d-%m-%Y %H:%M"))
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Name: {}", self.name)?;
writeln!(f, "Organizer: {}", self.organizer)?;
writeln!(f, "Start: {}", self.start_time.to_rfc3339())?;
writeln!(f, "Participants: {}", self.participants)?;
write!(f, "Owned games:\n{}", self.owned_games)?;
Ok(())
}
}

View File

@@ -0,0 +1,19 @@
use std::fmt::Display;
use chrono::{DateTime, Local};
use uuid::Uuid;
#[derive(Clone)]
pub struct GamenightSelectData {
pub id: Uuid,
pub name: String,
pub start_time: DateTime<Local>,
pub owner_id: Uuid,
}
impl Display for GamenightSelectData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, r#"Name: {}
When: {}"#, self.name, self.start_time.format("%d-%m-%Y %H:%M"))
}
}

View File

@@ -1,7 +1,9 @@
pub mod gamenight;
pub mod gamenight_select_data;
pub mod user;
pub mod config;
pub mod participants;
pub mod game;
pub mod owned_games;
pub mod location;
pub mod location;
pub mod location_select_data;
pub mod gamenight;

View File

@@ -2,12 +2,12 @@ use std::{collections::HashMap, fmt::Display};
use crate::domain::game::Game;
#[derive(Clone)]
pub struct OwnedGames(pub HashMap<String, Vec<Game>>);
pub struct OwnedGames<'a>(pub &'a HashMap<String, Vec<Game>>);
impl<'a> Display for OwnedGames<'a> {
impl Display for OwnedGames {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (k,v) in self.0 {
for (k,v) in &self.0 {
write!(f, "{k}:\n")?;
for g in v {
write!(f, "\t{}\n", g.name)?;

View File

@@ -2,9 +2,10 @@ use std::fmt::Display;
use crate::domain::user::User;
pub struct Participants<'a>(pub &'a Vec<User>);
#[derive(Clone)]
pub struct Participants(pub Vec<User>);
impl<'a> Display for Participants<'a> {
impl<'a> Display for Participants {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let string_list: Vec<String> = self.0.iter().map(|x| {format!("{x}")}).collect();
write!(f, "{}", string_list.join(", "))

View File

@@ -1,6 +1,7 @@
use std::fmt::Display;
use gamenight_api_client_rs::models;
use uuid::Uuid;
use crate::flows::FlowError;
#[derive(Clone)]
pub struct User {
@@ -13,4 +14,15 @@ impl Display for User {
write!(f, "{}", self.username)
}
}
impl TryFrom<models::User> for User {
type Error = FlowError;
fn try_from(user: models::User) -> Result<Self, Self::Error> {
Ok(Self {
username: user.username,
id: Uuid::parse_str(&user.id)?
})
}
}

View File

@@ -1,12 +1,10 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::game_post, models::AddGameRequestBody, models::OwnGameRequestBody};
use gamenight_api_client_rs::apis::default_api::{locations_get, own_post};
use inquire::{Confirm, Select, Text};
use crate::flows::flow_helpers::LocationSelectData;
use crate::flows::own::Own;
use super::*;
use crate::flows::own::Own;
use async_trait::async_trait;
use gamenight_api_client_rs::{apis::default_api::game_post, models::AddGameRequestBody};
use inquire::Text;
#[derive(Clone)]

View File

@@ -4,7 +4,7 @@ use gamenight_api_client_rs::apis::default_api::get_gamenights;
use inquire::Select;
use uuid::Uuid;
use crate::{domain::{gamenight::Gamenight}, flows::view_gamenight::ViewGamenight};
use crate::{domain::{gamenight_select_data::GamenightSelectData}, flows::view_gamenight::ViewGamenight};
use super::{exit::Exit, *};
@@ -27,7 +27,7 @@ impl<'a> Flow<'a> for ListGamenights {
let mut view_flows: Vec<Box<dyn Flow<'_> + Send>> = vec![];
for response in response.iter() {
let gamenight = Gamenight {
let gamenight = GamenightSelectData {
id: Uuid::parse_str(&response.id)?,
name: response.name.clone(),
start_time:DateTime::parse_from_rfc3339(&response.datetime)?.into(),

View File

@@ -7,7 +7,6 @@ use crate::flows::{add_location::AddLocation, exit::Exit, list_locations::ListLo
use super::*;
#[derive(Clone)]
pub struct Locations {
}

View File

@@ -35,7 +35,7 @@ mod list_locations;
mod view_location;
mod add_location;
mod location_authorize;
mod flow_helpers;
mod remove_game;
pub struct GamenightState {
api_configuration: Configuration,
@@ -140,7 +140,7 @@ impl From<jsonwebtoken::errors::Error> for FlowError {
pub enum FlowOutcome {
Successful,
Cancelled,
Abort
Abort,
}
type FlowResult<'a> = Result<(FlowOutcome, &'a mut GamenightState), FlowError>;

View File

@@ -1,7 +1,7 @@
use std::fmt::Display;
use super::{Flow, FlowError, FlowOutcome, FlowResult, GamenightState};
use crate::flows::flow_helpers::LocationSelectData;
use crate::domain::location_select_data::LocationSelectData;
use async_trait::async_trait;
use gamenight_api_client_rs::apis::default_api::locations_get;
use gamenight_api_client_rs::models::OwnGameRequestBody;

View File

@@ -0,0 +1,38 @@
use std::fmt::Display;
use async_trait::async_trait;
use gamenight_api_client_rs::models::GameId;
use super::{Flow, FlowOutcome, FlowResult, GamenightState};
use crate::domain::game::Game;
#[derive(Clone)]
pub struct RemoveGame {
pub game: Game
}
impl RemoveGame {
pub fn new(game: Game) -> Self {
Self{
game
}
}
}
#[async_trait]
impl<'a> Flow<'a> for RemoveGame {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let req = GameId {
game_id: self.game.id.to_string()
};
gamenight_api_client_rs::apis::default_api::game_delete(&state.api_configuration, Some(req)).await?;
//Hack to return to right stack item, skipping detail view that doesn't exist.
Ok((FlowOutcome::Abort, state))
}
}
impl Display for RemoveGame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Remove")
}
}

View File

@@ -4,7 +4,7 @@ use inquire::Select;
use uuid::Uuid;
use crate::{domain::game::Game, flows::{disown::Disown, exit::Exit, own::Own, rename_game::RenameGame}};
use crate::flows::remove_game::RemoveGame;
use super::*;
#[derive(Clone)]
@@ -45,6 +45,7 @@ impl<'a> Flow<'a> for ViewGame {
let options: Vec<Box<dyn Flow<'a> + Send>> = vec![
own_or_disown,
Box::new(RemoveGame::new(game.clone())),
Box::new(RenameGame::new(game.clone())),
Box::new(Exit::new())
];

View File

@@ -1,24 +1,23 @@
use crate::domain::gamenight::Gamenight;
use std::collections::HashMap;
use futures::future::join_all;
use gamenight_api_client_rs::{apis::default_api::{game_get, owned_games_get, participants_get, user_get, GameGetError}, models::{self, GameId, GamenightId, UserId}};
use inquire::Select;
use uuid::Uuid;
use crate::{domain::{game::Game, gamenight::Gamenight, owned_games::OwnedGames, participants::Participants, user::User}, flows::{exit::Exit, join::Join, leave::Leave}};
use crate::{domain::{game::Game, gamenight_select_data::GamenightSelectData, owned_games::OwnedGames, participants::Participants}, flows::{exit::Exit, join::Join, leave::Leave}};
use super::*;
#[derive(Clone)]
pub struct ViewGamenight {
gamenight: Gamenight
pub gamenight_select_data: GamenightSelectData,
}
impl ViewGamenight {
pub fn new(gamenight: Gamenight) -> Self {
pub fn new(gamenight_select_data: GamenightSelectData) -> Self {
Self {
gamenight
gamenight_select_data
}
}
}
@@ -26,38 +25,41 @@ impl ViewGamenight {
#[async_trait]
impl<'a> Flow<'a> for ViewGamenight {
async fn run(&self, state: &'a mut GamenightState) -> FlowResult<'a> {
let participants = participants_get(&state.api_configuration, Some(GamenightId{gamenight_id: self.gamenight.id.to_string()})).await?;
let mut users = vec![];
let mut gamenight = Gamenight {
id: self.gamenight_select_data.id,
start_time: self.gamenight_select_data.start_time,
name: self.gamenight_select_data.name.clone(),
organizer: user_get(&state.api_configuration, Some(UserId{user_id: self.gamenight_select_data.owner_id.to_string()})).await?.try_into()?,
participants: Participants(vec![]),
owned_games: OwnedGames(HashMap::new())
};
let participants = participants_get(&state.api_configuration, Some(GamenightId{gamenight_id: gamenight.id.to_string()})).await?;
for participant in participants.participants.iter() {
let user = user_get(&state.api_configuration, Some(UserId{user_id: participant.clone()})).await?;
users.push(User {
id: Uuid::parse_str(&user.id)?,
username: user.username
});
gamenight.participants.0.push(user_get(&state.api_configuration, Some(UserId{user_id: participant.clone()})).await?.try_into()?);
}
let mut user_games: HashMap<String, Vec<Game>> = HashMap::new();
for user in &users {
for user in &gamenight.participants.0 {
let request = UserId{ user_id: user.id.to_string() };
let games: Vec<Game> = join_all(owned_games_get(&state.api_configuration, Some(request)).await?
.iter().map(async |game_id| -> Result<models::Game, Error<GameGetError>> {
let request = GameId{ game_id: game_id.clone() };
game_get(&state.api_configuration, Some(request)).await
})).await.into_iter().collect::<Result<Vec<models::Game>, Error<GameGetError>>>()?.into_iter().map(Into::into).collect();
user_games.insert(user.username.clone(), games);
gamenight.owned_games.0.insert(user.username.clone(), games);
}
println!("{}\nwho: {}\nGames:\n{}", self.gamenight, Participants(&users), OwnedGames(&user_games));
println!("{}", gamenight);
let my_uid = state.get_user_id()?;
let join_or_leave: Box<dyn Flow<'a> + Send> =
if users.iter().map(|x| {x.id}).any(|x| x == my_uid) {
Box::new(Leave::new(self.gamenight.id))
if gamenight.participants.0.iter().map(|x| {x.id}).any(|x| x == my_uid) {
Box::new(Leave::new(gamenight.id))
}
else {
Box::new(Join::new(self.gamenight.id))
else {
Box::new(Join::new(gamenight.id))
};
let options: Vec<Box<dyn Flow<'a> + Send>> = vec![
@@ -71,9 +73,9 @@ impl<'a> Flow<'a> for ViewGamenight {
}
}
impl Display for ViewGamenight {
impl<'a> Display for ViewGamenight {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.gamenight.name, self.gamenight.start_time.format("%d-%m-%Y %H:%M"))
write!(f, "{} {}", self.gamenight_select_data.name, self.gamenight_select_data.start_time.format("%d-%m-%Y %H:%M"))
}
}