| 44 |
44 |
|
//! 2026-08-21: a save dialog asks the host where to put something, and nothing
|
| 45 |
45 |
|
//! named that. The ruling was that the control does not ask — the route answers
|
| 46 |
46 |
|
//! with the file and the host decides where it lands — so all three exports are
|
| 47 |
|
- |
//! described now too. This sidebar still does not offer the section, and that is
|
| 48 |
|
- |
//! now only about addresses: what is described lives at `/data` rather than
|
| 49 |
|
- |
//! under this screen's.
|
|
47 |
+ |
//! described now too. The sidebar offers the section as of 2026-08-22, as a row
|
|
48 |
+ |
//! that navigates to `/data` rather than a pane this screen draws; see
|
|
49 |
+ |
//! [`Section::at`] for what that costs and what the alternative was.
|
| 50 |
50 |
|
//!
|
| 51 |
51 |
|
//! Half of that is the app's own doing and is fixed here: the theme list also
|
| 52 |
52 |
|
//! needed an `AppHandle`, because the search path is built from the resource and
|
| 103 |
103 |
|
slug: &'static str,
|
| 104 |
104 |
|
/// What the section is called.
|
| 105 |
105 |
|
title: &'static str,
|
|
106 |
+ |
/// Where the row goes, when that is not a section of this screen.
|
|
107 |
+ |
///
|
|
108 |
+ |
/// `None` for the ordinary case, which is `/settings/{slug}` and a pane
|
|
109 |
+ |
/// this module draws.
|
|
110 |
+ |
///
|
|
111 |
+ |
/// `Some` is a row that navigates away, and the shipped sidebar's Import &
|
|
112 |
+ |
/// Export is the one. That section is [`super::data`], a whole screen at
|
|
113 |
+ |
/// `/data`, described on 2026-08-16 and finished on 2026-08-21 when
|
|
114 |
+ |
/// `Outcome::File` made the exports sayable. Two shapes were on the table
|
|
115 |
+ |
/// and this is the cheaper by a wide margin: the other is serving `/data`
|
|
116 |
+ |
/// under `/settings/data`, which moves a finished screen for the sake of
|
|
117 |
+ |
/// the sidebar's shape.
|
|
118 |
+ |
///
|
|
119 |
+ |
/// What it costs, stated rather than discovered: the sidebar belongs to
|
|
120 |
+ |
/// this screen, so following such a row leaves it behind and `/data` draws
|
|
121 |
+ |
/// its own band and no nav. The alternative was a sidebar with a section
|
|
122 |
+ |
/// missing, which is what stood here until 2026-08-22.
|
|
123 |
+ |
///
|
|
124 |
+ |
/// Two consequences hold wherever this is read. A row that leaves is never
|
|
125 |
+ |
/// [`Row::current`], because this screen is not showing it. And
|
|
126 |
+ |
/// [`section_of`] does not answer for one, so `/settings/data` is a 404
|
|
127 |
+ |
/// rather than Appearance drawn under somebody else's name.
|
|
128 |
+ |
at: Option<&'static str>,
|
| 106 |
129 |
|
}
|
| 107 |
130 |
|
|
| 108 |
131 |
|
/// The sections that are described, in the sidebar's order.
|
| 111 |
134 |
|
/// absent rather than disabled, for the reason the task overview left Edit out:
|
| 112 |
135 |
|
/// a control that is drawn and does nothing is worse than a control that is not
|
| 113 |
136 |
|
/// drawn, and the module header says which and why.
|
| 114 |
|
- |
const SECTIONS: [Section; 4] = [
|
|
137 |
+ |
const SECTIONS: [Section; 5] = [
|
| 115 |
138 |
|
Section {
|
| 116 |
139 |
|
slug: "appearance",
|
| 117 |
140 |
|
title: "Appearance",
|
|
141 |
+ |
at: None,
|
| 118 |
142 |
|
},
|
| 119 |
143 |
|
Section {
|
| 120 |
144 |
|
slug: "notifications",
|
| 121 |
145 |
|
title: "Notifications",
|
|
146 |
+ |
at: None,
|
| 122 |
147 |
|
},
|
| 123 |
148 |
|
Section {
|
| 124 |
149 |
|
slug: "planning",
|
| 125 |
150 |
|
title: "Planning & Review",
|
|
151 |
+ |
at: None,
|
| 126 |
152 |
|
},
|
| 127 |
153 |
|
// Added 2026-08-21. The header above lists Email among the sections that
|
| 128 |
154 |
|
// are "about the host rather than about the app"; that was measured wrong.
|
| 130 |
156 |
|
Section {
|
| 131 |
157 |
|
slug: "email",
|
| 132 |
158 |
|
title: "Email",
|
|
159 |
+ |
at: None,
|
|
160 |
+ |
},
|
|
161 |
+ |
// Added 2026-08-22. Described in full as `super::data` and not a section of
|
|
162 |
+ |
// this screen; see `Section::at`.
|
|
163 |
+ |
Section {
|
|
164 |
+ |
slug: "data",
|
|
165 |
+ |
title: "Import & Export",
|
|
166 |
+ |
at: Some("/data"),
|
| 133 |
167 |
|
},
|
| 134 |
168 |
|
];
|
| 135 |
169 |
|
|
| 392 |
426 |
|
}
|
| 393 |
427 |
|
|
| 394 |
428 |
|
/// The section under this slug, or 404.
|
|
429 |
+ |
///
|
|
430 |
+ |
/// A row that navigates away is in [`SECTIONS`] for the sidebar's sake and is
|
|
431 |
+ |
/// not a section of this screen, so it does not answer here. Without that,
|
|
432 |
+ |
/// `/settings/data` would fall through [`screen`]'s match and draw Appearance
|
|
433 |
+ |
/// under the Import & Export heading.
|
| 395 |
434 |
|
fn section_of(slug: &str) -> Result<&'static Section, RouteError> {
|
| 396 |
435 |
|
SECTIONS
|
| 397 |
436 |
|
.iter()
|
| 398 |
|
- |
.find(|section| section.slug == slug)
|
|
437 |
+ |
.find(|section| section.at.is_none() && section.slug == slug)
|
| 399 |
438 |
|
.ok_or_else(|| RouteError::not_found("no such settings section"))
|
| 400 |
439 |
|
}
|
| 401 |
440 |
|
|
| 424 |
463 |
|
// `.settings-nav-item` buttons and tracks with an `active` class.
|
| 425 |
464 |
|
let nav = Slot::new("settings-nav", RegionKind::Sidebar).with(Node::list(SECTIONS.iter().map(
|
| 426 |
465 |
|
|item| {
|
| 427 |
|
- |
let mut row =
|
| 428 |
|
- |
Row::new(item.title).activate(Action::get(format!("/settings/{}", item.slug)));
|
|
466 |
+ |
let at = item
|
|
467 |
+ |
.at
|
|
468 |
+ |
.map_or_else(|| format!("/settings/{}", item.slug), ToOwned::to_owned);
|
|
469 |
+ |
let mut row = Row::new(item.title).activate(Action::get(at));
|
| 429 |
470 |
|
// `current` and not `selected`: this is the app's own pointer at
|
| 430 |
471 |
|
// what the pane is showing, which is the distinction the 2026-08-08
|
| 431 |
472 |
|
// rename drew, and the first place in the port where the sidebar
|
| 432 |
473 |
|
// half of it is what is wanted. Set on the field because the two
|
| 433 |
474 |
|
// have no paired constructor the way `toggling` pairs the other
|
| 434 |
475 |
|
// two, and inventing one for a plain bool would be noise.
|
| 435 |
|
- |
row.current = item.slug == section.slug;
|
|
476 |
+ |
// A row that leaves is never current; see `Section::at`.
|
|
477 |
+ |
row.current = item.at.is_none() && item.slug == section.slug;
|
| 436 |
478 |
|
row
|
| 437 |
479 |
|
},
|
| 438 |
480 |
|
)));
|