Skip to content

Website: Merge Events + Updates into a News section, add Buttondown newsletters [ENG-2148] - #1306

Draft
jsmorabito wants to merge 9 commits into
mainfrom
feature/news-section-buttondown
Draft

Website: Merge Events + Updates into a News section, add Buttondown newsletters [ENG-2148]#1306
jsmorabito wants to merge 9 commits into
mainfrom
feature/news-section-buttondown

Conversation

@jsmorabito

@jsmorabito jsmorabito commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Renamed the homepage "Events" section to "News". There was also a separate "Updates" (blog) section already in the code, but it was gated behind {blogs.length > 0 && (...)} and there are currently zero published posts in content/blog/ (only an unpublished EXAMPLE.mdx template), so it never actually rendered on the live site — this PR removes that hidden/dead code path and folds its data source (published blog posts, whenever they exist) into the new News feed instead.
  • Added a live integration with Buttondown's API to pull newsletter issues into that feed alongside static events and (once any exist) blog posts.
  • Newsletter items are filtered to sent, publicly archived emails whose subject contains "Newsletter" (matches the existing "DG Newsletter: " naming convention), with full pagination so historical issues backfill correctly.
  • Nav updated: the old "Events"/"Updates" links are now a single "News" link (/#news). ("Updates" was already conditionally hidden from nav too, via the same hasUpdates check, so this isn't a visible nav change today.)

Why

Consolidates three content sources (static events, blog posts, newsletter issues) into one chronological News list on the homepage, so newsletter issues sent via Buttondown show up automatically instead of needing a manual homepage edit each time — and so blog posts get a working home to render into once any are published.

How it works

  • apps/website/app/utils/buttondown.ts — fetches GET /v1/emails?status=sent&subject=Newsletter from Buttondown, paginating through all next pages, and keeps only emails with archival_mode === "enabled" (fully public in the archive).
  • apps/website/app/types/news.ts / apps/website/app/utils/formatDate.ts — shared NewsItem shape and date formatting used to merge static events, blog posts, and newsletter items into one sorted list.
  • apps/website/app/(home)/page.tsx / layout.tsx — the merged section and nav link.

Config needed to go live

  • BUTTONDOWN_API_KEY (required) — without it, the newsletter fetch is skipped silently and the News section just shows static events (and any published blog posts). Generate this in the Buttondown dashboard under Settings → Programming (API), then add it as an env var in Vercel (production) and apps/website/.env.local (local dev). I don't have Vercel access, so someone with access needs to add this for it to go live.
  • BUTTONDOWN_NEWSLETTER_SUBJECT_FILTER (optional) — override the "Newsletter" subject-match string if the naming convention ever changes; set to an empty string to disable the filter and show all public sent emails.

Next steps

  1. Dev lead / someone with Vercel access adds BUTTONDOWN_API_KEY to the production environment (and ideally .env.local for local dev).
  2. Sanity-check in Buttondown that all newsletter issues you want listed have "Newsletter" somewhere in the subject line — anything that doesn't will be silently excluded.
  3. Once the key is added, verify the deployed News section shows the expected newsletter issues, in the right order, linking to the correct Buttondown archive pages.
  4. Decide if/when to prune older static STATIC_NEWS_ITEMS entries in page.tsx now that real events can be added the same way, or if that array should stay hand-maintained for talks/panels that aren't Buttondown emails.
  5. Separately: no blog posts are currently published, so if the team wants the "posts" side of News to show anything, someone needs to publish one (set published: true in its frontmatter in content/blog/).

Test plan

  • pnpm check-types and pnpm lint show no new errors (confirmed pre-existing issues are unrelated/present on main too)
  • Verified locally via dev server that the merged News section renders static events correctly, sorted by date, with no console/server errors (blog posts weren't testable here since none are currently published)
  • Verify live Buttondown newsletter fetch once BUTTONDOWN_API_KEY is added to an environment

…newsletters

Renames the homepage "Events" section to "News" and merges in blog posts and
Buttondown newsletter issues, sorted by date. Newsletters are fetched live
from Buttondown's API, filtered to sent + publicly archived emails whose
subject contains "Newsletter" (configurable via
BUTTONDOWN_NEWSLETTER_SUBJECT_FILTER), and paginate through all results.
Requires a BUTTONDOWN_API_KEY env var to activate; falls back to an empty
list otherwise so the build isn't blocked on it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@supabase

supabase Bot commented Aug 18, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project zytfjzqyijgagqxrzbmz because there are no changes detected in packages/database/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
discourse-graph Ready Ready Preview Aug 20, 2026 1:39pm

Request Review

@jsmorabito jsmorabito changed the title Website: Merge Events + Updates into a News section, add Buttondown newsletters Website: Merge Events + Updates into a News section, add Buttondown newsletters [ENG-2148] Aug 18, 2026
@linear-code

linear-code Bot commented Aug 18, 2026

Copy link
Copy Markdown

ENG-2148

jsmorabito and others added 8 commits August 20, 2026 09:28
new Date() parses date-only ISO strings as UTC midnight, but
toLocaleDateString with no timeZone option formats in the host's local
zone — shifting the displayed date back a day on any server running
behind UTC. Pin formatting to UTC so the displayed date matches the
source value regardless of server timezone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Previously, a failed pagination request logged an error and broke out
of the loop, returning whatever pages had already been fetched as if
that were the complete newsletter history — silently showing an
incomplete backfill as correct. Throwing lets the existing outer
catch discard the partial result and fall back to an empty list,
consistent with how a missing API key is already handled.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The merged News section previously rendered every static event, every
published blog post, and every matching Buttondown newsletter with no
limit — unlike the old Updates section, which capped at 3 recent
posts. Left unbounded, this list only grows over time. Cap it to the
10 most recent items; the full blog archive remains reachable via
"See all posts".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The removed Updates section showed "By {author}" for each post; the
merged News list dropped that field entirely. Fold it into the meta
line, consistent with the "date | label" pattern used by events and
newsletters.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
sortNewsByDateDesc in page.tsx was a byte-for-byte copy of
sortBlogsByDate in readBlogs.tsx. Extract sortByDateDesc into a shared
generic utility and use it in both places.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… both

Each STATIC_NEWS_ITEMS entry independently hand-wrote both a sortable
date and a prose meta string repeating that date, with nothing to
keep them in sync. Restructure as event sources with a date + location
(and an optional dateLabel override for the one multi-day event), and
derive the display string via formatDisplayDate — matching the
approach already used for blog posts and newsletters.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Buttondown's page-number pagination isn't guaranteed stable if an
email is created mid-fetch, so the same newsletter could theoretically
appear twice in newsletterItems. Since href is used as the React key
for the news list, dedupe by href before sorting/rendering to avoid
duplicate-key warnings and undefined reconciliation behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Previously the "Events"/"Updates" nav items and section were only
shown when there was content; this PR's merge into "News" dropped that
guard entirely, always rendering the nav link and section even if
there were nothing to show. Move the static items into a shared
app/data/news.ts module so layout.tsx can cheaply check for them
without duplicating page.tsx's async blog/newsletter fetches, and
guard the section itself with the already-computed `news.length > 0`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@jsmorabito

Copy link
Copy Markdown
Collaborator Author

Ran a code review over this diff and fixed everything it surfaced, each as its own commit:

  1. 9d873d57 — fixed an off-by-one-day bug in formatDisplayDate on servers running behind UTC (date-only strings parsed as UTC but formatted in local time).
  2. 06a35c7e — a failed Buttondown pagination request now discards the fetch and falls back to empty, instead of silently returning a truncated list as if it were the complete history.
  3. 93f43a43 — capped the merged News list to the 10 most recent items instead of rendering it unbounded (previously the "Updates" section capped at 3; the merge dropped that).
  4. 31e427f6 — restored blog post author attribution ("By {author}"), which the merge into News had dropped from the homepage summary list.
  5. c4cc98b6 — extracted a shared sortByDateDesc instead of duplicating the same comparator that already existed in readBlogs.tsx.
  6. 659036c0 — static event items now derive their display date from the sortable date field instead of hand-authoring both independently (which could drift out of sync).
  7. 7bd974f2 — dedupe merged news items by href before rendering, since Buttondown's pagination isn't guaranteed stable under concurrent sends and a duplicate would collide as a React key.
  8. 03770392 — the News nav link and section are now guarded against being empty, restored to the same pattern the old conditional "Updates" link/section used.

All commits pass pnpm check-types and pnpm lint with no new errors/warnings beyond what's already on main.

@jsmorabito

Copy link
Copy Markdown
Collaborator Author

@mdroidian can you take a look at this and let me know if I did things correctly? this update is part of several that will be consolidating info on the site and making it easier to navigate

@mdroidian

Copy link
Copy Markdown
Member

@mdroidian can you take a look at this and let me know if I did things correctly? this update is part of several that will be consolidating info on the site and making it easier to navigate

Did you test this locally with the API key? Was it tested it with blog posts?
It looks like pagination was added too, was that tested?

I'm slightly worried about the fact that we are calling a third party API on every page load of our homepage, but a quick check seems like it is using NextJS ISR correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants