| 27 |
27 |
|
//! will typically look like, and therefore what the blocking-pool question is
|
| 28 |
28 |
|
//! actually about.
|
| 29 |
29 |
|
//!
|
| 30 |
|
- |
//! # Two vocabulary gaps this screen hit
|
|
30 |
+ |
//! # The vocabulary gaps this screen found
|
| 31 |
31 |
|
//!
|
| 32 |
32 |
|
//! Both filed on quasicoherent rather than worked around silently.
|
| 33 |
33 |
|
//!
|
| 34 |
|
- |
//! 1. **A table row cannot carry an action.** `Node::Table`'s rows are
|
| 35 |
|
- |
//! [`Cells`], which offer `activate` and nothing else, so a table whose rows
|
| 36 |
|
- |
//! each have a Remove button cannot be said. `Node::List` can say it, so both
|
| 37 |
|
- |
//! lists here are lists, and the column headers the templates had are gone.
|
| 38 |
|
- |
//! That is a real loss on the tokens list, which has four columns.
|
|
34 |
+ |
//! 1. **A table row could not carry an action**, so both tables here had to be
|
|
35 |
+ |
//! lists and lost their column headers. **Closed** by quasi@`b4e3e21`: a
|
|
36 |
+ |
//! table cell holds controls as well as a string, and both are tables again.
|
| 39 |
37 |
|
//! 2. **No date field.** The token form's expiry was `<input type="date">` and
|
| 40 |
|
- |
//! is now `Text`. See [`add_token_form`].
|
|
38 |
+ |
//! is now `Text`. Still open. See [`add_token_form`].
|
| 41 |
39 |
|
|
| 42 |
40 |
|
use makeover_layout as layout;
|
| 43 |
|
- |
use quasi_router::screen::{Act, Choice, Field, Row};
|
|
41 |
+ |
use quasi_router::screen::{Act, Cell, Cells, Choice, Column, Field};
|
| 44 |
42 |
|
use quasi_router::{Action, Node, RegionKind, Request, Response, RouteError, Slot};
|
| 45 |
43 |
|
use quasi_webview::{Shell, Webview};
|
| 46 |
44 |
|
|
| 163 |
161 |
|
if keys.is_empty() {
|
| 164 |
162 |
|
return Node::empty("No SSH keys registered.");
|
| 165 |
163 |
|
}
|
| 166 |
|
- |
Node::list(keys.iter().map(|key| {
|
| 167 |
|
- |
Row::new(key.fingerprint.clone())
|
| 168 |
|
- |
.secondary(key.label.clone())
|
| 169 |
|
- |
.meta(format!("Added {}", key.added))
|
| 170 |
|
- |
.act(
|
| 171 |
|
- |
Act::new(
|
| 172 |
|
- |
"Remove",
|
| 173 |
|
- |
Action::post(format!("/api/users/me/ssh-keys/{}/delete", key.id)),
|
| 174 |
|
- |
)
|
| 175 |
|
- |
// The template asked with hx-confirm. Said here, a terminal host
|
| 176 |
|
- |
// asks in its own way and no host can forget to ask.
|
| 177 |
|
- |
.confirm("Remove this SSH key?")
|
| 178 |
|
- |
.tone(layout::Tone::Danger),
|
| 179 |
|
- |
)
|
| 180 |
|
- |
}))
|
|
164 |
+ |
Node::Table {
|
|
165 |
+ |
// The template's four columns, in its order, the last one the empty
|
|
166 |
+ |
// header its actions sit under.
|
|
167 |
+ |
columns: vec![
|
|
168 |
+ |
Column::new("Fingerprint")
|
|
169 |
+ |
.width(layout::Width::Fill)
|
|
170 |
+ |
.priority(layout::Priority::Essential),
|
|
171 |
+ |
Column::new("Label").width(layout::Width::Content),
|
|
172 |
+ |
Column::new("Added")
|
|
173 |
+ |
.width(layout::Width::Content)
|
|
174 |
+ |
.priority(layout::Priority::Optional),
|
|
175 |
+ |
Column::new("")
|
|
176 |
+ |
.width(layout::Width::Content)
|
|
177 |
+ |
.priority(layout::Priority::Essential),
|
|
178 |
+ |
],
|
|
179 |
+ |
rows: keys
|
|
180 |
+ |
.iter()
|
|
181 |
+ |
.map(|key| {
|
|
182 |
+ |
Cells::new([
|
|
183 |
+ |
Cell::new(key.fingerprint.clone()),
|
|
184 |
+ |
Cell::new(key.label.clone()),
|
|
185 |
+ |
Cell::new(format!("Added {}", key.added)),
|
|
186 |
+ |
Cell::acts([Act::new(
|
|
187 |
+ |
"Remove",
|
|
188 |
+ |
Action::post(format!("/api/users/me/ssh-keys/{}/delete", key.id)),
|
|
189 |
+ |
)
|
|
190 |
+ |
// The template asked with hx-confirm. Said here, a terminal
|
|
191 |
+ |
// host asks in its own way and no host can forget to ask.
|
|
192 |
+ |
.confirm("Remove this SSH key?")
|
|
193 |
+ |
.tone(layout::Tone::Danger)]),
|
|
194 |
+ |
])
|
|
195 |
+ |
})
|
|
196 |
+ |
.collect(),
|
|
197 |
+ |
}
|
| 181 |
198 |
|
}
|
| 182 |
199 |
|
|
| 183 |
200 |
|
/// The add-a-key form.
|
| 221 |
238 |
|
if tokens.is_empty() {
|
| 222 |
239 |
|
return Node::empty("No access tokens.");
|
| 223 |
240 |
|
}
|
| 224 |
|
- |
Node::list(tokens.iter().map(|token| {
|
| 225 |
|
- |
Row::new(token.name.clone())
|
| 226 |
|
- |
.secondary(token.scope)
|
| 227 |
|
- |
.meta(format!(
|
| 228 |
|
- |
"Expires {} - last used {}",
|
| 229 |
|
- |
token.expires, token.last_used
|
| 230 |
|
- |
))
|
| 231 |
|
- |
.act(
|
| 232 |
|
- |
Act::new(
|
| 233 |
|
- |
"Revoke",
|
| 234 |
|
- |
Action::post(format!("/api/users/me/git-tokens/{}/delete", token.id)),
|
| 235 |
|
- |
)
|
| 236 |
|
- |
.confirm("Revoke this token?")
|
| 237 |
|
- |
.tone(layout::Tone::Danger),
|
| 238 |
|
- |
)
|
| 239 |
|
- |
}))
|
|
241 |
+ |
Node::Table {
|
|
242 |
+ |
// The four independent facts the list version had to run together into
|
|
243 |
+ |
// one `meta` string, back in their own columns. This is the table that
|
|
244 |
+ |
// lost the most by being a list: name, scope, expiry and last use are
|
|
245 |
+ |
// read down the column, which is what a table is for.
|
|
246 |
+ |
columns: vec![
|
|
247 |
+ |
Column::new("Name")
|
|
248 |
+ |
.width(layout::Width::Fill)
|
|
249 |
+ |
.priority(layout::Priority::Essential),
|
|
250 |
+ |
Column::new("Scope").width(layout::Width::Content),
|
|
251 |
+ |
Column::new("Expires")
|
|
252 |
+ |
.width(layout::Width::Content)
|
|
253 |
+ |
.priority(layout::Priority::Optional),
|
|
254 |
+ |
Column::new("Last used")
|
|
255 |
+ |
.width(layout::Width::Content)
|
|
256 |
+ |
.priority(layout::Priority::Optional),
|
|
257 |
+ |
Column::new("")
|
|
258 |
+ |
.width(layout::Width::Content)
|
|
259 |
+ |
.priority(layout::Priority::Essential),
|
|
260 |
+ |
],
|
|
261 |
+ |
rows: tokens
|
|
262 |
+ |
.iter()
|
|
263 |
+ |
.map(|token| {
|
|
264 |
+ |
Cells::new([
|
|
265 |
+ |
Cell::new(token.name.clone()),
|
|
266 |
+ |
Cell::new(token.scope),
|
|
267 |
+ |
Cell::new(token.expires.clone()),
|
|
268 |
+ |
Cell::new(token.last_used.clone()),
|
|
269 |
+ |
Cell::acts([Act::new(
|
|
270 |
+ |
"Revoke",
|
|
271 |
+ |
Action::post(format!("/api/users/me/git-tokens/{}/delete", token.id)),
|
|
272 |
+ |
)
|
|
273 |
+ |
.confirm("Revoke this token?")
|
|
274 |
+ |
.tone(layout::Tone::Danger)]),
|
|
275 |
+ |
])
|
|
276 |
+ |
})
|
|
277 |
+ |
.collect(),
|
|
278 |
+ |
}
|
| 240 |
279 |
|
}
|
| 241 |
280 |
|
|
| 242 |
281 |
|
/// The mint-a-token form.
|