Remove unused minus icon; update TODO

Audited the ICON set for consistency: minus was defined but never
referenced anywhere (circleMinus already covers Unsubscribe).
Everything else checked out.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Tk3nAVF6n4dtjQS17FRFr
This commit is contained in:
2026-09-14 20:18:51 +00:00
parent a30edff248
commit e38e3c563c
3 changed files with 41 additions and 46 deletions

82
TODO.md
View File

@@ -2,56 +2,48 @@
## Errors in the log
From the production log and the feeds' stored errors on 2026-09-13. The Docker log only reached back
to 12:32 UTC, so the list comes from `feeds.last_error`: 57 of 1,059 feeds. None of it is ipx's
User-Agent; a browser gets the same answers.
- [x] **Stop scanning an OPML's feeds once nobody subscribes to it.** 55 of the 57 are feeds from
`davewiner` (lists.opml.org/davefeeds.xml). The list left `config.toml` about 14 hours before this
was written, but its 922 feeds are still in the database and still scanned every hour, with
auto-download on: `subscriptions()` adds every derived feed, and with no parent to copy from,
`auto_download` falls back to `true` (`src/main.rs:937`). `docs/users.md` says a feed stops being
scanned when its last subscriber leaves. Skip a derived feed whose parent is not in config, and
when the last subscriber leaves an OPML, treat its feeds the way `sync_group` treats ones the list
dropped: remove those with nothing downloaded, mark the rest orphaned. `remove_feed` and `ipx rm`
both leave them behind today. (`src/main.rs`, `src/web.rs`)
- [x] **Read feeds with a bare `&`.** kcpw has `<link>https://kcpw.org/?post_type=post&p=125715</link>`,
and both feedland feeds have the same fault. Strict XML refuses them; browsers and other readers do
not. When `feed::parse` fails, try once more with every `&` that does not start an entity written
as `&amp;`. Nobody subscribes to these three now, but the next feed like them will fail the same
way. (`src/feed.rs`)
- [x] **Say what came back when it is not a feed.** Thirteen errors read "not RSS (the input did not
begin with an rss tag) and not Atom (...)". Each one checked was a web page: the feed moved and its
old URL redirects to the site, or the domain lapsed. Say "got a web page, not a feed", and when the
page links a feed (`<link rel="alternate" type="application/rss+xml">`), name it. That link found
the new feed for om.co, ms.now, Letters of Note, the Daily Dot, Hell Gate, The Frame Lab and
Daily Kos. A `202` with an empty body (British Antarctic Survey) should read as "nothing yet", not
as a parse failure. (`src/feed.rs`)
- [x] **Show a publisher's error in the UI.** Today a failing feed shows its raw error in red only
once you open it (`web/index.html:877`, `:960`); the OPML view marks a failing child "error"
(`:988`), and the sidebar shows nothing. Mark a failing feed in the sidebar too, and say whose
problem it is and what to do, in plain words: a 404 means the publisher took the feed down or moved
it, 401 and 403 that the site refuses ipx, 402 that the feed needs a paid plan, a name that no
longer resolves that the site is gone, and a web page instead of a feed that it moved (with the new
URL when the page links one). Offer Unsubscribe, or Use the new address. Keep timeouts, 5xx and
429 out of it, and flag a feed only once it has failed for a day: macmanx failed once and read
fine an hour later. That needs the time a feed started failing, kept beside `last_error` and
cleared when a check succeeds. (`web/index.html`, `src/db.rs`, `src/main.rs`)
- [ ] **Westenberg.** The only failing feed anyone subscribes to. The Hacker News stars list still
gives `joanwestenberg.com/rss`, which is a 404; the feed is now `joanwestenberg.com/feed`.
Nothing for ipx to fix; subscribe to the new URL directly.
## Other Fixes and Features
## Icons
- [x] Remember which feed is selected and view (all, unread, flagged, etc) user as selected between visits. If unknown default to All Subscriptions
- [x] When clicking any link it should open in a new tab
- [x] In mobile (iOS) sometimes the top line items like the hamburger menu are not clickable unless you do a hard refresh — likely fixed (100dvh instead of 100vh), unverified on a real device; reopen if it still happens
- [x] video files play as audio files, they should play as video.
- [x] Move Light/Dark/Classic options to user settings. Include an Auto mode that uses system preferences for light/dark modes
- [x] Below Popular, have a currently listening section to show what podcasts have been started and not finnished
- [x] Update subscribe/unsubscribe icons to be circle-minus (unsubscribe) and circle-check (subscribe)
- [x] If I'm on the Unread tab, and I click to read an item the entry in the list will disappear. it should remain until I click to another item.
- [x] **Audit the icon set for consistency.** Checked every glyph in `ICON` (`web/index.html`)
against its source, weight, sizing, and every place it's used — both the `${ICON.x}` template
calls and the `data-icon="x"` lookups the startup loop wires up (`web/index.html:713`). Found one
real problem: `minus`, a bare uncircled minus glyph, was defined but never referenced anywhere;
`circleMinus` already covers the one place a minus-shaped icon is used (Unsubscribe). Removed it.
Everything else checked out: `flag`'s regular weight against `flagOn`'s solid is the standard
outline/filled toggle idiom, not a mismatch; icon sizing is one shared 16px base rule with three
narrowly-scoped overrides; `caret` is deliberately reused (not duplicated) for both the folder
triangle and the sort arrow, rotated correctly per context; play/pause swapping is symmetric.
(`web/index.html`)
## Directory Overhaul
- [ ] Directory needs to be more functional, with categories and a more interesting layout. use /frontend-design to help
- [ ] **Give feeds a real category, from the feed itself.** `ParsedFeed` only carries per-episode
`categories` (`src/feed.rs:24`); nothing at the channel level survives parsing today, so there is
nothing to browse the directory by. Parse `<itunes:category>` (and its `<itunes:subcategory>` if
present) off the channel the same way entry categories already are, keep the first one a feed
reports rather than building a multi-tag facet, and add it to `FeedSummary` and the feeds table
via a migration. Most feeds will not carry one; a feed with none counts only under "All".
(`src/feed.rs`, `src/db.rs`)
- [ ] **Expose category on `/api/popular` and `/api/directory`.** `PopularRow` (`src/web.rs`) is id,
title, image, subscribers, subscribed today; add `category: Option<String>` once a feed carries
one, so the client can group and filter without a second request.
- [ ] **Replace the flat alphabetical list with a category rail over an artwork grid.** Today
Directory is a single-column list of rows: a thumbnail, a title, a subscriber count, a subscribe
button (`renderListed`/`listFeeds`, `web/index.html:1649`). Put a horizontal row of category chips
above it — "All" plus whatever categories actually occur in the catalogue, never an empty one —
and turn the list itself into a grid of square cover-art tiles with the title underneath: podcast
art is designed to be recognized at a glance, the way the app's own iPod-screen styling already
leans on imagery over text. Keep the subscriber count as a small corner badge (the same treatment
`unread` counts already get in the feed list) and move the subscribe button to appear on
hover/focus instead of sitting on every row permanently. Selecting a chip filters the grid in
place; the active chip gets the EQ-amber underline (`--accent2`) the level meter already uses,
rather than a new color. No new palette and no new typeface — reuse `--bg`/`--fg`/`--accent`/
`--accent2` and the existing Lucida Grande stack so Directory still looks like the rest of ipx, not
a bolted-on card kit: no border, shadow or radius beyond what the app already uses elsewhere. On
phone width the chip row scrolls horizontally in place rather than wrapping, so it never pushes
the grid down. Popular can stay a plain top-ten list for now, or get the same tile treatment later
— worth deciding separately rather than folding into this. (`web/index.html`)