The logo beside the add-feed button, with the version on hover (#60)
The logo sat at the top of the feed list with "iPX" written beside it, and the page showed the version nowhere. It is now in the top bar just before the feed buttons, alone, and its tooltip names the app and its version. The version is filled in by the server as it sends the page, not by build.mjs: build.rs reruns only when web/ or package-lock.json changes, so a release that bumped only Cargo.toml would have kept the page naming the one before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The logo's needle is a brighter, redder orange that stands apart from the blue bars.
|
- The logo's needle is a brighter, redder orange that stands apart from the blue bars.
|
||||||
|
- The logo is in the top bar beside the add-feed button, in place of the name at the top of the
|
||||||
|
feed list. Hovering it shows iPX's version.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
12
src/web.rs
12
src/web.rs
@@ -610,6 +610,9 @@ fn constant_time_eq(a: &str, b: &str) -> bool {
|
|||||||
// quotes, so ADMIN_LINK and HTML_TAG are spelled the way the minifier leaves them.
|
// quotes, so ADMIN_LINK and HTML_TAG are spelled the way the minifier leaves them.
|
||||||
const INDEX: &str = include_str!(concat!(env!("OUT_DIR"), "/index.html"));
|
const INDEX: &str = include_str!(concat!(env!("OUT_DIR"), "/index.html"));
|
||||||
const ADMIN_LINK: &str = "<a id=admin ";
|
const ADMIN_LINK: &str = "<a id=admin ";
|
||||||
|
// The logo's tooltip. Filled in here, not by build.mjs, because build.rs does not rerun when only
|
||||||
|
// Cargo.toml's version changes, and the page would go on naming the last release.
|
||||||
|
const VERSION_SLOT: &str = "iPX {version}";
|
||||||
|
|
||||||
/// The page, with the log button left out for anyone but an admin. Hiding it from the page's
|
/// The page, with the log button left out for anyone but an admin. Hiding it from the page's
|
||||||
/// script instead showed it for a moment on every load, until /api/me answered.
|
/// script instead showed it for a moment on every load, until /api/me answered.
|
||||||
@@ -624,7 +627,7 @@ const HTML_TAG: &str = "<html lang=en>";
|
|||||||
/// are an admin. Not hidden for everyone else but left out: hiding it from the page's script
|
/// are an admin. Not hidden for everyone else but left out: hiding it from the page's script
|
||||||
/// showed it for a moment on every load, until /api/me answered (issue #29).
|
/// showed it for a moment on every load, until /api/me answered (issue #29).
|
||||||
fn page_for(admin: bool, theme: (Option<String>, Option<String>)) -> String {
|
fn page_for(admin: bool, theme: (Option<String>, Option<String>)) -> String {
|
||||||
let mut page = with_theme(INDEX, theme);
|
let mut page = with_theme(INDEX, theme).replacen(VERSION_SLOT, concat!("iPX ", env!("CARGO_PKG_VERSION")), 1);
|
||||||
if !admin
|
if !admin
|
||||||
&& let Some(at) = page.find(ADMIN_LINK)
|
&& let Some(at) = page.find(ADMIN_LINK)
|
||||||
&& let Some(len) = page[at..].find("</a>")
|
&& let Some(len) = page[at..].find("</a>")
|
||||||
@@ -1002,6 +1005,13 @@ mod tests {
|
|||||||
assert!(page.contains("id=prefs"), "and only the link: the settings button beside it stays");
|
assert!(page.contains("id=prefs"), "and only the link: the settings button beside it stays");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_logo_names_the_version() {
|
||||||
|
// If the minifier drifts from VERSION_SLOT, replacen matches nothing and says nothing.
|
||||||
|
let page = page_for(false, (None, None));
|
||||||
|
assert!(!page.contains(VERSION_SLOT) && page.contains(concat!("iPX ", env!("CARGO_PKG_VERSION"))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_page_arrives_in_the_theme_the_account_chose() {
|
fn the_page_arrives_in_the_theme_the_account_chose() {
|
||||||
let page = |t: &str, m: &str| page_for(true, (Some(t.into()), Some(m.into())));
|
let page = |t: &str, m: &str| page_for(true, (Some(t.into()), Some(m.into())));
|
||||||
|
|||||||
@@ -493,11 +493,7 @@ a{color:var(--accent)}
|
|||||||
background:var(--panel);border-right:1px solid var(--line);
|
background:var(--panel);border-right:1px solid var(--line);
|
||||||
display:flex;flex-direction:column;min-height:0;
|
display:flex;flex-direction:column;min-height:0;
|
||||||
}
|
}
|
||||||
.brand{display:flex;align-items:center;gap:9px;padding:14px 14px 10px}
|
#applogo{width:28px;height:28px;flex:none;border-radius:7px}
|
||||||
.brand .logo{
|
|
||||||
width:30px;height:30px;flex:none;border-radius:7px;
|
|
||||||
}
|
|
||||||
.brand h1{font-size:16px;margin:0;font-weight:650;letter-spacing:-.01em;color:var(--fg);flex:1}
|
|
||||||
.iconbtn{
|
.iconbtn{
|
||||||
width:30px;height:30px;border-radius:8px;display:grid;place-items:center;
|
width:30px;height:30px;border-radius:8px;display:grid;place-items:center;
|
||||||
color:var(--dim);flex:none;
|
color:var(--dim);flex:none;
|
||||||
@@ -548,7 +544,7 @@ a{color:var(--accent)}
|
|||||||
.who button:hover{color:var(--fg)}
|
.who button:hover{color:var(--fg)}
|
||||||
.sidefoot button{display:inline-flex;align-items:center;justify-content:center;gap:6px}
|
.sidefoot button{display:inline-flex;align-items:center;justify-content:center;gap:6px}
|
||||||
.sidefoot i{font-style:normal;opacity:.75}
|
.sidefoot i{font-style:normal;opacity:.75}
|
||||||
.searchwrap{padding:0 12px 8px}
|
.searchwrap{padding:12px 12px 8px}
|
||||||
input[type=search],input[type=text],input[type=password],input[type=number],select{
|
input[type=search],input[type=text],input[type=password],input[type=number],select{
|
||||||
width:100%;background:var(--bg);border:1px solid var(--line);color:var(--fg);
|
width:100%;background:var(--bg);border:1px solid var(--line);color:var(--fg);
|
||||||
border-radius:8px;padding:7px 10px;font:inherit;font-size:13.5px;
|
border-radius:8px;padding:7px 10px;font:inherit;font-size:13.5px;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<button class="iconbtn" id="burger" title="Feeds" aria-label="Feeds" data-icon="menu"></button>
|
<button class="iconbtn" id="burger" title="Feeds" aria-label="Feeds" data-icon="menu"></button>
|
||||||
<!-- One group per thing acted on, in the order the panes read: feeds, then the selected item.
|
<!-- One group per thing acted on, in the order the panes read: feeds, then the selected item.
|
||||||
A phone hides the item group, which it has no table for. -->
|
A phone hides the item group, which it has no table for. -->
|
||||||
|
<img class="logo" id="applogo" src="/logo.svg" alt="iPX" title="iPX {version}">
|
||||||
<div class="tgroup">
|
<div class="tgroup">
|
||||||
<button id="addFeed" title="Add a feed" aria-label="Add a feed" data-icon="plus"></button>
|
<button id="addFeed" title="Add a feed" aria-label="Add a feed" data-icon="plus"></button>
|
||||||
<button id="tbRemove" title="Unsubscribe from this feed" aria-label="Unsubscribe from this feed" data-icon="circleMinus" disabled></button>
|
<button id="tbRemove" title="Unsubscribe from this feed" aria-label="Unsubscribe from this feed" data-icon="circleMinus" disabled></button>
|
||||||
@@ -33,9 +34,6 @@
|
|||||||
</header>
|
</header>
|
||||||
<div id="shell">
|
<div id="shell">
|
||||||
<aside id="sidebar">
|
<aside id="sidebar">
|
||||||
<div class="brand">
|
|
||||||
<img class="logo" src="/logo.svg" alt="iPX"><h1>iPX</h1>
|
|
||||||
</div>
|
|
||||||
<div class="searchwrap"><input type="search" id="feedFilter" placeholder="Filter feeds…"></div>
|
<div class="searchwrap"><input type="search" id="feedFilter" placeholder="Filter feeds…"></div>
|
||||||
<div id="feedlist"></div>
|
<div id="feedlist"></div>
|
||||||
<div class="sidefoot">
|
<div class="sidefoot">
|
||||||
|
|||||||
Reference in New Issue
Block a user