Skip to main content

max / makenotwork

4.0 KB · 118 lines History Blame Raw
1 //! The Askama entry point for the two described clip controls, and the place
2 //! Shape 8's ruling is visible in one file.
3 //!
4 //! `b279b9eb` decided that destructiveness is marked separately from asking
5 //! first. Both acts here destroy something and take [`Tone::Danger`]; only one
6 //! of them asks. Deleting a clip from the library removes it from every item
7 //! using it, which is worth stopping for. Removing a clip from one item is the
8 //! undo of the Add sitting three lines below it in the same form, so
9 //! interrupting it would be asking about nothing.
10 //!
11 //! Written together for that reason. The asymmetry is the ruling's proof, and a
12 //! pair described in one pass is deliberate where two passes look like an
13 //! oversight.
14 //!
15 //! # Why each names its region
16 //!
17 //! `DELETE /api/insertions/{id}` and `DELETE /api/item-insertions/{id}` are
18 //! plain API routes the description layer does not serve. Each answers its
19 //! whole list rebuilt, so the control has to say where that lands. See
20 //! [`Action::replaces`].
21 //!
22 //! Ids arrive as `impl Display` rather than `&str`: the caller is Askama and
23 //! the server's ids are typed newtypes over `Uuid`, so asking for a string
24 //! would push a `to_string()` into every call site to say what `format!`
25 //! already says here.
26
27 use std::fmt;
28
29 use makeover_layout::Tone;
30 use quasi_router::screen::Act;
31 use quasi_router::{Action, Node};
32
33 /// Delete a clip from the creator's library.
34 ///
35 /// Asks, because the clip is shared: items the reader is not looking at lose
36 /// their pre-roll too, and the prompt is the only place that is said.
37 #[must_use]
38 pub fn delete_html(insertion_id: impl fmt::Display) -> String {
39 fragment(
40 Act::new(
41 "Delete",
42 Action::delete(format!("/api/insertions/{insertion_id}"))
43 .replacing("insertion-library"),
44 )
45 .tone(Tone::Danger)
46 .confirm("Delete this clip? It will be removed from all items using it."),
47 )
48 }
49
50 /// Take one clip off one item.
51 ///
52 /// Danger with no confirm, which is the pair's point. The template already said
53 /// the first half as `btn-danger` to the stylesheet alone; nothing said it to
54 /// any other renderer, and nothing ever asked.
55 #[must_use]
56 pub fn remove_html(placement_id: impl fmt::Display) -> String {
57 fragment(
58 Act::new(
59 "Remove",
60 Action::delete(format!("/api/item-insertions/{placement_id}"))
61 .replacing("placement-list"),
62 )
63 .tone(Tone::Danger),
64 )
65 }
66
67 /// No shell and no region: this lands inside a document Askama has already
68 /// built, which is what every glue module here does.
69 fn fragment(act: Act) -> String {
70 use quasi_axum::Serves as _;
71
72 quasi_webview::Webview::new().fragment(&Node::Act(act))
73 }
74
75 #[cfg(test)]
76 mod tests {
77 #[test]
78 fn deleting_a_clip_names_its_route_its_region_and_its_prompt() {
79 let html = super::delete_html("9");
80
81 assert!(html.contains(r#"hx-delete="/api/insertions/9""#), "{html}");
82 assert!(
83 html.contains(r##"hx-target="#insertion-library""##),
84 "{html}"
85 );
86 assert!(
87 html.contains(
88 r#"hx-confirm="Delete this clip? It will be removed from all items using it.""#
89 ),
90 "{html}"
91 );
92 }
93
94 /// The ruling in one assertion: both destroy, one asks.
95 #[test]
96 fn both_are_dangerous_and_only_one_asks() {
97 let delete = super::delete_html("9");
98 let remove = super::remove_html("4");
99
100 assert!(delete.contains(r#"data-tone="danger""#), "{delete}");
101 assert!(remove.contains(r#"data-tone="danger""#), "{remove}");
102
103 assert!(delete.contains("hx-confirm"), "{delete}");
104 assert!(!remove.contains("hx-confirm"), "{remove}");
105 }
106
107 #[test]
108 fn removing_a_placement_names_its_route_and_its_region() {
109 let html = super::remove_html("4");
110
111 assert!(
112 html.contains(r#"hx-delete="/api/item-insertions/4""#),
113 "{html}"
114 );
115 assert!(html.contains(r##"hx-target="#placement-list""##), "{html}");
116 }
117 }
118