flams_web_utils/components/
errors.rs1use 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}