client lobby refactor
This commit is contained in:
parent
01c1a4554a
commit
c96e019071
|
|
@ -737,3 +737,20 @@ input {
|
||||||
.zoom {
|
.zoom {
|
||||||
zoom: 200%;
|
zoom: 200%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.client-lobby-player-list {
|
||||||
|
@extend .row-list;
|
||||||
|
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
&>* {
|
||||||
|
border: 1px solid white;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use core::{num::NonZeroU8, sync::atomic::AtomicBool, time::Duration};
|
use core::{num::NonZeroU8, sync::atomic::AtomicBool, time::Duration};
|
||||||
use std::collections::VecDeque;
|
use std::{collections::VecDeque, rc::Rc};
|
||||||
|
|
||||||
use futures::{
|
use futures::{
|
||||||
SinkExt, StreamExt,
|
SinkExt, StreamExt,
|
||||||
|
|
@ -21,12 +21,13 @@ use werewolves_proto::{
|
||||||
player::PlayerId,
|
player::PlayerId,
|
||||||
role::RoleTitle,
|
role::RoleTitle,
|
||||||
};
|
};
|
||||||
use yew::{html::Scope, prelude::*};
|
use yew::{html::Scope, prelude::*, suspense::use_future};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
clients::client::connection::{Connection2, ConnectionError},
|
||||||
components::{
|
components::{
|
||||||
Button, Identity, Notification,
|
Button, Identity, Notification,
|
||||||
client::{ClientNav, InputName},
|
client::{ClientNav, Signin},
|
||||||
},
|
},
|
||||||
storage::StorageKey,
|
storage::StorageKey,
|
||||||
};
|
};
|
||||||
|
|
@ -184,7 +185,7 @@ impl Connection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum ClientEvent {
|
pub enum ClientEvent {
|
||||||
Disconnected,
|
Disconnected,
|
||||||
Waiting,
|
Waiting,
|
||||||
|
|
@ -219,6 +220,168 @@ impl TryFrom<ServerMessage> for ClientEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
pub enum ClientEvent2 {
|
||||||
|
Disconnected,
|
||||||
|
Connecting,
|
||||||
|
Waiting,
|
||||||
|
ShowRole(RoleTitle),
|
||||||
|
|
||||||
|
Lobby {
|
||||||
|
joined: bool,
|
||||||
|
players: Rc<[PublicIdentity]>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, PartialEq)]
|
||||||
|
pub struct ClientContext {
|
||||||
|
pub error_cb: Callback<Option<WerewolfError>>,
|
||||||
|
pub forced_identity: Option<Identification>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
pub fn Client2(ClientProps { auto_join }: &ClientProps) -> Html {
|
||||||
|
let client_state = use_state(|| ClientEvent2::Connecting);
|
||||||
|
let ClientContext {
|
||||||
|
error_cb,
|
||||||
|
forced_identity,
|
||||||
|
} = use_context::<ClientContext>().unwrap_or_default();
|
||||||
|
let force = use_force_update();
|
||||||
|
|
||||||
|
let ident = if let Some(Identification { player_id, public }) = forced_identity {
|
||||||
|
(player_id, public)
|
||||||
|
} else {
|
||||||
|
match PlayerId::load_from_storage()
|
||||||
|
.and_then(|pid| PublicIdentity::load_from_storage().map(|ident| (pid, ident)))
|
||||||
|
{
|
||||||
|
Ok((pid, ident)) => (pid, ident),
|
||||||
|
Err(StorageError::KeyNotFound(_)) => {
|
||||||
|
let on_signin = Callback::from(move |ident: PublicIdentity| {
|
||||||
|
PlayerId::new().save_to_storage().expect("saving player id");
|
||||||
|
ident.save_to_storage().expect("saving ident");
|
||||||
|
force.force_update();
|
||||||
|
});
|
||||||
|
return html! {
|
||||||
|
<Signin callback={on_signin}/>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error_cb.emit(Some(err.into()));
|
||||||
|
PlayerId::delete();
|
||||||
|
PublicIdentity::delete();
|
||||||
|
force.force_update();
|
||||||
|
return html! {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let ident = use_state(|| ident);
|
||||||
|
let (send, recv) = yew::platform::pinned::mpsc::unbounded();
|
||||||
|
let send = use_state(|| send);
|
||||||
|
let recv = use_mut_ref(|| recv);
|
||||||
|
let connection = use_mut_ref(|| Connection2::new(client_state.setter(), ident.clone(), recv));
|
||||||
|
|
||||||
|
let content = match &*client_state {
|
||||||
|
ClientEvent2::Disconnected => html! {<p>{"disconnected"}</p>},
|
||||||
|
ClientEvent2::Connecting => {
|
||||||
|
if let Err(err) = connection
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| ConnectionError::ConnectionAlreadyActive)
|
||||||
|
.and_then(|mut conn| conn.start())
|
||||||
|
{
|
||||||
|
error_cb.emit(Some(err.into()));
|
||||||
|
}
|
||||||
|
if *auto_join {
|
||||||
|
let _ = send.send_now(ClientMessage::Hello);
|
||||||
|
}
|
||||||
|
html! {<p>{"connecting..."}</p>}
|
||||||
|
}
|
||||||
|
ClientEvent2::Waiting => html! {<p>{"waiting..."}</p>},
|
||||||
|
ClientEvent2::ShowRole(role_title) => {
|
||||||
|
let send = (*send).clone();
|
||||||
|
let error_cb = error_cb.clone();
|
||||||
|
let on_click = Callback::from(move |_| {
|
||||||
|
if let Err(err) = send.clone().send_now(ClientMessage::RoleAck) {
|
||||||
|
error_cb.emit(Some(err.into()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
html! {
|
||||||
|
<div class="game-start-role">
|
||||||
|
<p>{format!("Your role: {role_title}")}</p>
|
||||||
|
<button onclick={on_click}>{"got it"}</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent2::Lobby { joined, players } => {
|
||||||
|
let player = {
|
||||||
|
html! {
|
||||||
|
<Identity ident={ident.1.clone()} class="zoom"/>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let player_list = players
|
||||||
|
.iter()
|
||||||
|
.map(|ident| {
|
||||||
|
html! {
|
||||||
|
<Identity ident={ident.clone()}/>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Html>();
|
||||||
|
|
||||||
|
let button_send = (*send).clone();
|
||||||
|
let err_cb = error_cb.clone();
|
||||||
|
let button = if *joined {
|
||||||
|
let cb = move |_| {
|
||||||
|
if let Err(err) = button_send.send_now(ClientMessage::Goodbye) {
|
||||||
|
err_cb.emit(Some(err.into()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
html! {
|
||||||
|
<Button on_click={cb}>{"leave"}</Button>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let cb = move |_| {
|
||||||
|
if let Err(err) = button_send.send_now(ClientMessage::Hello) {
|
||||||
|
err_cb.emit(Some(err.into()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
html! {
|
||||||
|
<Button on_click={cb}>{"join"}</Button>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<div class="column-list gap">
|
||||||
|
{player}
|
||||||
|
<h2>{"there are currently "}{players.len()}{" players in the lobby"}</h2>
|
||||||
|
{button}
|
||||||
|
<div class="client-lobby-player-list">
|
||||||
|
{player_list}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let nav = {
|
||||||
|
let send = (*send).clone();
|
||||||
|
let error_cb = error_cb.clone();
|
||||||
|
let client_nav_msg_cb = move |msg| {
|
||||||
|
if let Err(err) = send.send_now(msg) {
|
||||||
|
error_cb.emit(Some(err.into()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
html! {
|
||||||
|
<ClientNav message_callback={client_nav_msg_cb} />
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<>
|
||||||
|
{nav}
|
||||||
|
{content}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
player: Option<Identification>,
|
player: Option<Identification>,
|
||||||
send: Sender<ClientMessage>,
|
send: Sender<ClientMessage>,
|
||||||
|
|
@ -284,7 +447,7 @@ impl Component for Client {
|
||||||
scope.send_message(Message::SetPublicIdentity(public));
|
scope.send_message(Message::SetPublicIdentity(public));
|
||||||
});
|
});
|
||||||
return html! {
|
return html! {
|
||||||
<InputName callback={callback}/>
|
<Signin callback={callback}/>
|
||||||
};
|
};
|
||||||
} else if self.recv.is_some() {
|
} else if self.recv.is_some() {
|
||||||
// Player info loaded, but connection isn't started
|
// Player info loaded, but connection isn't started
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,252 @@
|
||||||
|
use core::num::NonZeroU8;
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use futures::{SinkExt, StreamExt};
|
||||||
|
use gloo::net::websocket::{self, futures::WebSocket};
|
||||||
|
use instant::Instant;
|
||||||
|
use serde::Serialize;
|
||||||
|
use thiserror::Error;
|
||||||
|
use werewolves_proto::message::{PlayerUpdate, ServerMessage};
|
||||||
|
use werewolves_proto::{
|
||||||
|
message::{ClientMessage, Identification, PublicIdentity},
|
||||||
|
player::PlayerId,
|
||||||
|
};
|
||||||
|
use yew::{platform::pinned::mpsc::UnboundedReceiver, prelude::*};
|
||||||
|
|
||||||
|
use crate::clients::client::ClientEvent2;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Error)]
|
||||||
|
pub enum ConnectionError {
|
||||||
|
#[error("connection already active")]
|
||||||
|
ConnectionAlreadyActive,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn url() -> String {
|
||||||
|
format!(
|
||||||
|
"{}client",
|
||||||
|
option_env!("LOCAL")
|
||||||
|
.map(|_| crate::clients::DEBUG_URL)
|
||||||
|
.unwrap_or(crate::clients::LIVE_URL)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Connection2 {
|
||||||
|
state: UseStateSetter<ClientEvent2>,
|
||||||
|
ident: UseStateHandle<(PlayerId, PublicIdentity)>,
|
||||||
|
receiver: Rc<RefCell<UnboundedReceiver<ClientMessage>>>,
|
||||||
|
active: Rc<RefCell<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connection2 {
|
||||||
|
pub fn new(
|
||||||
|
state: UseStateSetter<ClientEvent2>,
|
||||||
|
ident: UseStateHandle<(PlayerId, PublicIdentity)>,
|
||||||
|
receiver: Rc<RefCell<UnboundedReceiver<ClientMessage>>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
state,
|
||||||
|
ident,
|
||||||
|
receiver,
|
||||||
|
active: Rc::new(RefCell::new(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn identification(&self) -> Identification {
|
||||||
|
Identification {
|
||||||
|
player_id: self.ident.0.clone(),
|
||||||
|
public: self.ident.1.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn connect_ws() -> WebSocket {
|
||||||
|
let url = url();
|
||||||
|
loop {
|
||||||
|
match WebSocket::open(&url) {
|
||||||
|
Ok(ws) => break ws,
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("connect: {err}");
|
||||||
|
yew::platform::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_message(msg: &impl Serialize) -> websocket::Message {
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
{
|
||||||
|
websocket::Message::Text(serde_json::to_string(msg).expect("message serialization"))
|
||||||
|
}
|
||||||
|
#[cfg(feature = "cbor")]
|
||||||
|
{
|
||||||
|
websocket::Message::Bytes({
|
||||||
|
let mut v = Vec::new();
|
||||||
|
ciborium::into_writer(msg, &mut v).expect("serializing message");
|
||||||
|
v
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) -> Result<(), ConnectionError> {
|
||||||
|
let active = self
|
||||||
|
.active
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| ConnectionError::ConnectionAlreadyActive)?;
|
||||||
|
core::mem::drop(active);
|
||||||
|
let mut conn = self.clone();
|
||||||
|
yew::platform::spawn_local(async move {
|
||||||
|
let active = conn.active.clone();
|
||||||
|
conn.active = Rc::new(RefCell::new(()));
|
||||||
|
let active_borrow = active.borrow_mut();
|
||||||
|
conn.run().await;
|
||||||
|
core::mem::drop(active_borrow);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(&mut self) {
|
||||||
|
const CONNECT_WAIT: Duration = Duration::from_secs(3);
|
||||||
|
let url = url();
|
||||||
|
let mut last_connect: Option<Instant> = None;
|
||||||
|
'outer: loop {
|
||||||
|
if let Some(last_connect) = last_connect.as_ref() {
|
||||||
|
let time_since_last = Instant::now() - *last_connect;
|
||||||
|
if time_since_last <= CONNECT_WAIT {
|
||||||
|
yew::platform::time::sleep(CONNECT_WAIT.saturating_sub(time_since_last)).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_connect = Some(Instant::now());
|
||||||
|
log::info!("connecting to {url}");
|
||||||
|
let mut ws = Self::connect_ws().await.fuse();
|
||||||
|
log::info!("connected to {url}");
|
||||||
|
|
||||||
|
log::debug!("sending self ident");
|
||||||
|
if let Err(err) = ws.send(Self::encode_message(&self.identification())).await {
|
||||||
|
log::error!("websocket identification send: {err}");
|
||||||
|
continue 'outer;
|
||||||
|
};
|
||||||
|
|
||||||
|
log::debug!("sending get state");
|
||||||
|
if let Err(err) = ws
|
||||||
|
.send(Self::encode_message(&ClientMessage::GetState))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
log::error!("websocket identification send: {err}");
|
||||||
|
continue 'outer;
|
||||||
|
};
|
||||||
|
log::debug!("beginning listening loop");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut recv = self.receiver.borrow_mut();
|
||||||
|
let msg = futures::select! {
|
||||||
|
r = ws.next() => {
|
||||||
|
match r {
|
||||||
|
Some(Ok(msg)) => msg,
|
||||||
|
Some(Err(err)) => {
|
||||||
|
log::error!("websocket recv: {err}");
|
||||||
|
continue 'outer;
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
log::warn!("websocket closed");
|
||||||
|
continue 'outer;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r = recv.next() => {
|
||||||
|
match r {
|
||||||
|
Some(msg) => {
|
||||||
|
log::info!("sending message: {msg:?}");
|
||||||
|
if let Err(err) = ws.send(
|
||||||
|
Self::encode_message(&msg)
|
||||||
|
).await {
|
||||||
|
log::error!("websocket send error: {err}");
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
log::info!("recv channel closed");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
core::mem::drop(recv);
|
||||||
|
let parse = {
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
{
|
||||||
|
match msg {
|
||||||
|
websocket::Message::Text(text) => {
|
||||||
|
serde_json::from_str::<ServerMessage>(&text)
|
||||||
|
}
|
||||||
|
websocket::Message::Bytes(items) => serde_json::from_slice(&items),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "cbor")]
|
||||||
|
{
|
||||||
|
match msg {
|
||||||
|
websocket::Message::Text(_) => {
|
||||||
|
log::error!("text messages not supported in cbor mode; discarding");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
websocket::Message::Bytes(bytes) => {
|
||||||
|
ciborium::from_reader::<ServerMessage, _>(bytes.as_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match parse {
|
||||||
|
Ok(msg) => {
|
||||||
|
if let Some(state) = self.message_to_client_state(msg) {
|
||||||
|
self.state.set(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("parsing server message: {err}; ignoring.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message_to_client_state(&self, msg: ServerMessage) -> Option<ClientEvent2> {
|
||||||
|
log::debug!("received message: {msg:?}");
|
||||||
|
Some(match msg {
|
||||||
|
ServerMessage::Disconnect => ClientEvent2::Disconnected,
|
||||||
|
ServerMessage::LobbyInfo {
|
||||||
|
joined,
|
||||||
|
mut players,
|
||||||
|
} => {
|
||||||
|
const LAST: NonZeroU8 = NonZeroU8::new(0xFF).unwrap();
|
||||||
|
players.sort_by(|l, r| l.number.unwrap_or(LAST).cmp(&r.number.unwrap_or(LAST)));
|
||||||
|
ClientEvent2::Lobby {
|
||||||
|
joined,
|
||||||
|
players: players.into_iter().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ServerMessage::GameStart { role } => ClientEvent2::ShowRole(role),
|
||||||
|
ServerMessage::InvalidMessageForGameState => {
|
||||||
|
log::error!("invalid message for game state");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
ServerMessage::NoSuchTarget => {
|
||||||
|
log::error!("no such target");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
ServerMessage::Update(PlayerUpdate::Number(new_num)) => {
|
||||||
|
let (pid, mut ident) = (*self.ident).clone();
|
||||||
|
ident.number = Some(new_num);
|
||||||
|
self.ident.set((pid, ident));
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
ServerMessage::GameOver(_)
|
||||||
|
| ServerMessage::Sleep
|
||||||
|
| ServerMessage::Reset
|
||||||
|
| ServerMessage::GameInProgress => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod client {
|
pub mod client {
|
||||||
mod client;
|
mod client;
|
||||||
|
pub mod connection;
|
||||||
pub use client::*;
|
pub use client::*;
|
||||||
}
|
}
|
||||||
pub mod host {
|
pub mod host {
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ use yew::prelude::*;
|
||||||
use crate::components::Button;
|
use crate::components::Button;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Properties)]
|
#[derive(Debug, PartialEq, Properties)]
|
||||||
pub struct InputProps {
|
pub struct SigninProps {
|
||||||
pub callback: Callback<PublicIdentity, ()>,
|
pub callback: Callback<PublicIdentity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
pub fn InputName(props: &InputProps) -> Html {
|
pub fn Signin(props: &SigninProps) -> Html {
|
||||||
let callback = props.callback.clone();
|
let callback = props.callback.clone();
|
||||||
let num_value = use_state(String::new);
|
let num_value = use_state(String::new);
|
||||||
let name_value = use_state(String::new);
|
let name_value = use_state(String::new);
|
||||||
|
|
@ -44,27 +44,7 @@ pub fn InputName(props: &InputProps) -> Html {
|
||||||
number,
|
number,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// let num_value = num_value.clone();
|
|
||||||
// let on_change = move |ev: InputEvent| {
|
|
||||||
// let data = ev.data();
|
|
||||||
// if let Some(z) = data.as_ref().and_then(|d| d.trim().parse::<u8>().ok())
|
|
||||||
// && !(z == 0 && num_value.is_empty())
|
|
||||||
// {
|
|
||||||
// let new_value = format!("{}{z}", num_value.as_str());
|
|
||||||
// num_value.set(new_value);
|
|
||||||
|
|
||||||
// return;
|
|
||||||
// } else if data.is_none()
|
|
||||||
// && let Some(target) = ev.target_dyn_into::<HtmlInputElement>()
|
|
||||||
// {
|
|
||||||
// num_value.set(target.value());
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if let Some(target) = ev.target_dyn_into::<HtmlInputElement>() {
|
|
||||||
// target.set_value(num_value.as_str());
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
let on_change = crate::components::input_element_number_oninput(num_value);
|
let on_change = crate::components::input_element_number_oninput(num_value);
|
||||||
html! {
|
html! {
|
||||||
<div class="signin">
|
<div class="signin">
|
||||||
|
|
|
||||||
|
|
@ -37,17 +37,3 @@ pub fn Identity(props: &IdentityProps) -> Html {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
|
||||||
pub struct StatefulIdentityProps {
|
|
||||||
pub ident: UseStateHandle<PublicIdentity>,
|
|
||||||
#[prop_or_default]
|
|
||||||
pub class: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component]
|
|
||||||
pub fn StatefulIdentity(props: &StatefulIdentityProps) -> Html {
|
|
||||||
html! {
|
|
||||||
<Identity ident={props.ident.deref().clone()} class={props.class.clone()}/>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,10 @@ use werewolves_proto::{
|
||||||
message::{Identification, PublicIdentity},
|
message::{Identification, PublicIdentity},
|
||||||
player::PlayerId,
|
player::PlayerId,
|
||||||
};
|
};
|
||||||
use yew::prelude::*;
|
use yew::{context::ContextProviderProps, prelude::*};
|
||||||
|
|
||||||
use crate::clients::{
|
use crate::clients::{
|
||||||
client::{Client, ClientProps, Message},
|
client::{Client, Client2, ClientContext, ClientProps, Message},
|
||||||
host::{Host, HostEvent},
|
host::{Host, HostEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -70,25 +70,62 @@ fn main() {
|
||||||
clients.append_child(&dupe).unwrap();
|
clients.append_child(&dupe).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let client =
|
let client = yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
||||||
yew::Renderer::<Client>::with_root_and_props(dupe, ClientProps { auto_join: true })
|
dupe,
|
||||||
.render();
|
ContextProviderProps {
|
||||||
client.send_message(Message::ForceIdentity(Identification {
|
context: ClientContext {
|
||||||
|
error_cb: error_callback.clone(),
|
||||||
|
forced_identity: Some(Identification {
|
||||||
player_id,
|
player_id,
|
||||||
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: None,
|
||||||
},
|
},
|
||||||
}));
|
}),
|
||||||
client.send_message(Message::SetErrorCallback(error_callback.clone()));
|
},
|
||||||
}
|
children: html! {
|
||||||
} else {
|
<Client2 auto_join=true/>
|
||||||
let client = yew::Renderer::<Client>::with_root_and_props(
|
},
|
||||||
app_element,
|
},
|
||||||
ClientProps { auto_join: false },
|
|
||||||
)
|
)
|
||||||
.render();
|
.render();
|
||||||
client.send_message(Message::SetErrorCallback(error_callback));
|
|
||||||
|
// let client = yew::Renderer::<Client2>::with_root_and_props(
|
||||||
|
// dupe,
|
||||||
|
// ClientProps { auto_join: true },
|
||||||
|
// )
|
||||||
|
// .render();
|
||||||
|
|
||||||
|
// client.send_message(Message::ForceIdentity(Identification {
|
||||||
|
// player_id,
|
||||||
|
// public: PublicIdentity {
|
||||||
|
// name: name.to_string(),
|
||||||
|
// pronouns: Some(String::from("he/him")),
|
||||||
|
// number: None,
|
||||||
|
// },
|
||||||
|
// }));
|
||||||
|
// client.send_message(Message::SetErrorCallback(error_callback.clone()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let client = yew::Renderer::<ContextProvider<ClientContext>>::with_root_and_props(
|
||||||
|
app_element,
|
||||||
|
ContextProviderProps {
|
||||||
|
context: ClientContext {
|
||||||
|
error_cb: error_callback.clone(),
|
||||||
|
forced_identity: None,
|
||||||
|
},
|
||||||
|
children: html! {
|
||||||
|
<Client2 auto_join=false/>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.render();
|
||||||
|
// let client = yew::Renderer::<Client>::with_root_and_props(
|
||||||
|
// app_element,
|
||||||
|
// ClientProps { auto_join: false },
|
||||||
|
// )
|
||||||
|
// .render();
|
||||||
|
// client.send_message(Message::SetErrorCallback(error_callback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
use gloo::storage::errors::StorageError;
|
use gloo::storage::errors::StorageError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use werewolves_proto::error::GameError;
|
use werewolves_proto::{error::GameError, message::ClientMessage};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Error)]
|
use crate::clients::client::connection::ConnectionError;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
pub enum WerewolfError {
|
pub enum WerewolfError {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
GameError(#[from] GameError),
|
GameError(#[from] GameError),
|
||||||
|
|
@ -13,6 +15,10 @@ pub enum WerewolfError {
|
||||||
InvalidTarget,
|
InvalidTarget,
|
||||||
#[error("send error: {0}")]
|
#[error("send error: {0}")]
|
||||||
SendError(#[from] futures::channel::mpsc::SendError),
|
SendError(#[from] futures::channel::mpsc::SendError),
|
||||||
|
#[error("send error: {0}")]
|
||||||
|
ClientSendError(#[from] yew::platform::pinned::mpsc::SendError<ClientMessage>),
|
||||||
|
#[error("connection error: {0}")]
|
||||||
|
ConnectionError(#[from] ConnectionError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<StorageError> for WerewolfError {
|
impl From<StorageError> for WerewolfError {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue