player list styling
This commit is contained in:
parent
8c2791054a
commit
cbeee94113
|
|
@ -28,12 +28,12 @@ pub async fn handler(
|
||||||
.map(|x| x.to_string())
|
.map(|x| x.to_string())
|
||||||
.unwrap_or_else(|| addr.to_string())
|
.unwrap_or_else(|| addr.to_string())
|
||||||
.italic();
|
.italic();
|
||||||
log::info!(
|
// log::debug!(
|
||||||
"{who}{} connected.",
|
// "{who}{} connected.",
|
||||||
user_agent
|
// user_agent
|
||||||
.map(|agent| format!(" (User-Agent: {})", agent.as_str()))
|
// .map(|agent| format!(" (User-Agent: {})", agent.as_str()))
|
||||||
.unwrap_or_default(),
|
// .unwrap_or_default(),
|
||||||
);
|
// );
|
||||||
let player_list = state.joined_players;
|
let player_list = state.joined_players;
|
||||||
|
|
||||||
// finalize the upgrade process by returning upgrade callback.
|
// finalize the upgrade process by returning upgrade callback.
|
||||||
|
|
@ -46,7 +46,7 @@ pub async fn handler(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
log::info!("connected {who} as {ident}");
|
// log::debug!("connected {who} as {ident}");
|
||||||
let connection_id = ConnectionId::new(ident.player_id.clone());
|
let connection_id = ConnectionId::new(ident.player_id.clone());
|
||||||
let recv = {
|
let recv = {
|
||||||
let (send, recv) = tokio::sync::broadcast::channel(100);
|
let (send, recv) = tokio::sync::broadcast::channel(100);
|
||||||
|
|
@ -76,7 +76,7 @@ pub async fn handler(
|
||||||
.run()
|
.run()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
log::info!("ending connection with {who}");
|
// log::debug!("ending connection with {who}");
|
||||||
player_list.disconnect(&connection_id).await;
|
player_list.disconnect(&connection_id).await;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -178,11 +178,11 @@ impl Client {
|
||||||
}
|
}
|
||||||
Message::Pong(_) => return Ok(()),
|
Message::Pong(_) => return Ok(()),
|
||||||
Message::Close(Some(close_frame)) => {
|
Message::Close(Some(close_frame)) => {
|
||||||
log::debug!("sent close frame: {close_frame:?}");
|
// log::debug!("sent close frame: {close_frame:?}");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Message::Close(None) => {
|
Message::Close(None) => {
|
||||||
log::debug!("host closed connection");
|
// log::debug!("host closed connection");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use super::{HostComms, player::PlayerIdComms};
|
||||||
|
|
||||||
pub struct LobbyComms {
|
pub struct LobbyComms {
|
||||||
comms: Comms,
|
comms: Comms,
|
||||||
|
// TODO: move this to not use a receiver
|
||||||
connect_recv: Receiver<(PlayerId, bool)>,
|
connect_recv: Receiver<(PlayerId, bool)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,13 +35,17 @@ impl LobbyComms {
|
||||||
pub async fn next_message(&mut self) -> Result<Message, GameError> {
|
pub async fn next_message(&mut self) -> Result<Message, GameError> {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
r = self.comms.message() => {
|
r = self.comms.message() => {
|
||||||
r
|
match r {
|
||||||
|
Ok(val) => Ok(val),
|
||||||
|
Err(GameError::GenericError(err)) => Err(GameError::GenericError(format!("comms message: {err}"))),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
r = self.connect_recv.recv() => {
|
r = self.connect_recv.recv() => {
|
||||||
match r {
|
match r {
|
||||||
Ok((player_id, true)) => Ok(Message::Connect(player_id)),
|
Ok((player_id, true)) => Ok(Message::Connect(player_id)),
|
||||||
Ok((player_id, false)) => Ok(Message::Disconnect(player_id)),
|
Ok((player_id, false)) => Ok(Message::Disconnect(player_id)),
|
||||||
Err(err) => Err(GameError::GenericError(err.to_string())),
|
Err(err) => Err(GameError::GenericError(format!("connect recv: {err}"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
@use 'sass:color';
|
||||||
|
|
||||||
$wolves_color: rgba(255, 0, 0, 0.7);
|
$wolves_color: rgba(255, 0, 0, 0.7);
|
||||||
$village_color: rgba(0, 0, 255, 0.7);
|
$village_color: rgba(0, 0, 255, 0.7);
|
||||||
$connected_color: hsl(120, 68%, 50%);
|
$connected_color: hsl(120, 68%, 50%);
|
||||||
|
|
@ -94,6 +96,8 @@ nav.debug-nav {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
color: #cccccc;
|
color: #cccccc;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
width: fit-content;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
@ -104,9 +108,6 @@ nav.debug-nav {
|
||||||
|
|
||||||
.player {
|
.player {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
// padding-left: 5px;
|
|
||||||
// padding-right: 5px;
|
|
||||||
// padding-bottom: 5px;
|
|
||||||
min-width: 10rem;
|
min-width: 10rem;
|
||||||
max-width: 10vw;
|
max-width: 10vw;
|
||||||
max-height: 4rem;
|
max-height: 4rem;
|
||||||
|
|
@ -116,7 +117,6 @@ nav.debug-nav {
|
||||||
font-family: 'Cute Font';
|
font-family: 'Cute Font';
|
||||||
|
|
||||||
&.marked {
|
&.marked {
|
||||||
// background-color: brighten($village_color, 100%);
|
|
||||||
filter: hue-rotate(90deg);
|
filter: hue-rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,18 +219,17 @@ button {
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
filter: grayscale(80%);
|
background-color: rgba(128, 128, 128, 0.5);
|
||||||
}
|
color: rgb(128, 128, 128);
|
||||||
|
cursor: not-allowed;
|
||||||
&:disabled:hover {
|
|
||||||
filter: sepia(100%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled:hover::after {
|
&:disabled:hover::after {
|
||||||
content: attr(reason);
|
content: attr(reason);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
top: 90%;
|
// top: 90%;
|
||||||
|
// left: 0;
|
||||||
font: 'Cute Font';
|
font: 'Cute Font';
|
||||||
// color: #000;
|
// color: #000;
|
||||||
// background-color: #fff;
|
// background-color: #fff;
|
||||||
|
|
@ -258,11 +257,21 @@ button {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wolves-list {
|
.wolves-intro {
|
||||||
flex-wrap: wrap;
|
@extend .column-list;
|
||||||
flex-direction: row;
|
align-content: center;
|
||||||
justify-content: space-evenly;
|
width: 100%;
|
||||||
flex: 1 1 0;
|
|
||||||
|
.wolves-list {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& button {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.character {
|
.character {
|
||||||
|
|
@ -391,9 +400,12 @@ bool_role {
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-container {
|
.error-container {
|
||||||
width: 70vw;
|
position: fixed;
|
||||||
margin-left: 10vw;
|
top: 10vh;
|
||||||
margin-right: 10vw;
|
width: 100vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-container button {
|
.error-container button {
|
||||||
|
|
@ -412,14 +424,15 @@ bool_role {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 80%;
|
||||||
margin: 30px;
|
margin: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
// gap: 20px;
|
// gap: 20px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 30px;
|
gap: 30px;
|
||||||
background-color: $error_color;
|
background-color: $error_color;
|
||||||
filter: $error_filter;
|
border: 1px solid color.change($error_color, $alpha: 1.0);
|
||||||
|
backdrop-filter: grayscale(100%);
|
||||||
|
|
||||||
|
|
||||||
padding-left: 5vw;
|
padding-left: 5vw;
|
||||||
|
|
@ -507,9 +520,17 @@ clients {
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-reveal-card {
|
.role-reveal-card {
|
||||||
border: 3px solid rgba(0, 0, 0, 0.5);
|
min-width: 5cm;
|
||||||
background-color: rgba(255, 0, 0, 0.7);
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid $wolves_color;
|
||||||
|
background-color: color.change($wolves_color, $alpha: 0.1);
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
& p.number {
|
& p.number {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
|
|
@ -519,12 +540,22 @@ clients {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
&>button {
|
||||||
|
border: 1px solid $wolves_color;
|
||||||
|
$bg: color.change($wolves_color, $alpha: 0.2);
|
||||||
|
background-color: $bg;
|
||||||
|
|
||||||
.role-reveal-card.ready {
|
&:hover {
|
||||||
background-color: rgba(0, 255, 0, 0.7);
|
background-color: white;
|
||||||
}
|
color: color.change($wolves_color, $alpha: 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ready {
|
||||||
|
border: 1px solid $village_color;
|
||||||
|
background-color: color.change($village_color, $alpha: 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.pronouns {
|
.pronouns {
|
||||||
font-size: 70%;
|
font-size: 70%;
|
||||||
|
|
@ -541,6 +572,8 @@ clients {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
&.margin-20 {
|
&.margin-20 {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
|
|
@ -562,6 +595,8 @@ clients {
|
||||||
.column-list {
|
.column-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
font-family: 'Cute Font';
|
font-family: 'Cute Font';
|
||||||
|
|
||||||
|
|
@ -607,6 +642,12 @@ clients {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
& button {
|
||||||
|
width: fit-content;
|
||||||
|
text-align: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.small {
|
.small {
|
||||||
|
|
@ -767,11 +808,6 @@ input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom {
|
|
||||||
zoom: 200%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.game-start-role {
|
.game-start-role {
|
||||||
@extend .column-list;
|
@extend .column-list;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
@ -826,3 +862,41 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.character-picker {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
color: white;
|
||||||
|
$marked_bg: color.change($wolves_color, $alpha: 0.3);
|
||||||
|
$marked_border: color.change($wolves_color, $alpha: 1.0);
|
||||||
|
$village_bg: color.change($village_color, $alpha: 0.3);
|
||||||
|
$village_border: color.change($village_color, $alpha: 1.0);
|
||||||
|
|
||||||
|
.character {
|
||||||
|
padding: 0.5cm;
|
||||||
|
|
||||||
|
& * {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.marked {
|
||||||
|
background-color: $marked_bg;
|
||||||
|
border: 1px solid $marked_border;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: $marked_border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: $village_bg;
|
||||||
|
border: 1px solid $village_border;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
background-color: $village_border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -391,9 +391,8 @@ impl Component for Host {
|
||||||
self.send.clone(),
|
self.send.clone(),
|
||||||
);
|
);
|
||||||
html! {
|
html! {
|
||||||
<>
|
|
||||||
<h2>{format!("Day {}", day.get())}</h2>
|
|
||||||
<DaytimePlayerList
|
<DaytimePlayerList
|
||||||
|
day={day}
|
||||||
marked={marked_for_execution}
|
marked={marked_for_execution}
|
||||||
big_screen={self.big_screen}
|
big_screen={self.big_screen}
|
||||||
characters={
|
characters={
|
||||||
|
|
@ -402,7 +401,6 @@ impl Component for Host {
|
||||||
on_execute={on_execute}
|
on_execute={on_execute}
|
||||||
on_mark={on_mark}
|
on_mark={on_mark}
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HostState::RoleReveal { ackd, waiting } => {
|
HostState::RoleReveal { ackd, waiting } => {
|
||||||
|
|
@ -582,15 +580,16 @@ impl Component for Host {
|
||||||
HostEvent::SetBigScreenState(state) => {
|
HostEvent::SetBigScreenState(state) => {
|
||||||
self.big_screen = state;
|
self.big_screen = state;
|
||||||
if self.big_screen
|
if self.big_screen
|
||||||
&& let Ok(Some(root)) = gloo::utils::document().query_selector("app")
|
&& let Some(root) = gloo::utils::document().document_element()
|
||||||
&& let Err(err) = root.set_attribute("style", "zoom: 200%;")
|
&& let Err(err) = root.set_attribute("style", "font-size: 3rem;")
|
||||||
{
|
{
|
||||||
log::error!("setting zoom: {err:?}");
|
log::error!("setting zoom: {err:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if state {
|
if state {
|
||||||
let (discard_send, mut discard_recv) = futures::channel::mpsc::channel(10);
|
let (mut discard_send, mut discard_recv) = futures::channel::mpsc::channel(10);
|
||||||
self.send = discard_send;
|
core::mem::swap(&mut discard_send, &mut self.send);
|
||||||
|
Box::leak(Box::new(discard_send));
|
||||||
yew::platform::spawn_local(async move {
|
yew::platform::spawn_local(async move {
|
||||||
while discard_recv.next().await.is_some() {}
|
while discard_recv.next().await.is_some() {}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -290,26 +290,24 @@ impl Component for SingleTarget {
|
||||||
.then(|| html!(<h2>{headline}</h2>));
|
.then(|| html!(<h2>{headline}</h2>));
|
||||||
|
|
||||||
let submit = target_selection.as_ref().map(|target_selection| {
|
let submit = target_selection.as_ref().map(|target_selection| {
|
||||||
let disabled = self.selected.is_none();
|
let disabled = self.selected.is_none().then_some("pick a target");
|
||||||
let target_selection = target_selection.clone();
|
let target_selection = target_selection.clone();
|
||||||
let on_click = self
|
let on_click = self
|
||||||
.selected
|
.selected
|
||||||
.clone()
|
.clone()
|
||||||
.map(|t| move |_| target_selection.emit(t.clone()));
|
.map(|t| Callback::from(move |_| target_selection.emit(t.clone())))
|
||||||
|
.unwrap_or_default();
|
||||||
html! {
|
html! {
|
||||||
<div class="button-container sp-ace">
|
<div class="button-container sp-ace">
|
||||||
<button
|
<Button disabled_reason={disabled} on_click={on_click}>
|
||||||
disabled={disabled}
|
|
||||||
onclick={on_click}
|
|
||||||
>
|
|
||||||
{"submit"}
|
{"submit"}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div class="column-list">
|
<div class="character-picker">
|
||||||
{headline}
|
{headline}
|
||||||
{children.clone()}
|
{children.clone()}
|
||||||
<div class="row-list">
|
<div class="row-list">
|
||||||
|
|
@ -344,81 +342,15 @@ pub struct TargetCardProps {
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
fn TargetCard(props: &TargetCardProps) -> Html {
|
fn TargetCard(props: &TargetCardProps) -> Html {
|
||||||
let submenu = {
|
let character_id = props.target.character_id.clone();
|
||||||
let button_text = if props.selected { "unpick" } else { "pick" };
|
let on_select = props.on_select.clone();
|
||||||
let character_id = props.target.character_id.clone();
|
let on_click = Callback::from(move |_| on_select.emit(character_id.clone()));
|
||||||
let on_select = props.on_select.clone();
|
|
||||||
let on_click = Callback::from(move |_| on_select.emit(character_id.clone()));
|
|
||||||
html! {
|
|
||||||
<nav class="submenu">
|
|
||||||
<Button on_click={on_click}>{button_text}</Button>
|
|
||||||
</nav>
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let marked = props.selected.then_some("marked");
|
let marked = props.selected.then_some("marked");
|
||||||
|
let ident: PublicIdentity = props.target.clone().into();
|
||||||
html! {
|
html! {
|
||||||
<div class={"row-list baseline margin-5"}>
|
<Button on_click={on_click} classes={classes!(marked, "character")}>
|
||||||
<div class={classes!("player", "ident", "column-list", marked)}>
|
<Identity ident={ident}/>
|
||||||
<Identity ident={Into::<PublicIdentity>::into(&props.target)} />
|
</Button>
|
||||||
{submenu}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
|
||||||
pub struct CustomTargetCardProps {
|
|
||||||
pub target: CharacterIdentity,
|
|
||||||
pub options: Arc<[String]>,
|
|
||||||
pub on_select: Option<Callback<(CharacterId, String)>>,
|
|
||||||
#[prop_or_default]
|
|
||||||
pub class: String,
|
|
||||||
#[prop_or(true)]
|
|
||||||
pub hide_submenu: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component]
|
|
||||||
pub fn CustomTargetCard(
|
|
||||||
CustomTargetCardProps {
|
|
||||||
target,
|
|
||||||
options,
|
|
||||||
on_select,
|
|
||||||
class,
|
|
||||||
hide_submenu,
|
|
||||||
}: &CustomTargetCardProps,
|
|
||||||
) -> Html {
|
|
||||||
let submenu = options.is_empty().not().then(|| {
|
|
||||||
let buttons = options
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|option| {
|
|
||||||
let on_select = on_select.clone();
|
|
||||||
let button_text = option.clone();
|
|
||||||
let character_id = target.character_id.clone();
|
|
||||||
let on_click = on_select
|
|
||||||
.map(|on_select| {
|
|
||||||
Callback::from(move |_| {
|
|
||||||
on_select.emit((character_id.clone(), option.clone()))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
html! {
|
|
||||||
<Button on_click={on_click}>{button_text}</Button>
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Html>();
|
|
||||||
html! {
|
|
||||||
<nav class={classes!("submenu", hide_submenu.not().then_some("shown"))}>
|
|
||||||
{buttons}
|
|
||||||
</nav>
|
|
||||||
}
|
|
||||||
});
|
|
||||||
html! {
|
|
||||||
<div class={"row-list baseline"}>
|
|
||||||
<div class={classes!("ident", "column-list", class)}>
|
|
||||||
<Identity ident={Into::<PublicIdentity>::into(target)} />
|
|
||||||
{submenu}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ pub fn WolvesIntro(props: &WolvesIntroProps) -> Html {
|
||||||
let on_complete = props.on_complete.clone();
|
let on_complete = props.on_complete.clone();
|
||||||
let on_complete = Callback::from(move |_| on_complete.emit(()));
|
let on_complete = Callback::from(move |_| on_complete.emit(()));
|
||||||
html! {
|
html! {
|
||||||
<div class="column-list">
|
<div class="wolves-intro">
|
||||||
<h2>{"these are the wolves:"}</h2>
|
<h2>{"these are the wolves:"}</h2>
|
||||||
<div class="row-list wolves-list">
|
<div class="row-list wolves-list">
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ pub struct ButtonProperties {
|
||||||
pub disabled_reason: Option<String>,
|
pub disabled_reason: Option<String>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub children: Html,
|
pub children: Html,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub classes: yew::Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
|
|
@ -15,7 +17,7 @@ pub fn Button(props: &ButtonProperties) -> Html {
|
||||||
let on_click = Callback::from(move |_| on_click.emit(()));
|
let on_click = Callback::from(move |_| on_click.emit(()));
|
||||||
html! {
|
html! {
|
||||||
<button
|
<button
|
||||||
class="default-button"
|
class={classes!("default-button", props.classes.clone())}
|
||||||
disabled={props.disabled_reason.is_some()}
|
disabled={props.disabled_reason.is_some()}
|
||||||
reason={props.disabled_reason.clone()}
|
reason={props.disabled_reason.clone()}
|
||||||
onclick={on_click}
|
onclick={on_click}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use core::ops::Not;
|
use core::{num::NonZeroU8, ops::Not};
|
||||||
|
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
message::{CharacterState, PublicIdentity},
|
message::{CharacterState, PublicIdentity},
|
||||||
|
|
@ -6,10 +6,11 @@ use werewolves_proto::{
|
||||||
};
|
};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
use crate::components::{Button, Identity};
|
use crate::components::{Button, ClickableField, Identity};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||||
pub struct DaytimePlayerListProps {
|
pub struct DaytimePlayerListProps {
|
||||||
|
pub day: NonZeroU8,
|
||||||
pub characters: Box<[CharacterState]>,
|
pub characters: Box<[CharacterState]>,
|
||||||
pub marked: Box<[CharacterId]>,
|
pub marked: Box<[CharacterId]>,
|
||||||
pub on_execute: Callback<()>,
|
pub on_execute: Callback<()>,
|
||||||
|
|
@ -20,6 +21,7 @@ pub struct DaytimePlayerListProps {
|
||||||
#[function_component]
|
#[function_component]
|
||||||
pub fn DaytimePlayerList(
|
pub fn DaytimePlayerList(
|
||||||
DaytimePlayerListProps {
|
DaytimePlayerListProps {
|
||||||
|
day,
|
||||||
characters,
|
characters,
|
||||||
on_execute,
|
on_execute,
|
||||||
on_mark,
|
on_mark,
|
||||||
|
|
@ -42,22 +44,22 @@ pub fn DaytimePlayerList(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Html>();
|
.collect::<Html>();
|
||||||
|
let button_text = if marked.is_empty() {
|
||||||
|
"end day"
|
||||||
|
} else {
|
||||||
|
"execute"
|
||||||
|
};
|
||||||
let button = big_screen.not().then(|| {
|
let button = big_screen.not().then(|| {
|
||||||
html! {
|
html! {
|
||||||
<Button
|
<Button on_click={on_execute}>
|
||||||
on_click={on_execute}
|
{button_text}
|
||||||
disabled_reason={
|
</Button>
|
||||||
marked.is_empty()
|
|
||||||
.then_some(String::from("no one is on the block"))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{"execute"}
|
|
||||||
</Button>
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
html! {
|
html! {
|
||||||
<div class="column-list">
|
<div class="character-picker">
|
||||||
<div class="row-list small baseline player-list gap">
|
<h2>{"day "}{day.to_string()}</h2>
|
||||||
|
<div class="player-list">
|
||||||
{chars}
|
{chars}
|
||||||
</div>
|
</div>
|
||||||
{button}
|
{button}
|
||||||
|
|
@ -87,26 +89,16 @@ pub fn DaytimePlayer(
|
||||||
}: &DaytimePlayerProps,
|
}: &DaytimePlayerProps,
|
||||||
) -> Html {
|
) -> Html {
|
||||||
let dead = died_to.is_some().then_some("dead");
|
let dead = died_to.is_some().then_some("dead");
|
||||||
let button_text = if *on_the_block { "unmark" } else { "mark" };
|
let marked = on_the_block.then_some("marked");
|
||||||
let on_the_block = on_the_block.then_some("marked");
|
let character_id = identity.character_id.clone();
|
||||||
let submenu = died_to.is_none().then_some(()).and_then(|_| {
|
let on_click: Callback<_> = on_select
|
||||||
on_select.as_ref().map(|on_select| {
|
.clone()
|
||||||
let character_id = identity.character_id.clone();
|
.map(|on_select| Callback::from(move |_| on_select.emit(character_id.clone())))
|
||||||
let on_select = on_select.clone();
|
.unwrap_or_default();
|
||||||
let on_click = Callback::from(move |_| on_select.emit(character_id.clone()));
|
|
||||||
html! {
|
|
||||||
<nav class="submenu">
|
|
||||||
<Button on_click={on_click}>{button_text}</Button>
|
|
||||||
</nav>
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
let identity: PublicIdentity = identity.into();
|
let identity: PublicIdentity = identity.into();
|
||||||
html! {
|
html! {
|
||||||
<div class={classes!("player", dead, on_the_block, "column-list", "ident")}>
|
<Button on_click={on_click} classes={classes!(marked, dead, "character")}>
|
||||||
<Identity ident={identity}/>
|
<Identity ident={identity}/>
|
||||||
{submenu}
|
</Button>
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,16 +67,13 @@ pub fn LobbyPlayer(LobbyPlayerProps { player, on_action }: &LobbyPlayerProps) ->
|
||||||
});
|
});
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
// <div class={classes!("player", class, "column-list")}>
|
<ClickableField
|
||||||
<ClickableField
|
state={open}
|
||||||
state={open}
|
options={submenu}
|
||||||
options={submenu}
|
class={classes!("player", class, "column-list")}
|
||||||
class={classes!("player", class, "column-list")}
|
with_backdrop_exit=true
|
||||||
with_backdrop_exit=true
|
>
|
||||||
>
|
<Identity ident={player.identification.public.clone()}/>
|
||||||
<Identity ident={player.identification.public.clone()}/>
|
</ClickableField>
|
||||||
</ClickableField>
|
|
||||||
// {submenu}
|
|
||||||
// </div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use std::sync::Arc;
|
use core::ops::Not;
|
||||||
|
|
||||||
use werewolves_proto::message::CharacterIdentity;
|
use werewolves_proto::message::{CharacterIdentity, PublicIdentity};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
use crate::components::{Button, action::CustomTargetCard};
|
use crate::components::{Button, Identity};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Properties)]
|
#[derive(Debug, PartialEq, Properties)]
|
||||||
pub struct RoleRevealProps {
|
pub struct RoleRevealProps {
|
||||||
|
|
@ -65,11 +65,9 @@ impl Component for RoleReveal {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
html! {
|
html! {
|
||||||
<div class="column-list gap">
|
<div class="role-reveal-cards">
|
||||||
{force_all}
|
{force_all}
|
||||||
<div class={"role-reveal-cards"}>
|
{cards}
|
||||||
{cards}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -87,25 +85,20 @@ pub fn RoleRevealCard(props: &RoleRevealCardProps) -> Html {
|
||||||
let class = props.is_ready.then_some("ready");
|
let class = props.is_ready.then_some("ready");
|
||||||
let target = props.target.clone();
|
let target = props.target.clone();
|
||||||
let on_force_ready = props.on_force_ready.clone();
|
let on_force_ready = props.on_force_ready.clone();
|
||||||
let on_click = on_force_ready.map(|on_force_ready| {
|
let on_click = props.is_ready.not().then_some(()).and_then(|_| {
|
||||||
Callback::from(move |_| {
|
on_force_ready.map(|on_force_ready| {
|
||||||
on_force_ready.emit(target.clone());
|
let on_click = Callback::from(move |_| {
|
||||||
|
on_force_ready.emit(target.clone());
|
||||||
|
});
|
||||||
|
html! {<Button on_click={on_click}>{"ready"}</Button>}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let options: Arc<[String]> = if props.is_ready || props.on_force_ready.is_none() {
|
|
||||||
Arc::new([])
|
let ident: PublicIdentity = props.target.clone().into();
|
||||||
} else {
|
|
||||||
Arc::new([String::from("ready")])
|
|
||||||
};
|
|
||||||
html! {
|
html! {
|
||||||
<div class={classes!(class, "role-reveal-card")}>
|
<div class={classes!(class, "role-reveal-card")}>
|
||||||
<CustomTargetCard
|
<Identity ident={ident}/>
|
||||||
target={props.target.clone()}
|
{on_click}
|
||||||
options={options}
|
|
||||||
class={if props.is_ready { String::from("ready") } else { String::new() }}
|
|
||||||
on_select={on_click}
|
|
||||||
hide_submenu=false
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ mod pages {
|
||||||
}
|
}
|
||||||
mod callback;
|
mod callback;
|
||||||
|
|
||||||
|
use core::num::NonZeroU8;
|
||||||
|
|
||||||
use pages::{ErrorComponent, WerewolfError};
|
use pages::{ErrorComponent, WerewolfError};
|
||||||
use web_sys::Url;
|
use web_sys::Url;
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
|
|
@ -53,19 +55,14 @@ fn main() {
|
||||||
}
|
}
|
||||||
} else if path.starts_with("/many-client") {
|
} else if path.starts_with("/many-client") {
|
||||||
let clients = document.query_selector("clients").unwrap().unwrap();
|
let clients = document.query_selector("clients").unwrap().unwrap();
|
||||||
for (player_id, name, dupe) in [(
|
for (player_id, name, num, dupe) in (1..=16).map(|num| {
|
||||||
PlayerId::from_u128(1),
|
|
||||||
"player 1".to_string(),
|
|
||||||
document.query_selector("app").unwrap().unwrap(),
|
|
||||||
)]
|
|
||||||
.into_iter()
|
|
||||||
.chain((1..=2).map(|num| {
|
|
||||||
(
|
(
|
||||||
PlayerId::from_u128(num as u128),
|
PlayerId::from_u128(num as u128),
|
||||||
format!("player {num}"),
|
format!("player {num}"),
|
||||||
|
NonZeroU8::new(num).unwrap(),
|
||||||
document.create_element("autoclient").unwrap(),
|
document.create_element("autoclient").unwrap(),
|
||||||
)
|
)
|
||||||
})) {
|
}) {
|
||||||
if dupe.tag_name() == "AUTOCLIENT" {
|
if dupe.tag_name() == "AUTOCLIENT" {
|
||||||
clients.append_child(&dupe).unwrap();
|
clients.append_child(&dupe).unwrap();
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +77,7 @@ fn main() {
|
||||||
public: PublicIdentity {
|
public: PublicIdentity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
pronouns: Some(String::from("he/him")),
|
pronouns: Some(String::from("he/him")),
|
||||||
number: None,
|
number: Some(num),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue