Skip to main content

max / makenotwork

3.5 KB · 113 lines History Blame Raw
1 //! The Askama entry point for the two described git-repository removals.
2 //!
3 //! Shape 8 (`b279b9eb`), through the glue-module ruling `27d5e5b8`. Both
4 //! destroy an association rather than a repository, and both ask, because a
5 //! creator cannot tell the two apart from the button alone.
6 //!
7 //! Each prompt names what it acts on, so the two are separate calls rather than
8 //! one with a label parameter: the sentence differs, not just the address.
9 //!
10 //! `DELETE /api/projects/{id}/repos/{name}` and
11 //! `DELETE /api/repos/{id}/collaborators/{user}` are plain API routes the
12 //! description layer does not serve. Both answer the whole section rebuilt, so
13 //! the control says where that lands. See [`Action::replaces`].
14 //!
15 //! Ids arrive as `impl Display` rather than `&str`: the caller is Askama and
16 //! the server's ids are typed newtypes over `Uuid`, so asking for a string
17 //! would push a `to_string()` into every call site to say what `format!`
18 //! already says here.
19
20 use std::fmt;
21
22 use makeover_layout::Tone;
23 use quasi_router::screen::Act;
24 use quasi_router::{Action, Node};
25
26 /// The section both acts redraw.
27 const REGION: &str = "git-repos-section";
28
29 /// Take a repository off a project.
30 #[must_use]
31 pub fn unlink_html(project_id: impl fmt::Display, repo_name: &str) -> String {
32 fragment(
33 Act::new(
34 "Unlink",
35 Action::delete(format!("/api/projects/{project_id}/repos/{repo_name}"))
36 .replacing(REGION),
37 )
38 .tone(Tone::Danger)
39 .confirm(format!("Unlink '{repo_name}' from this project?")),
40 )
41 }
42
43 /// Take a collaborator off a repository.
44 #[must_use]
45 pub fn remove_collaborator_html(
46 repo_id: impl fmt::Display,
47 repo_name: &str,
48 user_id: impl fmt::Display,
49 username: &str,
50 ) -> String {
51 fragment(
52 Act::new(
53 "Remove",
54 Action::delete(format!("/api/repos/{repo_id}/collaborators/{user_id}"))
55 .replacing(REGION),
56 )
57 .tone(Tone::Danger)
58 .confirm(format!("Remove @{username} from {repo_name}?")),
59 )
60 }
61
62 /// No shell and no region: this lands inside a document Askama has already
63 /// built, which is what every glue module here does.
64 fn fragment(act: Act) -> String {
65 use quasi_axum::Serves as _;
66
67 quasi_webview::Webview::new().fragment(&Node::Act(act))
68 }
69
70 #[cfg(test)]
71 mod tests {
72 #[test]
73 fn an_unlink_names_its_route_its_region_and_the_repository_it_asks_about() {
74 let html = super::unlink_html("p1", "synth");
75
76 assert!(
77 html.contains(r#"hx-delete="/api/projects/p1/repos/synth""#),
78 "{html}"
79 );
80 assert!(
81 html.contains(r##"hx-target="#git-repos-section""##),
82 "{html}"
83 );
84 assert!(
85 html.contains(r#"hx-confirm="Unlink 'synth' from this project?""#),
86 "{html}"
87 );
88 }
89
90 #[test]
91 fn removing_a_collaborator_asks_about_the_person_and_the_repository() {
92 let html = super::remove_collaborator_html("r1", "synth", "u9", "ada");
93
94 assert!(
95 html.contains(r#"hx-delete="/api/repos/r1/collaborators/u9""#),
96 "{html}"
97 );
98 assert!(
99 html.contains(r#"hx-confirm="Remove @ada from synth?""#),
100 "{html}"
101 );
102 }
103
104 #[test]
105 fn both_removals_say_they_are_dangerous() {
106 assert!(super::unlink_html("p1", "synth").contains(r#"data-tone="danger""#));
107 assert!(
108 super::remove_collaborator_html("r1", "synth", "u9", "ada")
109 .contains(r#"data-tone="danger""#)
110 );
111 }
112 }
113