From d4650a3160d52d289686fb59efbf8f0a436b71eb Mon Sep 17 00:00:00 2001 From: Kjetil Orbekk Date: Sun, 13 Nov 2022 16:07:27 -0500 Subject: Add create/leave table options --- webapp/src/components.rs | 8 +- webapp/src/components/app_context_provider.rs | 141 ++++++++++++++++++++------ webapp/src/components/error_info.rs | 4 +- webapp/src/components/table.rs | 21 +++- webapp/src/main.rs | 65 +++++------- webapp/src/routing.rs | 13 +++ 6 files changed, 174 insertions(+), 78 deletions(-) create mode 100644 webapp/src/routing.rs (limited to 'webapp/src') diff --git a/webapp/src/components.rs b/webapp/src/components.rs index 0326c09..018b1c3 100644 --- a/webapp/src/components.rs +++ b/webapp/src/components.rs @@ -2,29 +2,29 @@ use crate::card::Suit; mod app_context_provider; mod bidding; -mod error_info; mod bidding_box; mod bidding_table; mod card; +mod error_info; mod game; mod hand; mod show_bid; +mod table; mod trick_in_play; mod tricks_played; -mod table; pub use self::app_context_provider::*; pub use self::bidding::*; pub use self::bidding_box::*; pub use self::bidding_table::*; pub use self::card::*; +pub use self::error_info::*; pub use self::game::*; pub use self::hand::*; pub use self::show_bid::*; +pub use self::table::*; pub use self::trick_in_play::*; pub use self::tricks_played::*; -pub use self::error_info::*; -pub use self::table::*; pub fn suit_css_class(suit: Suit) -> &'static str { match suit { diff --git a/webapp/src/components/app_context_provider.rs b/webapp/src/components/app_context_provider.rs index 4cf233b..19e7611 100644 --- a/webapp/src/components/app_context_provider.rs +++ b/webapp/src/components/app_context_provider.rs @@ -1,17 +1,89 @@ +use crate::routing::Route; use gloo_net::http::Request; +use log::info; use protocol::UserInfo; -use std::rc::Rc; +use uuid::Uuid; +use std::{future::Future, rc::Rc}; +use wasm_bindgen_futures::spawn_local; use yew::prelude::*; +use yew_router::prelude::*; #[derive(Properties, Clone, PartialEq, Debug)] pub struct ErrorInfoProperties { pub message: String, } -#[derive(Clone, Debug, PartialEq, Default)] +#[derive(Clone, PartialEq)] +pub struct AppState { + user: UseStateHandle>, + error: UseStateHandle>, +} + +#[derive(Clone, PartialEq)] pub struct AppContext { - pub error: Option, - pub user: Option, + state: AppState, + history: AnyHistory, +} + +impl AppContext { + fn spawn_async(&self, f: F) + where + F: Future> + 'static, + { + let error = self.state.error.clone(); + spawn_local(async move { + if let Err(err) = f.await { + error.set(Some(ErrorInfoProperties { + message: format!("Some error occured: {:?}", err), + })); + } + }); + } + + pub fn user(&self) -> Option<&UserInfo> { + self.state.user.as_ref() + } + + pub fn error(&self) -> Option<&ErrorInfoProperties> { + self.state.error.as_ref() + } + + pub fn create_table(&self) { + let user = self.state.user.clone(); + let history = self.history.clone(); + self.spawn_async(async move { + let response = Request::post("/api/table").send().await?; + let table_id: Uuid = response.json().await?; + info!("Created table {table_id}"); + if let Some(user_info) = user.as_ref() { + user.set(Some(UserInfo { + table: Some(protocol::Table { id: table_id }), + ..(user_info.clone()) + })); + } + history.push(Route::Home); + Ok(()) + }); + } + + pub fn leave_table(&self) { + let user = self.state.user.clone(); + let history = self.history.clone(); + self.spawn_async(async move { + let response = Request::delete("/api/table").send().await?; + if !response.ok() { + anyhow::bail!("error while leaving table"); + } + if let Some(user_info) = user.as_ref() { + user.set(Some(UserInfo { + table: None, + ..(user_info.clone()) + })); + } + history.push(Route::Home); + Ok(()) + }); + } } #[derive(Properties, Clone, PartialEq)] @@ -32,30 +104,30 @@ async fn initialize_user_info() -> Result { Ok(user_info) } +pub fn use_app_context() -> AppContext { + let state : AppState = use_context::().unwrap(); + let history = use_history().unwrap(); + + AppContext { state: state, history } +} + #[function_component(AppContextProvider)] pub fn app_context_provider(props: &Props) -> Html { - let context: UseStateHandle>> = use_state(|| None); + let user: UseStateHandle> = use_state(|| None); + let error: UseStateHandle> = use_state(|| None); { - let context = context.clone(); + let user = user.clone(); + let error = error.clone(); use_effect_with_deps( move |_| { - wasm_bindgen_futures::spawn_local(async move { - let previous_context: AppContext = context - .as_ref() - .map_or(Default::default(), |c| (**c).clone()); - context.set(Some(Rc::new(match initialize_user_info().await { - Ok(user_info) => AppContext { - user: Some(user_info), - ..previous_context - }, - Err(e) => AppContext { - error: Some(ErrorInfoProperties { - message: format!("Could not contact server"), - }), - ..previous_context - }, - }))); + spawn_local(async move { + match initialize_user_info().await { + Ok(user_info) => user.set(Some(user_info)), + Err(e) => error.set(Some(ErrorInfoProperties { + message: format!("Could not contact server"), + })), + }; }); || () }, @@ -63,14 +135,23 @@ pub fn app_context_provider(props: &Props) -> Html { ); } - match &*context { - None => html! { -

{ "Loading app..." }

- }, - Some(context) => html! { - > {context}> + if user.is_none() && error.is_none() { + return html! { +

{ "Loading app..." }

+ }; + } + + info!("Recomputing state"); + info!("User is {:?}", *user); + + let state = AppState { + user, + error, + }; + + html! { + context={state}> { for props.children.iter() } -
>> - }, + > } } diff --git a/webapp/src/components/error_info.rs b/webapp/src/components/error_info.rs index 4ec5ba9..cb5a9c1 100644 --- a/webapp/src/components/error_info.rs +++ b/webapp/src/components/error_info.rs @@ -9,9 +9,9 @@ pub fn error_info(props: &ErrorInfoProperties) -> Html { html! {

- { format!("Error: {}. ", props.message) } + { format!("Error: {}. ", props.message) } -

+

} } diff --git a/webapp/src/components/table.rs b/webapp/src/components/table.rs index 8e8b5c8..829441e 100644 --- a/webapp/src/components/table.rs +++ b/webapp/src/components/table.rs @@ -1,15 +1,26 @@ use yew::prelude::*; +use yew_router::prelude::*; + +use crate::use_app_context; #[function_component(Table)] pub fn table(props: &TableProps) -> Html { - // let leave_table = { - // Callback::from(move |_| { - // }); - // }; + let ctx = use_app_context(); + let history = use_history().unwrap(); + + let leave_table = { + let ctx = ctx.clone(); + Callback::from(move |_| { + ctx.leave_table(); + }) + }; html! { <> -

{ format!("This is table {}", props.table.id) }

+

{ format!("This is table {}", props.table.id) }

+ } } diff --git a/webapp/src/main.rs b/webapp/src/main.rs index b275b49..577b720 100644 --- a/webapp/src/main.rs +++ b/webapp/src/main.rs @@ -2,29 +2,21 @@ use std::rc::Rc; #[allow(unused_imports)] use log::{debug, error, info, warn}; -use uuid::Uuid; use yew::prelude::*; use yew_router::prelude::*; pub mod bridge_engine; pub mod card; pub mod components; -use components::{AppContextProvider, AppContext, Game, ErrorInfo, Table}; +use components::{AppContext, AppContextProvider, ErrorInfo, Game, Table}; use gloo_net::http::Request; extern crate wee_alloc; +pub mod routing; +use crate::{routing::Route, components::use_app_context}; +use uuid::Uuid; // Use `wee_alloc` as the global allocator. #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; -#[derive(Clone, Routable, PartialEq)] - -enum Route { - #[at("/")] - Home, - #[at("/playground")] - Playground, - #[at("/table/:id")] - Table { id: Uuid, }, -} fn main() { std::panic::set_hook(Box::new(console_error_panic_hook::hook)); @@ -47,53 +39,52 @@ pub fn app() -> Html { #[function_component(Home)] fn home() -> Html { - let ctx = use_context::>().unwrap(); + let ctx = use_app_context(); - info!("User: {:#?}", ctx.user); + info!("User: {:#?}", ctx.user()); - let user = match &ctx.user { + let user = match &ctx.user() { Some(userinfo) => html! { -

{ format!("Logged in as {}", userinfo.username) }

- }, +

{ format!("Logged in as {}", userinfo.username) }

+ }, None => html! {

{ "Log in" }

}, }; - if let Some(table) = ctx.user.as_ref().and_then(|u| u.table.as_ref() ) { - let history = use_history().unwrap(); - history.push(Route::Table { id: table.id }); + if let Some(table) = ctx.user().as_ref().and_then(|u| u.table.as_ref()) { + let history = use_history().unwrap(); + history.push(Route::Table { id: table.id }); } - let create_table = Callback::from(|_| { - wasm_bindgen_futures::spawn_local(async move { - let response = Request::post("/api/table").send().await.unwrap(); - let table_id: Uuid = response.json().await.unwrap(); - info!("Created table {table_id}"); - }); - }); + let create_table = { + let ctx = ctx.clone(); + Callback::from(move |_| { + ctx.create_table(); + }) + }; html! { <> - if let Some(error) = &ctx.error { - + if let Some(error) = &ctx.error() { + } -
    -
  • { user }
  • +
      +
    • { user }
    • to={Route::Playground}>{ "Playground" }>
    • -
    • -
    +
  • +
} } fn switch(routes: &Route) -> Html { match routes { - Route::Home => html!{ }, + Route::Home => html! { }, Route::Playground => html! {
}, - Route::Table { id } => html! { - - }, + Route::Table { id } => html! { +
+ }, } } diff --git a/webapp/src/routing.rs b/webapp/src/routing.rs new file mode 100644 index 0000000..e451791 --- /dev/null +++ b/webapp/src/routing.rs @@ -0,0 +1,13 @@ +use yew_router::prelude::*; +use uuid::Uuid; + +#[derive(Clone, Routable, PartialEq)] +pub enum Route { + #[at("/")] + Home, + #[at("/playground")] + Playground, + #[at("/table/:id")] + Table { id: Uuid }, +} + -- cgit v1.2.3