From 3c1f1f40e0312361947f361ba6ce168c1da47686 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 4 May 2026 22:07:50 +0000 Subject: [PATCH 1/7] fix: Substack essays always fetch via public API, add iframe fallback + improved essay cards - Remove API key gate: public Substack /api/v1/posts works without auth, so skip the substackApiKey check that was blocking all CI fetches - Pass substackHandle through to component for use in fallback - Iframe embed fallback: when API returns no essays (e.g. blocked fetch), render the Substack embed widget instead of an empty list - Essay cards: cover image thumbnail, inline Substack source badge, flex layout so image+text sit side by side - Add netlify.toml so Netlify deploy previews trigger on PRs https://claude.ai/code/session_01P9Bn6gkWtM15yEzGf79XCQ --- netlify.toml | 14 ++++ pages/index.tsx | 167 +++++++++++++++++++++++++++++------------------- 2 files changed, 117 insertions(+), 64 deletions(-) create mode 100644 netlify.toml diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 0000000..6d5bc60 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,14 @@ +[build] + command = "npm run build && npm run export" + publish = "out" + +[build.environment] + NODE_VERSION = "22" + NODE_ENV = "production" + +# Deploy previews for all PRs +[context.deploy-preview] + command = "npm run build && npm run export" + +[context.branch-deploy] + command = "npm run build && npm run export" diff --git a/pages/index.tsx b/pages/index.tsx index cfad325..59c4d9b 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -22,8 +22,6 @@ import { layoutDefaultClasses, StyleClasses, } from "components/styles"; -import { log } from "lib/log"; - const classes: StyleClasses = { ...globalClasses, ...layoutDefaultClasses, @@ -36,18 +34,16 @@ export const getStaticProps = async () => { const channels = await resolveArenaChannels(); const siteMap = await getSiteMap(); - // Fetch Substack essays - use environment variables for Substack handle and API key + // Fetch Substack essays - public API works without a key; key is optional enhancement const substackHandle = process.env.SUBSTACK_HANDLE || "suruleredotdev"; const substackApiKey = process.env.SUBSTACK_API_KEY; let substackEssays: SubstackEssay[] = []; - if (substackHandle && substackApiKey) { + if (substackHandle) { substackEssays = await getSubstackEssays( substackHandle, 10, substackApiKey ); - } else { - log("ERROR", "Missing Substack handle or API key") } const props = { @@ -55,6 +51,7 @@ export const getStaticProps = async () => { channels, siteMap, substackEssays, + substackHandle, }; return { props, revalidate: 3600 }; // Revalidate every hour for fresh Substack content @@ -82,6 +79,7 @@ const textVersion = 0; interface HomePageContentProps extends types.PageProps { substackEssays?: SubstackEssay[]; + substackHandle?: string; } export const HomePageContent: React.FC = ({ @@ -91,6 +89,7 @@ export const HomePageContent: React.FC = ({ channels, siteMap, substackEssays = [], + substackHandle = "suruleredotdev", }) => { // TODO: render from root page block const posts = getSitePosts({ @@ -116,74 +115,111 @@ export const HomePageContent: React.FC = ({ ESSAYS - {/* {JSON.stringify(posts.slice(0, 3), null, 2)} */} -
    - {/* Combine and sort local posts with Substack essays by publication date */} - {[ + {(() => { + const allItems = [ ...posts ?.filter((post) => post.public == true) - .map((post) => ({ ...post, isExternal: false })), + .map((post) => ({ ...post, isExternal: false, image: undefined as string | undefined })), ...substackEssays.map((essay) => ({ ...essay, - title: essay.title, - description: essay.description, - published: essay.published, isExternal: true, id: `substack-${essay.id}`, })), - ] - ?.sort((a, b) => b.published - a.published) - .map((item: any, i) => ( -

    - - {item.title} - {item.isExternal && } - + ].sort((a, b) => b.published - a.published); - -   —{" "} - {new Date(item.published).toLocaleDateString("en-US", { - year: "numeric", - month: "long", - day: "numeric", - })} - + if (allItems.length === 0) { + // Fallback: Substack embed iframe when no essay data is available + return ( +

    +