Skip to main content

flams_web_utils/components/
errors.rs

1use std::borrow::Cow;
2
3use ftml_component_utils::toasts::{
4    MessageBar, MessageBarBody, MessageBarIntent, ToastOptions, ToastPosition, ToasterInjection,
5};
6use ftml_component_utils::Spinner;
7use leptos::prelude::*;
8
9#[inline]
10pub fn display_error(err: Cow<'static, str>) -> AnyView {
11    #[cfg(feature = "hydrate")]
12    {
13        error_toast(err.clone());
14    }
15    view!(<h3 style="color:red">"Error: "{err}</h3>).into_any()
16}
17
18pub fn message_action<
19    I: Clone + Send + Sync + 'static,
20    O,
21    E: std::fmt::Display + Send + 'static,
22    V: std::fmt::Display + Send + 'static,
23    Fut: std::future::Future<Output = Result<O, E>> + Send,
24>(
25    run: impl Fn(I) -> Fut + Send + Sync + Clone + 'static,
26    msg: impl Fn(O) -> V + Send + Sync + Clone + 'static,
27) -> Action<I, ()> {
28    let toaster = ToasterInjection::expect_context();
29    Action::new(move |args: &I| {
30        let run = run.clone();
31        let msg = msg.clone();
32        let args = args.clone();
33        async move {
34            match run(args).await {
35                Ok(r) => success_with_toaster(msg(r), toaster),
36                Err(e) => error_with_toaster(e, toaster),
37            }
38        }
39    })
40}
41
42pub fn waiting_message_action<
43    I: Clone + Send + Sync + 'static,
44    O,
45    E: std::fmt::Display + Send + 'static,
46    V: std::fmt::Display + Send + 'static,
47    Fut: std::future::Future<Output = Result<O, E>> + Send + 'static,
48>(
49    run: impl Fn(&I) -> Fut + Send + Sync + Clone + 'static,
50    msg: impl Fn(O) -> V + Send + Sync + Clone + 'static,
51) -> (Action<I, ()>, impl IntoView) {
52    let toaster = ToasterInjection::expect_context();
53    waiting_action(run, move |o| success_with_toaster(msg(o), toaster))
54}
55
56pub fn waiting_action<
57    I: Clone + Send + Sync + 'static,
58    O,
59    E: std::fmt::Display + Send + 'static,
60    Fut: std::future::Future<Output = Result<O, E>> + Send + 'static,
61>(
62    run: impl Fn(&I) -> Fut + Send + Sync + Clone + 'static,
63    msg: impl Fn(O) + Send + Sync + Clone + 'static,
64) -> (Action<I, ()>, AnyView) {
65    use ftml_component_utils::{Dialog, DialogBody, DialogSurface};
66    let toaster = ToasterInjection::expect_context();
67    let open = RwSignal::new(false);
68    let a = Action::new(move |args: &I| {
69        let r = run(args);
70        open.set(true);
71        let msg = msg.clone();
72        async move {
73            match r.await {
74                Ok(r) => {
75                    open.set(false);
76                    msg(r);
77                }
78                Err(e) => {
79                    open.set(false);
80                    error_with_toaster(e, toaster);
81                }
82            }
83        }
84    });
85    (a,view!{<Dialog mask_closeable=false close_on_esc=false open=open ><DialogSurface><DialogBody><Spinner/></DialogBody></DialogSurface></Dialog>}.into_any())
86}
87
88#[inline]
89pub fn error_toast(err: impl std::fmt::Display + Send + 'static) {
90    let toaster = ToasterInjection::expect_context();
91    error_with_toaster(err, toaster);
92}
93
94#[inline]
95pub fn success_toast(msg: impl std::fmt::Display + Send + 'static) {
96    let toaster = ToasterInjection::expect_context();
97    success_with_toaster(msg, toaster);
98}
99
100pub fn error_with_toaster(err: impl std::fmt::Display + Send + 'static, toaster: ToasterInjection) {
101    tracing::error!("{err}");
102    toaster.dispatch_toast(
103        move || {
104            view! {
105              <MessageBar intent=MessageBarIntent::Error>
106                <MessageBarBody>{err.to_string()}</MessageBarBody>
107              </MessageBar>
108            }
109        },
110        ToastOptions::default().with_position(ToastPosition::Top),
111    );
112}
113
114fn success_with_toaster(msg: impl std::fmt::Display + Send + 'static, toaster: ToasterInjection) {
115    tracing::info!("{msg}");
116    toaster.dispatch_toast(
117        move || {
118            view! {
119              <MessageBar intent=MessageBarIntent::Success>
120                <MessageBarBody>{msg.to_string()}</MessageBarBody>
121              </MessageBar>
122            }
123        },
124        ToastOptions::default().with_position(ToastPosition::Top),
125    );
126}