Commit Graph

10 Commits

Author SHA1 Message Date
Harwinder Sekhon 89d0dcc67c Add BBC News RSS feed (source bbc, BBC red accent) 2026-10-16 15:57:43 -04:00
Harwinder Sekhon 3c2d90fb7e Stream the page shell first so a loader shows while feeds fetch
Opening the page used to block on refreshStale() before a single byte went
out, so the browser sat on a blank tab for the whole fetch. index.php now
renders templates/shell.php (head, header, filters, skeleton loader) and
flushes it, then refreshes and appends templates/content.php, which removes
the loader as the feed HTML arrives.

- templates/layout.php is split into shell.php + content.php
- refresh chip moved to a right-aligned row above the feed: the toolbar
  paints before the refresh report exists
- header story count shows the cached total, then corrects to the real one
- skeleton styles match the card grid and keep the sources' chroma; a
  noscript style hides the loader for clients without JS
- README: file map and page-load lifecycle updated
2026-10-08 15:28:51 -04:00
Harwinder Sekhon 20f4fd7095 Redesign UI color system: neutral ink chrome, sources own the chroma
The light lavender theme carried its color in decoration -- four colored
background blobs, a conic-gradient brand mark, gradient-clipped wordmark,
a four-color animated button, a spinning conic empty mark, and a five-color
spectrum rail. All of that competed with the only color on screen that
carries meaning: which source a story came from.

- One indigo-slate ink ramp (bg / surface / line / ink) carries every neutral
  surface; the only saturated hues are the three sources, used strictly as
  signals -- filter pill, card spine, source chip, timestamp, feed health.
- Rule for every filled state: bright fill, deep ink type. Source tokens kept
  in sync between :root and View::sourceColor().
- Signature "signal bars": a CSS brand mark of three ascending bars in the
  source hues, and the header rail re-cut into three wire segments that double
  as the palette legend. The empty state reuses the mark, unlit and static.
- Bootstrap switched to data-bs-theme="dark" with matching form controls,
  alerts, and focus rings; --ink-faint recalibrated so small text clears
  WCAG 4.5:1 on every surface. Reduced-motion behavior unchanged.
- README documents the two rules next to View::sourceColor().
2026-10-08 11:59:01 -04:00
Harwinder Sekhon aa31507ff5 Fix manage page Dashboard link pointing back to itself
"<- Dashboard" used href="?", which resolves to the current path with an
empty query -- manage.php -- so it reloaded the manage page instead of
returning to the feed. Point it at index.php.
2026-10-08 11:57:09 -04:00
Harwinder Sekhon 397ee1a413 Eliminate Reddit/YouTube 429s with efficient request handling
Reddit allows roughly one request per minute per IP on unauthenticated
.rss, but the app fired six separate subreddit requests, retried 429s
every couple of minutes, and only paced hosts within a single process.
Concurrent page/API/cron requests stampeded providers.

- Combine all due subreddits into one /r/a+b+c/.rss request and attribute
  items back to each subreddit by link; fall back to individual fetches
  when a banned/private subreddit poisons the combined feed.
- Single-flight refresh via data/refresh.lock so concurrent callers serve
  the cache instead of refetching.
- Enforce throttled-host spacing across processes (feeds.hostLastAttempt)
  in addition to the per-refresh cap; add www.youtube.com to throttled hosts.
- Honor 429 Retry-After / x-ratelimit-reset and back off exponentially
  (fail_count doubling, capped at error_retry_cap, plus jitter), reset on
  success. New feeds columns fail_count and next_retry_at.
- Jitter the healthy TTL so feeds don't all expire at once.
- Detect add-feed 404 by status instead of matching the error string.
- Update README (semantics, data model, config) and default user agent.
2026-10-07 20:34:16 -04:00
Harwinder Sekhon a6c27404e5 Add Apache vhost config for deployment (rss-feed.local) 2026-10-05 19:00:33 -04:00
Harwinder Sekhon 41f07b6ad8 Add manual subreddit/YouTube channel configuration (manage.php)
Browser-based feed selection layered on top of config/feeds.php:

- manage.php (password + CSRF gated, session based): add subreddits
  (name / r/name / URL) and YouTube channels (@handle / UC id / URL,
  handle resolved to channel_id once, youtube.com only); enable/disable
  toggles; remove manual feeds; config feeds are hidden, not deleted.
- New feeds are validated with one immediate fetch (bypassing the
  per-refresh host cap): HTTP 404 rejects the entry, 429 keeps it for
  a later refresh. YouTube labels adopt the real channel title.
- feeds table gains origin (config|manual) and enabled columns, with
  a guarded ALTER migration for existing databases.
- Only enabled feeds are refreshed and served: getItems/countItems
  filter by feed_url in enabled feeds, so deselected feeds' cached
  stories are hidden, not deleted; re-adding a URL re-enables it.
- Dashboard header gets a Manage link; manage page styled with the
  existing design tokens; README documents the feature.
2026-10-05 18:58:38 -04:00
Harwinder Sekhon 403fd2c604 Vendor composer.phar so install works on a fresh clone
- Un-ignore composer.phar (keep vendor/ and SQLite gitignored)
- Document install/bootstrap in README
2026-10-05 17:19:30 -04:00
Harwinder Sekhon 60bcf22692 Add developer README with architecture and installation instructions 2026-10-05 17:11:23 -04:00
Harwinder Sekhon ad4dc6b984 Initial commit: RSS feed dashboard aggregating YouTube, Reddit and RT
- PHP 8 + PDO SQLite cache with on-demand TTL refresh and dedup by link hash
- laminas-feed + Guzzle for RSS/Atom parsing and HTTP
- Bootstrap 5 UI with source and time-range filters, AJAX load-more
- User cron-free deployment under Apache/php-fpm; .htaccess hardening
2026-10-05 17:07:48 -04:00