summaryrefslogtreecommitdiff
path: root/webapp
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2022-11-13 16:07:27 -0500
committerKjetil Orbekk <kj@orbekk.com>2022-11-13 16:07:27 -0500
commitd4650a3160d52d289686fb59efbf8f0a436b71eb (patch)
tree0b38d0143824a3a7cab83e189fe1a2302f9685a7 /webapp
parent0012c25c9ab94754f3d6396e91dc3ae63d19ac9c (diff)
Add create/leave table options
Diffstat (limited to 'webapp')
-rw-r--r--webapp/src/components.rs8
-rw-r--r--webapp/src/components/app_context_provider.rs141
-rw-r--r--webapp/src/components/error_info.rs4
-rw-r--r--webapp/src/components/table.rs21
-rw-r--r--webapp/src/main.rs65
-rw-r--r--webapp/src/routing.rs13
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 },
+}
+