diff options
author | Kjetil Orbekk <kj@orbekk.com> | 2022-11-13 16:07:27 -0500 |
---|---|---|
committer | Kjetil Orbekk <kj@orbekk.com> | 2022-11-13 16:07:27 -0500 |
commit | d4650a3160d52d289686fb59efbf8f0a436b71eb (patch) | |
tree | 0b38d0143824a3a7cab83e189fe1a2302f9685a7 /webapp | |
parent | 0012c25c9ab94754f3d6396e91dc3ae63d19ac9c (diff) |
Add create/leave table options
Diffstat (limited to 'webapp')
-rw-r--r-- | webapp/src/components.rs | 8 | ||||
-rw-r--r-- | webapp/src/components/app_context_provider.rs | 141 | ||||
-rw-r--r-- | webapp/src/components/error_info.rs | 4 | ||||
-rw-r--r-- | webapp/src/components/table.rs | 21 | ||||
-rw-r--r-- | webapp/src/main.rs | 65 | ||||
-rw-r--r-- | webapp/src/routing.rs | 13 |
6 files changed, 174 insertions, 78 deletions
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<Option<UserInfo>>, + error: UseStateHandle<Option<ErrorInfoProperties>>, +} + +#[derive(Clone, PartialEq)] pub struct AppContext { - pub error: Option<ErrorInfoProperties>, - pub user: Option<UserInfo>, + state: AppState, + history: AnyHistory, +} + +impl AppContext { + fn spawn_async<F>(&self, f: F) + where + F: Future<Output = Result<(), anyhow::Error>> + '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<UserInfo, anyhow::Error> { Ok(user_info) } +pub fn use_app_context() -> AppContext { + let state : AppState = use_context::<AppState>().unwrap(); + let history = use_history().unwrap(); + + AppContext { state: state, history } +} + #[function_component(AppContextProvider)] pub fn app_context_provider(props: &Props) -> Html { - let context: UseStateHandle<Option<Rc<AppContext>>> = use_state(|| None); + let user: UseStateHandle<Option<UserInfo>> = use_state(|| None); + let error: UseStateHandle<Option<ErrorInfoProperties>> = 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! { - <p>{ "Loading app..." }</p> - }, - Some(context) => html! { - <ContextProvider<Rc<AppContext>> {context}> + if user.is_none() && error.is_none() { + return html! { + <p>{ "Loading app..." }</p> + }; + } + + info!("Recomputing state"); + info!("User is {:?}", *user); + + let state = AppState { + user, + error, + }; + + html! { + <ContextProvider<AppState> context={state}> { for props.children.iter() } - </ContextProvider<Rc<AppContext>>> - }, + </ContextProvider<AppState>> } } 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! { <div class="error-box"> <p> - { format!("Error: {}. ", props.message) } + { format!("Error: {}. ", props.message) } <button onclick={reload}>{ "Reload" }</button> - </p> + </p> </div> } } 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! { <> - <p>{ format!("This is table {}", props.table.id) }</p> + <p>{ format!("This is table {}", props.table.id) }</p> + <button onclick={leave_table}> + { "Leave table" } + </button> </> } } 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::<Rc<AppContext>>().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! { - <p>{ format!("Logged in as {}", userinfo.username) }</p> - }, + <p>{ format!("Logged in as {}", userinfo.username) }</p> + }, None => html! { <p><a href="/api/login">{ "Log in" }</a></p> }, }; - 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 { - <ErrorInfo ..error.clone()/> + if let Some(error) = &ctx.error() { + <ErrorInfo ..(*error).clone()/> } - <ul> - <li>{ user }</li> + <ul> + <li>{ user }</li> <li><Link<Route> to={Route::Playground}>{ "Playground" }</Link<Route>></li> - <li><button onclick={create_table}>{ "Create table" }</button></li> - </ul> + <li><button onclick={create_table}>{ "Create table" }</button></li> + </ul> </> } } fn switch(routes: &Route) -> Html { match routes { - Route::Home => html!{ <Home/> }, + Route::Home => html! { <Home/> }, Route::Playground => html! { <div class="game-layout"><Game /></div> }, - Route::Table { id } => html! { - <Table table={ protocol::Table { id: *id } } /> - }, + Route::Table { id } => html! { + <Table table={ protocol::Table { id: *id } } /> + }, } } 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 }, +} + |