diff --git a/web/src/components/mod.rs b/web/src/components/mod.rs index a8da35a..1af74df 100644 --- a/web/src/components/mod.rs +++ b/web/src/components/mod.rs @@ -1,2 +1,3 @@ pub mod card; pub mod summary; +pub mod toast; diff --git a/web/src/components/toast.rs b/web/src/components/toast.rs new file mode 100644 index 0000000..a66a2e0 --- /dev/null +++ b/web/src/components/toast.rs @@ -0,0 +1,20 @@ +//! Auto-dismissing error toast. +use gloo_timers::callback::Timeout; +use sycamore::prelude::*; + +const TOAST_MS: u32 = 10_000; + +/// Renders the error from `error` as a toast; hides it after `TOAST_MS`. +/// A new error replaces the message and restarts the timer. +pub fn toast(error: Signal>) -> View { + create_effect(move || { + if error.get_clone().is_some() { + // Held until cleanup; dropped (cancelled) when the effect re-runs. + let timeout = Timeout::new(TOAST_MS, move || error.set(None)); + on_cleanup(move || drop(timeout)); + } + }); + view! { + (move || error.get_clone().map(|e| view! { div(class="toast") { (e) } })) + } +} diff --git a/web/src/pages/game.rs b/web/src/pages/game.rs index 3450007..e64204d 100644 --- a/web/src/pages/game.rs +++ b/web/src/pages/game.rs @@ -3,6 +3,7 @@ use sycamore::prelude::*; use crate::components::card::{card_back, card_img}; use crate::components::summary::{hand_summary_modal, summary_rows}; +use crate::components::toast::toast; use crate::model::{card_label, GameView, MoveView, PlayerView, Scores, ServerMessage}; use crate::ws::{self, GameSocket}; @@ -123,7 +124,7 @@ pub fn GamePage(id: String) -> View { view! { div(class="game-page") { - (move || error.get_clone().map(|e| view! { div(class="toast") { (e) } })) + (toast(error)) (move || match game.get_clone() { None => { let status = if closed.get() { diff --git a/web/src/pages/history.rs b/web/src/pages/history.rs index d8d6ae1..37a9862 100644 --- a/web/src/pages/history.rs +++ b/web/src/pages/history.rs @@ -3,6 +3,7 @@ use wasm_bindgen_futures::spawn_local; use sycamore::prelude::*; use crate::api; +use crate::components::toast::toast; use crate::model::MatchesPage; #[component] @@ -45,7 +46,7 @@ pub fn HistoryPage() -> View { a(href="/leaderboard") { "Leaderboard" } } h1 { "My matches" } - (move || error.get_clone().map(|e| view! { div(class="toast") { (e) } })) + (toast(error)) (move || match page.get_clone() { None => view! { p(class="status") { "Loading…" } }, Some(_) if rows.get_clone().is_empty() => view! { diff --git a/web/src/pages/leaderboard.rs b/web/src/pages/leaderboard.rs index 20a9368..d832b8a 100644 --- a/web/src/pages/leaderboard.rs +++ b/web/src/pages/leaderboard.rs @@ -3,6 +3,7 @@ use wasm_bindgen_futures::spawn_local; use sycamore::prelude::*; use crate::api; +use crate::components::toast::toast; use crate::model::LeaderboardPage; #[component] @@ -24,7 +25,7 @@ pub fn LeaderboardPage() -> View { a(href="/history") { "My matches" } } h1 { "Leaderboard" } - (move || error.get_clone().map(|e| view! { div(class="toast") { (e) } })) + (toast(error)) (move || match page.get_clone() { None => view! { p(class="status") { "Loading…" } }, Some(p) => { diff --git a/web/src/pages/lobby.rs b/web/src/pages/lobby.rs index 9ec380f..2e224b2 100644 --- a/web/src/pages/lobby.rs +++ b/web/src/pages/lobby.rs @@ -4,6 +4,7 @@ use sycamore::prelude::*; use sycamore_router::navigate; use crate::api; +use crate::components::toast::toast; use crate::model::{GameTypeInfo, User}; /// Used when the game-types fetch fails: match creation must still work. @@ -70,7 +71,7 @@ pub fn LobbyPage() -> View { view! { div(class="lobby") { h1 { "Scopone scientifico" } - (move || error.get_clone().map(|e| view! { div(class="toast") { (e) } })) + (toast(error)) (move || match user.get_clone() { None => view! { p(class="status") { "Loading…" } }, Some(None) => view! {