lmjtfy.git / apps / lmjtfy / src / view / toast.rs

The toasts every open page is shown when something happens elsewhere, pushed by the archive over /live. Pure: what happened in, markup out.

4use archive::{Answer, Fetch};
5use maud::{Markup, html};
7use super::url_encode;

Someone asked a question. shown is the question and Jev's answers if the feed may show it; otherwise the toast says only that someone asked. data-q lets the page that asked it skip its own toast.

12pub fn asked(shown: Option<(&str, &[Answer])>) -> Markup {
13    html! {
14        @match shown {
15            Some((input, answers)) => {
16                div .toast data-q=(input) {
17                    "Someone asked "
18                    a href={ "/?q=" (url_encode(input)) } { "“" (input) "”" }
19                    @if !answers.is_empty() {
20                        " · Jev: "
21                        b { (answers.iter().map(|answer| answer.headline.as_str()).collect::<Vec<_>>().join(" ")) }
22                    }
23                }
24            }
25            None => div .toast { "Someone asked Jev something." },
26        }
27    }
28}

What a deploy changed, told to a page from before it.

31pub fn note(note: &str) -> Markup {
32    html! { div .toast .note { b { "New: " } (note) } }
33}

Someone cloned or pulled a repository (lmjtfy.git): this site's is "the code", any other is named.

37pub fn fetched(fetch: Fetch, served: &str) -> Markup {
38    let what = match fetch {
39        Fetch::Clone => "cloned",
40        Fetch::Pull => "pulled",
41    };
42    html! {
43        div .toast {
44            "Someone " (what) " "
45            a href={ "/" (served) } { @if served == "lmjtfy.git" { "the code" } @else { (served) } }
46            "."
47        }
48    }
49}
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use archive::Detail;
55
56    #[test]
57    fn an_unlisted_question_is_not_shown() {
58        let html = asked(None).into_string();
59        assert_eq!(html, r#"<div class="toast">Someone asked Jev something.</div>"#);
60    }
61
62    #[test]
63    fn a_listed_question_is_shown_escaped_with_its_answer() {
64        let answers = [Answer { headline: "No.".into(), detail: Detail::Noul { p_yes: 0.03 } }];
65        let html = asked(Some(("is <b>this</b> ok?", &answers))).into_string();
66        assert!(html.contains("&lt;b&gt;this&lt;/b&gt;"), "{html}");
67        assert!(html.contains("<b>No.</b>"), "{html}");
68        assert!(html.contains(r#"href="/?q=is+%3Cb%3Ethis%3C%2Fb%3E+ok%3F""#), "{html}");
69    }
70
71    #[test]
72    fn a_release_note_is_text() {
73        assert_eq!(note("a <b> c").into_string(), r#"<div class="toast note"><b>New: </b>a &lt;b&gt; c</div>"#);
74    }
75
76    #[test]
77    fn a_clone_links_to_the_code() {
78        assert!(fetched(Fetch::Pull, "lmjtfy.git").into_string().contains(r#"Someone pulled <a href="/lmjtfy.git">the code</a>."#));
79        assert!(fetched(Fetch::Clone, "jevsnes.git").into_string().contains(r#"Someone cloned <a href="/jevsnes.git">jevsnes.git</a>."#));
80    }
81}