Author: Manos Pitsidianakis [manos@pitsidianak.is]
Hash: 6b2c88a44f85765b16b4081e72ee1421436a9e61
Timestamp: Thu, 18 May 2023 19:15:05 +0000 (1 year ago)

+89 -4 +/-9 browse
web: show list topics on site
1diff --git a/Cargo.lock b/Cargo.lock
2index 3fcba1a..95f65b9 100644
3--- a/Cargo.lock
4+++ b/Cargo.lock
5 @@ -2036,6 +2036,7 @@ dependencies = [
6 "serde",
7 "serde_json",
8 "serde_urlencoded",
9+ "stderrlog",
10 "tempfile",
11 "tokio",
12 "tower",
13 diff --git a/mailpot-tests/src/lib.rs b/mailpot-tests/src/lib.rs
14index dccb32b..d759938 100644
15--- a/mailpot-tests/src/lib.rs
16+++ b/mailpot-tests/src/lib.rs
17 @@ -42,7 +42,7 @@ pub fn init_stderr_logging() {
18 INIT_STDERR_LOGGING.call_once(|| {
19 stderrlog::new()
20 .quiet(false)
21- .verbosity(15)
22+ .verbosity(log::LevelFilter::Trace)
23 .show_module_names(true)
24 .timestamp(stderrlog::Timestamp::Millisecond)
25 .init()
26 diff --git a/rest-http/src/main.rs b/rest-http/src/main.rs
27index 180de46..704e406 100644
28--- a/rest-http/src/main.rs
29+++ b/rest-http/src/main.rs
30 @@ -7,9 +7,13 @@ async fn main() {
31 let config_path = std::env::args()
32 .nth(1)
33 .expect("Expected configuration file path as first argument.");
34+ #[cfg(test)]
35+ let verbosity = log::LevelFilter::Trace;
36+ #[cfg(not(test))]
37+ let verbosity = log::LevelFilter::Info;
38 stderrlog::new()
39 .quiet(false)
40- .verbosity(15)
41+ .verbosity(verbosity)
42 .show_module_names(true)
43 .timestamp(stderrlog::Timestamp::Millisecond)
44 .init()
45 diff --git a/web/Cargo.toml b/web/Cargo.toml
46index a203fc7..3e0bef5 100644
47--- a/web/Cargo.toml
48+++ b/web/Cargo.toml
49 @@ -34,6 +34,7 @@ percent-encoding = { version = "^2.1" }
50 rand = { version = "^0.8", features = ["min_const_gen"] }
51 serde = { version = "^1", features = ["derive", ] }
52 serde_json = "^1"
53+ stderrlog = "^0.5"
54 tempfile = { version = "^3.5" }
55 tokio = { version = "1", features = ["full"] }
56 tower-http = { version = "^0.3" }
57 diff --git a/web/src/main.rs b/web/src/main.rs
58index 231ac2c..df233ad 100644
59--- a/web/src/main.rs
60+++ b/web/src/main.rs
61 @@ -20,7 +20,7 @@
62 use std::{collections::HashMap, sync::Arc};
63
64 use chrono::TimeZone;
65- use mailpot::{Configuration, Connection};
66+ use mailpot::{log, Configuration, Connection};
67 use mailpot_web::*;
68 use minijinja::value::Value;
69 use rand::Rng;
70 @@ -156,6 +156,17 @@ async fn main() {
71
72 return;
73 }
74+ #[cfg(test)]
75+ let verbosity = log::LevelFilter::Trace;
76+ #[cfg(not(test))]
77+ let verbosity = log::LevelFilter::Info;
78+ stderrlog::new()
79+ .quiet(false)
80+ .verbosity(verbosity)
81+ .show_module_names(true)
82+ .timestamp(stderrlog::Timestamp::Millisecond)
83+ .init()
84+ .unwrap();
85 let conf = Configuration::from_file(config_path).unwrap();
86 let app = create_app(new_state(conf));
87
88 diff --git a/web/src/minijinja_utils.rs b/web/src/minijinja_utils.rs
89index b7b0c04..ce77da1 100644
90--- a/web/src/minijinja_utils.rs
91+++ b/web/src/minijinja_utils.rs
92 @@ -19,6 +19,8 @@
93
94 //! Utils for templates with the [`minijinja`] crate.
95
96+ use std::fmt::Write;
97+
98 use super::*;
99
100 mod compressed;
101 @@ -150,6 +152,20 @@ impl Object for MailingList {
102 "unsubscription_mailto" => Ok(Value::from_serializable(
103 &self.inner.unsubscription_mailto(),
104 )),
105+ "topics" => {
106+ let mut ul = String::new();
107+ write!(&mut ul, r#"<ul class="tags inline">"#)?;
108+ for topic in &self.topics {
109+ write!(
110+ &mut ul,
111+ r#"<li class="tag" style="--red:110;--green:120;--blue:180;"><span class="tag-name">"#
112+ )?;
113+ write!(&mut ul, "{}", topic)?;
114+ write!(&mut ul, r#"</span></li>"#)?;
115+ }
116+ write!(&mut ul, r#"</ul>"#)?;
117+ Ok(Value::from_safe_string(ul))
118+ }
119 _ => Err(Error::new(
120 minijinja::ErrorKind::UnknownMethod,
121 format!("object has no method named {name}"),
122 diff --git a/web/src/templates/css.html b/web/src/templates/css.html
123index c389ad1..2029514 100644
124--- a/web/src/templates/css.html
125+++ b/web/src/templates/css.html
126 @@ -161,6 +161,7 @@
127 --code-foreground: #124;
128 --code-background: #8fbcbb;
129 --a-visited-text: var(--a-normal-text);
130+ --tag-border-color: black;
131 }
132
133 @media (prefers-color-scheme: light) {
134 @@ -237,6 +238,7 @@
135 --a-hover-bg: #bfbfbf40;
136 --a-active-text: #c00;
137 --a-active-underline: #c00;
138+ --tag-border-color: #0000005e;
139 color-scheme: light;
140 }
141 }
142 @@ -316,6 +318,7 @@
143 --a-hover-bg: #bfbfbf40;
144 --a-active-text: #c00;
145 --a-active-underline: #c00;
146+ --tag-border-color: #000;
147
148 color-scheme: dark;
149 }
150 @@ -995,4 +998,49 @@
151 height:1px;
152 overflow:hidden;
153 }
154+
155+ <
156+ ul.tags.inline {
157+ display: contents;
158+ }
159+
160+ ul.tags {
161+ list-style: none;
162+ margin: 0;
163+ padding: 0;
164+ height: max-content;
165+ vertical-align: baseline;
166+ display: inline-flex;
167+ gap: 0.8ex;
168+ flex-flow: row wrap;
169+ }
170+
171+ .tag {
172+ --padding-top-bottom: 1px;
173+ --padding-left-right: 5.4px;
174+
175+ display: inline-block;
176+ border: 1px solid var(--tag-border-color);
177+ border-radius:.2rem;
178+ color: #555;
179+ font-size: .9rem;
180+ padding: 0px 0.4em 1px 0.4em;
181+ padding: var(--padding-top-bottom) var(--padding-left-right);
182+ text-decoration: none;
183+
184+ --aa-brightness: ((var(--red) * 299) + (var(--green) * 587) + (var(--blue) * 114)) / 1000;
185+ --aa-color: calc((var(--aa-brightness) - 128) * -1000);
186+ background: rgb(var(--red), var(--green), var(--blue));
187+ color: rgb(var(--aa-color), var(--aa-color), var(--aa-color));
188+ min-width: max-content;
189+ max-height: calc(1.5cap + var(--padding-top-bottom));
190+ min-height: calc(1.5cap + var(--padding-top-bottom));
191+ }
192+
193+
194+
195+ span.tag-name a {
196+ text-decoration: none;
197+ color: inherit;
198+ }
199 </style>
200 diff --git a/web/src/templates/lists.html b/web/src/templates/lists.html
201index b9582bc..e1f8c45 100644
202--- a/web/src/templates/lists.html
203+++ b/web/src/templates/lists.html
204 @@ -5,7 +5,7 @@
205 <dl class="lists" aria-label="list of mailing lists">
206 {% for l in lists %}
207 <dt aria-label="mailing list name"><a href="{{ list_path(l.list.id) }}">{{ l.list.name }}</a></dt>
208- <dd><span aria-label="mailing list description"{% if not l.list.description %} class="no-description"{% endif %}>{{ l.list.description if l.list.description else "no description" }}</span> | {{ l.posts|length }} post{{ l.posts|length|pluralize("","s") }}{% if l.newest %} | <time datetime="{{ l.newest }}">{{ l.newest }}</time>{% endif %}</dd>
209+ <dd><span aria-label="mailing list description"{% if not l.list.description %} class="no-description"{% endif %}>{{ l.list.description if l.list.description else "no description" }}</span> | {{ l.posts|length }} post{{ l.posts|length|pluralize("","s") }}{% if l.newest %} | <time datetime="{{ l.newest }}">{{ l.newest }}</time>{% endif %}{% if l.list.topics|length > 0 %}<br aria-hidden="true"><br aria-hidden="true"><span><em>Topics</em>:</span>&nbsp;{{ l.list.topics() }}{% endif %}</dd>
210 {% endfor %}
211 </dl>
212 </div>
213 diff --git a/web/src/templates/lists/list.html b/web/src/templates/lists/list.html
214index dcd335d..4844855 100644
215--- a/web/src/templates/lists/list.html
216+++ b/web/src/templates/lists/list.html
217 @@ -1,5 +1,9 @@
218 {% include "header.html" %}
219 <div class="body">
220+ {% if list.topics|length > 0 %}<span><em>Topics</em>:</span>&nbsp;{{ list.topics() }}
221+ <br aria-hidden="true">
222+ <br aria-hidden="true">
223+ {% endif %}
224 {% if list.description %}
225 <p title="mailing list description">List description: {{ list.description }}</p>
226 {% else %}