1use crate::wasm_utc_now;
2use chrono::{DateTime, Utc};
3use std::time::Duration;
4use yew::prelude::*;
5use yew::services::timeout::{TimeoutService, TimeoutTask};
6
7#[derive(Debug, Clone, Eq, PartialEq)]
8pub enum Kind {
9 Primary,
10 Link,
11 Info,
12 Success,
13 Warning,
14 Danger,
15}
16
17impl Kind {
18 pub fn css_class(&self) -> &'static str {
19 match self {
20 Kind::Primary => "is-primary",
21 Kind::Link => "is-link",
22 Kind::Info => "is-info",
23 Kind::Success => "is-success",
24 Kind::Warning => "is-warning",
25 Kind::Danger => "is-danger",
26 }
27 }
28}
29
30#[derive(Clone, Debug, Eq, PartialEq)]
31pub struct Message {
32 pub created_at: DateTime<Utc>,
33 pub kind: Kind,
34 pub content: String,
35 pub duration: Duration,
36}
37
38impl Message {
39 pub fn new<S: Into<String>, D: Into<Duration>>(kind: Kind, content: S, duration: D) -> Self {
40 Message {
41 kind,
42 created_at: wasm_utc_now(),
43 content: content.into(),
44 duration: duration.into(),
45 }
46 }
47}
48
49#[derive(Debug)]
50pub struct Flash {
51 link: ComponentLink<Self>,
52 props: Props,
53 timeout: TimeoutService,
54 hide_task: Option<TimeoutTask>,
55}
56
57#[derive(Debug, Clone, Properties)]
58pub struct Props {
59 #[prop_or_default]
60 pub message: Option<Message>,
61}
62
63pub enum ComponentMessage {
64 Hide,
65}
66
67impl Component for Flash {
68 type Message = ComponentMessage;
69 type Properties = Props;
70
71 fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
72 Self {
73 link,
74 props,
75 timeout: TimeoutService::default(),
76 hide_task: None,
77 }
78 }
79
80 fn update(&mut self, msg: Self::Message) -> ShouldRender {
81 match msg {
82 ComponentMessage::Hide => true,
83 }
84 }
85
86 fn change(&mut self, props: Self::Properties) -> ShouldRender {
87 if props.message != self.props.message {
88 if let Some(new_message) = &props.message {
89 self.hide_task = Some(TimeoutService::spawn(
90 new_message.duration,
91 self.link.callback(|_| ComponentMessage::Hide),
92 ));
93 }
94 self.props = props;
95 true
96 } else {
97 false
98 }
99 }
100
101 fn view(&self) -> Html {
102 if let Some(message) = &self.props.message {
103 let should_show = wasm_utc_now()
104 < message
105 .created_at
106 .checked_add_signed(
107 chrono::Duration::from_std(message.duration).expect("Invalid duration"),
108 )
109 .expect("Unexpected date math error");
110 if should_show {
111 return html! {
112 <div class=format!("notification {}", message.kind.css_class())>
113 { &message.content }
114 </div>
115 };
116 }
117 }
118
119 Html::default()
120 }
121}