Source feeds: click a source to see its publication feed

Click a source name on any card → a feed of just that source's articles,
newest-first, still accepted / non-duplicate / boundary-filtered (the calm
promise isn't bypassed). A natural way to follow a publication's feel.

* queries.feed + /api/feed: source_id filter; Article output gains source_id.
* Frontend: source label is a button → transient 'source:<id>' view (like
  'tag:<slug>'), rendered in the feed grid with Load more, header = source name.
* Ad-hoc, not a pinned lane. Foundation for a future source page (metadata) +
  Follow; shareable /source/<slug> route and source_view analytics come then.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-08 08:30:33 -04:00
parent 50dc2167cd
commit 38889f76e5
5 changed files with 55 additions and 8 deletions
+5
View File
@@ -25,6 +25,7 @@ _ARTICLE_COLUMNS = f"""
a.canonical_url,
a.published_at,
a.image_url,
a.source_id,
src.name AS source_name,
s.topic,
s.flavor,
@@ -57,6 +58,7 @@ def feed(
max_cortisol: int | None = None,
max_ragebait: int | None = None,
tag: str | None = None,
source_id: int | None = None,
sort: str = "ranked",
) -> list[dict]:
"""Return articles with categorical filters applied in SQL.
@@ -106,6 +108,9 @@ def feed(
if tag:
clauses.append("EXISTS (SELECT 1 FROM article_tags at WHERE at.article_id = a.id AND at.tag = ?)")
params.append(tag.lower())
if source_id:
clauses.append("a.source_id = ?")
params.append(source_id)
where = "WHERE " + " AND ".join(clauses)
params.extend([limit, offset])