Skip to main content

tablo_test/protocol/
bell.rs

1//! Reads the notification bell out of a rendered panel page: `[data-unread]` on the trigger's
2//! count, and `li[data-notification]` for each listed notification, with its `data-read` and
3//! `data-type`.
4
5use super::dom::{attr_value, strip_text, tags_where};
6
7/// The bell a panel page renders in its topbar.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Bell {
10    /// The count over the trigger; `0` when it shows none.
11    pub unread: u64,
12    /// The notifications the sheet lists, newest first.
13    pub notifications: Vec<BellNotification>,
14}
15
16/// One notification the bell's sheet lists.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct BellNotification {
19    /// The stored notification's id, which the bell's procedures take.
20    pub id: String,
21    pub title: String,
22    pub read: bool,
23    /// The status token: `success`, `error`, `info` or `warning`.
24    pub status: String,
25    /// The `(label, href)` of each link it offers.
26    pub actions: Vec<(String, String)>,
27}
28
29/// The bell `html` renders, or `None` when the page shows none.
30pub fn bell(html: &str) -> Option<Bell> {
31    let trigger = tags_where(html, |tag, attrs| {
32        tag == "button"
33            && attr_value(attrs, "aria-controls").as_deref() == Some("tablo-notifications")
34    });
35    if trigger.is_empty() {
36        return None;
37    }
38    let unread = tags_where(html, |tag, attrs| {
39        tag == "span" && attr_value(attrs, "data-unread").is_some()
40    })
41    .first()
42    .and_then(|(_, attrs, _)| attr_value(attrs, "data-unread")?.parse().ok())
43    .unwrap_or(0);
44    let notifications = tags_where(html, |tag, attrs| {
45        tag == "li" && attr_value(attrs, "data-notification").is_some()
46    })
47    .into_iter()
48    .map(|(_, attrs, inner)| BellNotification {
49        id: attr_value(&attrs, "data-notification").unwrap_or_default(),
50        title: tags_where(&inner, |tag, _| tag == "p")
51            .first()
52            .map(|(_, _, text)| strip_text(text))
53            .unwrap_or_default(),
54        read: attr_value(&attrs, "data-read").as_deref() == Some("true"),
55        status: attr_value(&attrs, "data-type").unwrap_or_default(),
56        actions: tags_where(&inner, |tag, _| tag == "a")
57            .into_iter()
58            .map(|(_, attrs, text)| {
59                (
60                    strip_text(&text),
61                    attr_value(&attrs, "href").unwrap_or_default(),
62                )
63            })
64            .collect(),
65    })
66    .collect();
67    Some(Bell {
68        unread,
69        notifications,
70    })
71}