Skip to main content

max / makenotwork

4.1 KB · 118 lines History Blame Raw
1 //! The Askama entry point for the described "sign out a device" buttons.
2 //!
3 //! Shape 8 (`b279b9eb`), through the glue-module ruling `27d5e5b8`: a described
4 //! fragment inside an Askama page, at the granularity of one act, in a sibling
5 //! of `widgets/` rather than a member of it. The reasoning is in
6 //! [`super::widgets`]; [`super::export_act`] was the first thing it bought.
7 //!
8 //! # What the description says that the markup did not
9 //!
10 //! That revoking a session destroys something. The template drew both buttons
11 //! `btn-secondary`, so the page did not mark the destructive act even to the
12 //! stylesheet, while writing the question as an attribute only a browser reads.
13 //! Said here, a terminal host asks in its own way and no renderer can forget to
14 //! ask.
15 //!
16 //! # Why the region is named, and why there are two functions
17 //!
18 //! `DELETE /api/users/me/sessions{,/id}` is a plain API route the description
19 //! layer does not serve: it answers the whole rebuilt pane, so the control has
20 //! to say where that lands. See [`Action::replaces`], which is exactly this
21 //! case rather than the misuse it warns about.
22 //!
23 //! The bulk act is not the row act with a different address. It asks a
24 //! different question -- one device against every other one -- and a prompt is
25 //! part of what an act is, so reusing the row's wording would be describing the
26 //! wrong call. Two acts, one region.
27 //!
28 //! Ids arrive as `impl Display` rather than `&str`: the caller is Askama and
29 //! the server's ids are typed newtypes over `Uuid`, so asking for a string
30 //! would push a `to_string()` into every call site to say what `format!`
31 //! already says here.
32
33 use std::fmt;
34
35 use makeover_layout::Tone;
36 use quasi_router::screen::Act;
37 use quasi_router::{Action, Node};
38
39 /// The list this pane redraws into, which both acts replace.
40 const REGION: &str = "sessions-list";
41
42 /// Sign out one device, by session id.
43 #[must_use]
44 pub fn html(session_id: impl fmt::Display) -> String {
45 fragment(
46 Act::new(
47 "Sign out",
48 Action::delete(format!("/api/users/me/sessions/{session_id}")).replacing(REGION),
49 )
50 .tone(Tone::Danger)
51 .confirm("Sign out this device?"),
52 )
53 }
54
55 /// Sign out every device except the one reading this.
56 #[must_use]
57 pub fn all_others_html() -> String {
58 fragment(
59 Act::new(
60 "Sign out all other devices",
61 Action::delete("/api/users/me/sessions").replacing(REGION),
62 )
63 .tone(Tone::Danger)
64 .confirm("Sign out all other devices?"),
65 )
66 }
67
68 /// No shell and no region: this lands inside a document Askama has already
69 /// built, which is what every glue module here does.
70 fn fragment(act: Act) -> String {
71 use quasi_axum::Serves as _;
72
73 quasi_webview::Webview::new().fragment(&Node::Act(act))
74 }
75
76 #[cfg(test)]
77 mod tests {
78 /// Where it goes, what it redraws, and what to ask first.
79 #[test]
80 fn a_sign_out_names_its_route_its_region_and_its_prompt() {
81 let html = super::html("7");
82
83 assert!(
84 html.contains(r#"hx-delete="/api/users/me/sessions/7""#),
85 "{html}"
86 );
87 assert!(html.contains(r##"hx-target="#sessions-list""##), "{html}");
88 assert!(
89 html.contains(r#"hx-confirm="Sign out this device?""#),
90 "{html}"
91 );
92 }
93
94 /// Revoking a session destroys something, and the template said so to
95 /// nobody: both buttons were `btn-secondary`.
96 #[test]
97 fn signing_out_says_it_is_destructive() {
98 assert!(super::html("7").contains(r#"data-tone="danger""#));
99 assert!(super::all_others_html().contains(r#"data-tone="danger""#));
100 }
101
102 /// The bulk act asks its own question. Reusing the row's wording would be
103 /// asking about one device before revoking all of them.
104 #[test]
105 fn the_bulk_act_asks_about_the_set_it_acts_on() {
106 let html = super::all_others_html();
107
108 assert!(
109 html.contains(r#"hx-delete="/api/users/me/sessions""#),
110 "{html}"
111 );
112 assert!(
113 html.contains(r#"hx-confirm="Sign out all other devices?""#),
114 "{html}"
115 );
116 }
117 }
118