From 14e8f369ea26c6fcd1c5de47504c1cfe089fd8b3 Mon Sep 17 00:00:00 2001 From: emilis Date: Wed, 18 Feb 2026 23:06:46 +0000 Subject: [PATCH] host: change seat number for players --- Cargo.lock | 7 ++ Cargo.toml | 1 + style/main.scss | 68 ++++++++++---- werewolves-proto/src/message/dead.rs | 19 ++++ werewolves/Cargo.toml | 1 + werewolves/src/app/components/input/number.rs | 75 +++++++++++++++ werewolves/src/app/components/modal.rs | 69 +++++++------- werewolves/src/app/pages/game/host.rs | 3 +- werewolves/src/app/pages/game/host/players.rs | 91 +++++++++++++++++-- .../src/app/pages/game/host/settings.rs | 5 +- werewolves/src/app/pages/game/player.rs | 55 +---------- werewolves/src/app/pages/game/player/lobby.rs | 4 +- .../pages/user_settings/change_password.rs | 2 +- .../app/pages/user_settings/update_profile.rs | 2 +- 14 files changed, 280 insertions(+), 122 deletions(-) create mode 100644 werewolves/src/app/components/input/number.rs diff --git a/Cargo.lock b/Cargo.lock index d1c8b64..4923757 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3064,6 +3064,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "sorted-vec" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f58d7b0190c7f12df7e8be6b79767a0836059159811b869d5ab55721fe14d0" + [[package]] name = "spin" version = "0.9.8" @@ -4053,6 +4059,7 @@ dependencies = [ "rand 0.9.2", "reactive_stores", "serde", + "sorted-vec", "sqlx", "tokio", "tower-http", diff --git a/Cargo.toml b/Cargo.toml index 7cb7101..7ed3228 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,6 +124,7 @@ ciborium = { version = "0.2" } pretty_assertions = { version = "1.4" } colored = { version = "3.1" } pretty_env_logger = { version = "0.5" } +sorted-vec = { version = "0.8" } [profile.dev] opt-level = 0 diff --git a/style/main.scss b/style/main.scss index 18ea6a7..d6803c7 100644 --- a/style/main.scss +++ b/style/main.scss @@ -241,6 +241,10 @@ dialog::backdrop { &.full { border-bottom: 1px solid white; } + + .headline { + user-select: none; + } } } @@ -281,26 +285,7 @@ dialog::backdrop { max-width: 70%; } - form { - font-size: 1em; - display: flex; - flex-direction: column; - gap: 1ch; - align-items: center; - .form-fields { - display: flex; - flex-direction: column; - align-items: center; - // width: 100%; - gap: 1ch; - } - - - label { - user-select: none; - } - } } .user-settings-list { @@ -659,13 +644,24 @@ dialog .tab-content { } } +.host-seat-changer { + width: min-content; + + input[type=submit] { + width: 100%; + } +} + .number-update:not([hidden]) { display: flex; flex-direction: column; flex-wrap: nowrap; gap: 0.25ch; margin-top: 3ch; - font-size: 1.5em; + + .bigger { + font-size: 1.5em; + } input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { @@ -756,3 +752,35 @@ dialog .tab-content { border: 1px solid red; background-color: black; } + +.player-options { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + max-width: 80vw; + gap: 0.25ch; + min-width: 45vw; + + &>* { + width: 100%; + } +} + +form { + font-size: 1em; + display: flex; + flex-direction: column; + gap: 0.25ch; + align-items: center; + + .form-fields { + display: flex; + flex-direction: column; + align-items: center; + gap: 1ch; + } + + label { + user-select: none; + } +} diff --git a/werewolves-proto/src/message/dead.rs b/werewolves-proto/src/message/dead.rs index 8c3870a..9f63a58 100644 --- a/werewolves-proto/src/message/dead.rs +++ b/werewolves-proto/src/message/dead.rs @@ -13,6 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use core::hash::Hash; use std::collections::HashMap; use chrono::{DateTime, TimeDelta, Utc}; @@ -204,6 +205,24 @@ pub struct DeadChatMessage { pub message: DeadChatContent, } +impl Hash for DeadChatMessage { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +impl Ord for DeadChatMessage { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + +impl PartialOrd for DeadChatMessage { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum DeadChatContent { PlayerMessage { diff --git a/werewolves/Cargo.toml b/werewolves/Cargo.toml index 72cc22d..60e9ab1 100644 --- a/werewolves/Cargo.toml +++ b/werewolves/Cargo.toml @@ -40,6 +40,7 @@ werewolves-macros.workspace = true werewolves-proto.workspace = true codee.workspace = true convert_case.workspace = true +sorted-vec.workspace = true [features] hydrate = [ diff --git a/werewolves/src/app/components/input/number.rs b/werewolves/src/app/components/input/number.rs new file mode 100644 index 0000000..48a2d1e --- /dev/null +++ b/werewolves/src/app/components/input/number.rs @@ -0,0 +1,75 @@ +use core::num::NonZeroU8; + +use leptos::{ + ev::{Event, MouseEvent, SubmitEvent, Targeted}, + html::Form, + prelude::*, + tachys::html::node_ref::NodeRefContainer, + web_sys::HtmlInputElement, +}; + +use crate::ConsoleLogError; + +#[component] +pub fn ChangePlayerNumber( + submitted_number: WriteSignal>, + error: WriteSignal>, +) -> impl IntoView { + let number: RwSignal> = RwSignal::new(None); + let update = move |e: Targeted| { + e.prevent_default(); + let value = e.target().value(); + if value.trim().is_empty() { + number.set(None); + return; + } + let current = number.get_untracked(); + let default = current.map(|c| c.get().to_string()).unwrap_or_default(); + let value_u8 = match e.target().value().trim().parse::() { + Ok(v) => v, + Err(err) => { + log::error!("{err}"); + e.target().set_value(default.as_str()); + return; + } + }; + if let Some(nz) = NonZeroU8::new(value_u8) { + number.set(Some(nz)); + } else { + e.target().set_value(default.as_str()); + } + }; + let submit = move |ev: SubmitEvent| { + ev.prevent_default(); + log::warn!("called submit with number: {:?}", number.get()); + let Some(num) = number.get() else { + error.set(Some("please set a number".into())); + return; + }; + submitted_number.set(Some(num)); + }; + let submit_click = move |ev: MouseEvent| { + ev.prevent_default(); + log::warn!("called submit with number: {:?}", number.get()); + let Some(num) = number.get() else { + error.set(Some("please set a number".into())); + return; + }; + submitted_number.set(Some(num)); + }; + move || { + view! { +
+ + + +
+ } + } +} diff --git a/werewolves/src/app/components/modal.rs b/werewolves/src/app/components/modal.rs index 41c78b5..01312b1 100644 --- a/werewolves/src/app/components/modal.rs +++ b/werewolves/src/app/components/modal.rs @@ -13,7 +13,7 @@ pub enum DialogMode { #[component] pub fn DialogModal( - text: String, + button_content: impl IntoView + Clone, #[prop(optional)] open: Option>, #[prop(optional)] button_class: String, #[prop(default = Box::new(|| ().into_any()))] mut children: ChildrenFnMut, @@ -23,15 +23,13 @@ pub fn DialogModal( // use generated id if not supplied let open = open.unwrap_or_else(|| RwSignal::new(false)); - let close_cb = { - move |ev: MouseEvent| { - ev.prevent_default(); - open.set(false); - } + let close_cb = move |ev: MouseEvent| { + ev.prevent_default(); + open.set(false); }; let close = { view! { - } @@ -48,34 +46,31 @@ pub fn DialogModal( .into_any(), }; let dialog_element: NodeRef = NodeRef::new(); - let on_backdrop_click = { - let close_cb = close_cb.clone(); - move |ev: MouseEvent| { - ev.prevent_default(); - if !close_backdrop { + let on_backdrop_click = move |ev: MouseEvent| { + ev.prevent_default(); + if !close_backdrop { + return; + } + #[cfg(feature = "hydrate")] + { + let Some(dialog) = dialog_element.get() else { + log::error!("dialog_element is None"); return; - } - #[cfg(feature = "hydrate")] - { - let Some(dialog) = dialog_element.get() else { - log::error!("dialog_element is None"); - return; - }; + }; - let Ok(Some(dialog_box)) = dialog.query_selector(".dialog-box") else { - log::error!(".dialog-box is None"); - return; - }; - let rect: leptos::web_sys::DomRect = dialog_box.get_bounding_client_rect(); + let Ok(Some(dialog_box)) = dialog.query_selector(".dialog-box") else { + log::error!(".dialog-box is None"); + return; + }; + let rect: leptos::web_sys::DomRect = dialog_box.get_bounding_client_rect(); - let is_in_dialog = rect.top() as i32 <= ev.client_y() - && ev.client_y() <= rect.top() as i32 + rect.height() as i32 - && rect.left() as i32 <= ev.client_x() - && ev.client_x() <= rect.left() as i32 + rect.width() as i32; + let is_in_dialog = rect.top() as i32 <= ev.client_y() + && ev.client_y() <= rect.top() as i32 + rect.height() as i32 + && rect.left() as i32 <= ev.client_x() + && ev.client_x() <= rect.left() as i32 + rect.width() as i32; - if !is_in_dialog { - close_cb(ev); - } + if !is_in_dialog { + close_cb(ev); } } }; @@ -97,11 +92,13 @@ pub fn DialogModal( ev.prevent_default(); open.set(true); }; - let button = text.is_empty().not().then_some(view! { - - }); + let button = { + view! { + + } + }; view! {
{button} {modal}
} } diff --git a/werewolves/src/app/pages/game/host.rs b/werewolves/src/app/pages/game/host.rs index fc919c8..f9b5971 100644 --- a/werewolves/src/app/pages/game/host.rs +++ b/werewolves/src/app/pages/game/host.rs @@ -88,6 +88,7 @@ pub fn HostGamePage( HostPage::None => ().into_any(), HostPage::Settings => view! {