integrate overrides into host nav
This commit is contained in:
parent
ec29c66e4b
commit
8e1d2e34d0
|
|
@ -22,6 +22,7 @@ use futures::{
|
|||
use gloo::net::websocket::{self, futures::WebSocket};
|
||||
use instant::Instant;
|
||||
use serde::Serialize;
|
||||
use werewolves_macros::Titles;
|
||||
use werewolves_proto::{
|
||||
character::CharacterId,
|
||||
error::GameError,
|
||||
|
|
@ -47,6 +48,7 @@ use crate::{
|
|||
},
|
||||
pages::RolePage,
|
||||
storage::StorageKey,
|
||||
test_util::TestScreens,
|
||||
};
|
||||
|
||||
use crate::WerewolfError;
|
||||
|
|
@ -190,7 +192,7 @@ async fn worker(mut recv: Receiver<HostMessage>, scope: Scope<Host>) {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Titles, PartialEq)]
|
||||
pub enum HostEvent {
|
||||
SetErrorCallback(Callback<Option<WerewolfError>>),
|
||||
SetBigScreenState(bool),
|
||||
|
|
@ -201,8 +203,11 @@ pub enum HostEvent {
|
|||
Settings(GameSettings),
|
||||
Error(GameError),
|
||||
QrMode(bool),
|
||||
ToOverrideView,
|
||||
ReturnFromOverride,
|
||||
ExpectEcho(Box<HostEvent>),
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Titles)]
|
||||
pub enum HostState {
|
||||
Disconnected,
|
||||
Lobby {
|
||||
|
|
@ -224,6 +229,9 @@ pub enum HostState {
|
|||
},
|
||||
Prompt(ActionPrompt, usize),
|
||||
Result(Option<CharacterIdentity>, ActionResult),
|
||||
ScreenOverrides {
|
||||
return_to: Box<HostState>,
|
||||
},
|
||||
Story {
|
||||
story: GameStory,
|
||||
#[allow(unused)]
|
||||
|
|
@ -280,6 +288,7 @@ pub struct Host {
|
|||
big_screen: bool,
|
||||
qr_mode: bool,
|
||||
debug: bool,
|
||||
expecting_echo: Option<HostEvent>,
|
||||
}
|
||||
|
||||
impl Component for Host {
|
||||
|
|
@ -310,12 +319,33 @@ impl Component for Host {
|
|||
log::error!("{err}")
|
||||
}
|
||||
}),
|
||||
expecting_echo: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
log::trace!("state: {:?}", self.state);
|
||||
let content = match self.state.clone() {
|
||||
HostState::ScreenOverrides { .. } => {
|
||||
let send = {
|
||||
let send = self.send.clone();
|
||||
let on_err = self.error_callback.clone();
|
||||
let scope = _ctx.link().clone();
|
||||
Callback::from(move |msg: ServerToHostMessage| {
|
||||
scope.send_message(HostEvent::ExpectEcho(Box::new(msg.clone().into())));
|
||||
let mut send = send.clone();
|
||||
let on_err = on_err.clone();
|
||||
yew::platform::spawn_local(async move {
|
||||
if let Err(err) = send.send(HostMessage::Echo(msg)).await {
|
||||
on_err.emit(Some(err.into()))
|
||||
}
|
||||
});
|
||||
})
|
||||
};
|
||||
html! {
|
||||
<TestScreens send={send} />
|
||||
}
|
||||
}
|
||||
HostState::CharacterStates(chars) => {
|
||||
html! {
|
||||
<CharacterStatesReadOnly states={chars}/>
|
||||
|
|
@ -466,15 +496,6 @@ impl Component for Host {
|
|||
}
|
||||
}
|
||||
};
|
||||
let debug_nav = self.debug.then(|| {
|
||||
html! {
|
||||
<a href="/host/test"><Button on_click={|_|()}>{"test screens"}</Button></a>
|
||||
}
|
||||
});
|
||||
let on_prev_click = callback::send_message(
|
||||
HostMessage::InGame(HostGameMessage::PreviousState),
|
||||
self.send.clone(),
|
||||
);
|
||||
let view_roles_btn = match &self.state {
|
||||
HostState::Prompt(_, _) | HostState::Result(_, _) => {
|
||||
let on_view_click = crate::callback::send_message(
|
||||
|
|
@ -494,12 +515,52 @@ impl Component for Host {
|
|||
}
|
||||
_ => None,
|
||||
};
|
||||
let override_screens_btn = match &self.state {
|
||||
HostState::Prompt(_, _) | HostState::Result(_, _) => {
|
||||
let overrides_click = {
|
||||
let scope = _ctx.link().clone();
|
||||
Callback::from(move |_| {
|
||||
scope.send_message(HostEvent::ToOverrideView);
|
||||
})
|
||||
};
|
||||
|
||||
Some(html! {
|
||||
<Button on_click={overrides_click}>{"overrides"}</Button>
|
||||
})
|
||||
}
|
||||
HostState::ScreenOverrides { .. } => {
|
||||
let return_click = {
|
||||
let scope = _ctx.link().clone();
|
||||
Callback::from(move |_| {
|
||||
scope.send_message(HostEvent::ReturnFromOverride);
|
||||
})
|
||||
};
|
||||
|
||||
Some(html! {
|
||||
<Button on_click={return_click}>{"back"}</Button>
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let previous_btn = match &self.state {
|
||||
HostState::Prompt(_, _) | HostState::Result(_, _) => {
|
||||
let on_prev_click = callback::send_message(
|
||||
HostMessage::InGame(HostGameMessage::PreviousState),
|
||||
self.send.clone(),
|
||||
);
|
||||
|
||||
Some(html! {
|
||||
<Button on_click={on_prev_click}>{"previous"}</Button>
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let nav = self.big_screen.not().then(|| {
|
||||
html! {
|
||||
<nav class="host-nav" style="z-index: 3;">
|
||||
<Button on_click={on_prev_click}>{"previous"}</Button>
|
||||
{previous_btn}
|
||||
{view_roles_btn}
|
||||
{debug_nav}
|
||||
{override_screens_btn}
|
||||
</nav>
|
||||
}
|
||||
});
|
||||
|
|
@ -514,116 +575,8 @@ impl Component for Host {
|
|||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
log::debug!("update: {msg:?}, current: {:?}", self.state);
|
||||
match msg {
|
||||
HostEvent::CharacterList(char) => {
|
||||
if self.big_screen {
|
||||
return false;
|
||||
}
|
||||
self.state = HostState::CharacterStates(char);
|
||||
true
|
||||
}
|
||||
HostEvent::QrMode(mode) => {
|
||||
self.qr_mode = mode;
|
||||
true
|
||||
}
|
||||
HostEvent::PlayerList(mut players) => {
|
||||
const LAST: NonZeroU8 = NonZeroU8::new(0xFF).unwrap();
|
||||
players.sort_by(|l, r| {
|
||||
l.identification
|
||||
.public
|
||||
.number
|
||||
.unwrap_or(LAST)
|
||||
.cmp(&r.identification.public.number.unwrap_or(LAST))
|
||||
});
|
||||
match &mut self.state {
|
||||
HostState::Lobby {
|
||||
players: p,
|
||||
settings: _,
|
||||
} => *p = players.into_iter().collect(),
|
||||
HostState::Story { .. }
|
||||
| HostState::Disconnected
|
||||
| HostState::GameOver { .. } => {
|
||||
let mut send = self.send.clone();
|
||||
let on_err = self.error_callback.clone();
|
||||
|
||||
self.state = HostState::Lobby {
|
||||
players: players.into_iter().collect(),
|
||||
settings: Default::default(),
|
||||
};
|
||||
yew::platform::spawn_local(async move {
|
||||
if let Err(err) = send
|
||||
.send(HostMessage::Lobby(HostLobbyMessage::GetGameSettings))
|
||||
.await
|
||||
{
|
||||
on_err.emit(Some(err.into()))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
HostState::CharacterStates(_)
|
||||
| HostState::Prompt(_, _)
|
||||
| HostState::Result(_, _)
|
||||
| HostState::RoleReveal { .. }
|
||||
| HostState::Day { .. } => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
HostEvent::SetErrorCallback(callback) => {
|
||||
self.error_callback = callback;
|
||||
false
|
||||
}
|
||||
HostEvent::SetState(state) => {
|
||||
self.state = state;
|
||||
true
|
||||
}
|
||||
HostEvent::Settings(settings) => match &mut self.state {
|
||||
HostState::Lobby {
|
||||
players: _,
|
||||
settings: s,
|
||||
} => {
|
||||
*s = settings;
|
||||
true
|
||||
}
|
||||
HostState::CharacterStates(_)
|
||||
| HostState::Story { .. }
|
||||
| HostState::Prompt(_, _)
|
||||
| HostState::Result(_, _)
|
||||
| HostState::Disconnected
|
||||
| HostState::RoleReveal {
|
||||
ackd: _,
|
||||
waiting: _,
|
||||
}
|
||||
| HostState::GameOver { result: _ }
|
||||
| HostState::Day { .. } => {
|
||||
log::info!("ignoring settings get");
|
||||
false
|
||||
}
|
||||
},
|
||||
HostEvent::Error(err) => {
|
||||
self.error_callback.emit(Some(WerewolfError::Game(err)));
|
||||
false
|
||||
}
|
||||
HostEvent::SetBigScreenState(state) => {
|
||||
self.big_screen = state;
|
||||
if self.big_screen {
|
||||
gloo::utils::document_element().set_class_name("big-screen")
|
||||
}
|
||||
|
||||
if state {
|
||||
let (mut discard_send, mut discard_recv) = futures::channel::mpsc::channel(10);
|
||||
core::mem::swap(&mut discard_send, &mut self.send);
|
||||
Box::leak(Box::new(discard_send));
|
||||
yew::platform::spawn_local(async move {
|
||||
while discard_recv.next().await.is_some() {}
|
||||
});
|
||||
}
|
||||
self.debug = false;
|
||||
self.error_callback = Callback::noop();
|
||||
false
|
||||
}
|
||||
log::debug!("update: {}, current: {}", msg.title(), self.state.title());
|
||||
match &msg {
|
||||
HostEvent::Continue => {
|
||||
if self.big_screen {
|
||||
return false;
|
||||
|
|
@ -639,13 +592,178 @@ impl Component for Host {
|
|||
log::error!("sending next: {err:?}")
|
||||
}
|
||||
});
|
||||
false
|
||||
}
|
||||
HostEvent::SetErrorCallback(cb) => {
|
||||
self.error_callback = cb.clone();
|
||||
}
|
||||
HostEvent::SetBigScreenState(state) => {
|
||||
self.big_screen = *state;
|
||||
if self.big_screen {
|
||||
gloo::utils::document_element().set_class_name("big-screen")
|
||||
}
|
||||
|
||||
if *state {
|
||||
let (mut discard_send, mut discard_recv) = futures::channel::mpsc::channel(10);
|
||||
core::mem::swap(&mut discard_send, &mut self.send);
|
||||
Box::leak(Box::new(discard_send));
|
||||
yew::platform::spawn_local(async move {
|
||||
while discard_recv.next().await.is_some() {}
|
||||
});
|
||||
}
|
||||
self.debug = false;
|
||||
self.error_callback = Callback::noop();
|
||||
}
|
||||
HostEvent::QrMode(mode) => {
|
||||
self.qr_mode = *mode;
|
||||
}
|
||||
HostEvent::ExpectEcho(echo) => {
|
||||
self.expecting_echo.replace((**echo).clone());
|
||||
}
|
||||
HostEvent::ReturnFromOverride => {
|
||||
if let HostState::ScreenOverrides { return_to } = &self.state {
|
||||
log::debug!("returning from screen overrides to: {}", return_to.title());
|
||||
self.state = (**return_to).clone();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let HostState::ScreenOverrides { .. } = &self.state {
|
||||
let (state, update) = self.message_to_new_state(msg.clone());
|
||||
if let Some(mut state) = state
|
||||
// re-borrow so we can borrow self immutably for [message_to_new_state]
|
||||
&& let HostState::ScreenOverrides { return_to } = &mut self.state
|
||||
&& (self.expecting_echo.is_none()
|
||||
|| self
|
||||
.expecting_echo
|
||||
.as_ref()
|
||||
.map(|exp| *exp != msg)
|
||||
.unwrap_or_default())
|
||||
{
|
||||
log::debug!("screen override: new return_to: {}", return_to.title());
|
||||
core::mem::swap(&mut **return_to, &mut state);
|
||||
self.expecting_echo.take();
|
||||
}
|
||||
return update;
|
||||
}
|
||||
|
||||
let (state, update) = self.message_to_new_state(msg);
|
||||
if let Some(state) = state {
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
||||
|
||||
impl Host {
|
||||
fn message_to_new_state(&self, msg: HostEvent) -> (Option<HostState>, bool) {
|
||||
match msg {
|
||||
HostEvent::ExpectEcho(_) => (None, false),
|
||||
HostEvent::ReturnFromOverride => {
|
||||
if let HostState::ScreenOverrides { return_to } = &self.state {
|
||||
(Some((**return_to).clone()), true)
|
||||
} else {
|
||||
(None, false)
|
||||
}
|
||||
}
|
||||
HostEvent::ToOverrideView => {
|
||||
if let HostState::ScreenOverrides { .. } = &self.state {
|
||||
return (None, false);
|
||||
}
|
||||
(
|
||||
Some(HostState::ScreenOverrides {
|
||||
return_to: Box::new(self.state.clone()),
|
||||
}),
|
||||
true,
|
||||
)
|
||||
}
|
||||
HostEvent::CharacterList(char) => {
|
||||
if self.big_screen {
|
||||
return (None, false);
|
||||
}
|
||||
(Some(HostState::CharacterStates(char)), true)
|
||||
}
|
||||
HostEvent::QrMode(mode) => (None, true),
|
||||
HostEvent::PlayerList(mut players) => {
|
||||
const LAST: NonZeroU8 = NonZeroU8::new(0xFF).unwrap();
|
||||
players.sort_by(|l, r| {
|
||||
l.identification
|
||||
.public
|
||||
.number
|
||||
.unwrap_or(LAST)
|
||||
.cmp(&r.identification.public.number.unwrap_or(LAST))
|
||||
});
|
||||
match &self.state {
|
||||
HostState::Lobby { settings, .. } => (
|
||||
Some(HostState::Lobby {
|
||||
players: players.into_iter().collect(),
|
||||
settings: settings.clone(),
|
||||
}),
|
||||
true,
|
||||
),
|
||||
HostState::Story { .. }
|
||||
| HostState::Disconnected
|
||||
| HostState::GameOver { .. } => {
|
||||
let mut send = self.send.clone();
|
||||
let on_err = self.error_callback.clone();
|
||||
|
||||
let state = Some(HostState::Lobby {
|
||||
players: players.into_iter().collect(),
|
||||
settings: Default::default(),
|
||||
});
|
||||
yew::platform::spawn_local(async move {
|
||||
if let Err(err) = send
|
||||
.send(HostMessage::Lobby(HostLobbyMessage::GetGameSettings))
|
||||
.await
|
||||
{
|
||||
on_err.emit(Some(err.into()))
|
||||
}
|
||||
});
|
||||
(state, true)
|
||||
}
|
||||
|
||||
HostState::ScreenOverrides { .. }
|
||||
| HostState::CharacterStates(_)
|
||||
| HostState::Prompt(_, _)
|
||||
| HostState::Result(_, _)
|
||||
| HostState::RoleReveal { .. }
|
||||
| HostState::Day { .. } => (None, false),
|
||||
}
|
||||
}
|
||||
HostEvent::SetErrorCallback(callback) => (None, false),
|
||||
HostEvent::SetState(state) => (Some(state), true),
|
||||
HostEvent::Settings(settings) => match &self.state {
|
||||
HostState::Lobby { players, .. } => (
|
||||
Some(HostState::Lobby {
|
||||
settings,
|
||||
players: players.clone(),
|
||||
}),
|
||||
true,
|
||||
),
|
||||
HostState::ScreenOverrides { .. }
|
||||
| HostState::CharacterStates(_)
|
||||
| HostState::Story { .. }
|
||||
| HostState::Prompt(_, _)
|
||||
| HostState::Result(_, _)
|
||||
| HostState::Disconnected
|
||||
| HostState::RoleReveal {
|
||||
ackd: _,
|
||||
waiting: _,
|
||||
}
|
||||
| HostState::GameOver { result: _ }
|
||||
| HostState::Day { .. } => {
|
||||
log::info!("ignoring settings get");
|
||||
(None, false)
|
||||
}
|
||||
},
|
||||
HostEvent::Error(err) => (None, false),
|
||||
HostEvent::SetBigScreenState(state) => (None, true),
|
||||
HostEvent::Continue => (None, false),
|
||||
}
|
||||
}
|
||||
fn lobby_big_screen_show_setup(&self, settings: GameSettings) -> Html {
|
||||
if !self.qr_mode {
|
||||
return html! {
|
||||
|
|
|
|||
Loading…
Reference in New Issue