diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7e8d3e4..5376e85 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -71,4 +71,3 @@ jobs: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 - diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml new file mode 100644 index 0000000..66f2e64 --- /dev/null +++ b/.github/workflows/pr-preview.yml @@ -0,0 +1,50 @@ +name: PR Preview + +on: + pull_request: + types: [opened, synchronize, reopened, closed] + +permissions: + contents: write + pull-requests: write + +jobs: + preview: + runs-on: ubuntu-latest + env: + NOTION_SECRET: ${{ secrets.NOTION_SECRET }} + ARENA_UID: ${{ secrets.ARENA_UID }} + ARENA_SECRET: ${{ secrets.ARENA_SECRET }} + ARENA_PERSONAL_ACCESS_TOKEN: ${{ secrets.ARENA_PERSONAL_ACCESS_TOKEN }} + SUBSTACK_API_KEY: ${{ secrets.SUBSTACK_API_KEY }} + SUBSTACK_HANDLE: ${{ vars.SUBSTACK_HANDLE }} + NODE_ENV: production + LOG_LEVEL: error + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js 22.9.0 + if: github.event.action != 'closed' + uses: actions/setup-node@v2 + with: + node-version: '22.9.0' + + - name: Install dependencies + if: github.event.action != 'closed' + run: npm install --include=dev + + - name: Build + if: github.event.action != 'closed' + run: npm run build && npm run export + env: + # Set base path to the PR preview subdir so assets resolve correctly + NEXT_PUBLIC_BASE_PATH: /pr-preview/pr-${{ github.event.pull_request.number }} + + - name: Deploy / remove preview + uses: rossjrw/pr-preview-action@v1 + with: + source-dir: ./out + preview-branch: gh-pages + umbrella-dir: pr-preview + action: auto diff --git a/lib/resolve-arena-channels.ts b/lib/resolve-arena-channels.ts index 6cf72c1..f5d1991 100644 --- a/lib/resolve-arena-channels.ts +++ b/lib/resolve-arena-channels.ts @@ -8,7 +8,7 @@ import { log } from "./log"; const ARENA_USER = { slug: "korede-aderele", id: 60392, - token: getEnv("ARENA_PERSONAL_ACCESS_TOKEN"), + token: getEnv("ARENA_PERSONAL_ACCESS_TOKEN", ""), }; export const all_channels: Record = {}; diff --git a/package-lock.json b/package-lock.json index eb70f3f..ef5e113 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,9 +54,9 @@ }, "devDependencies": { "@next/bundle-analyzer": "^12.3.4", - "@types/node": "^17.0.23", + "@types/node": "17.0.45", "@types/node-fetch": "^3.0.3", - "@types/react": "^18.0.15", + "@types/react": "18.3.28", "@types/react-dom": "^18.0.6", "@typescript-eslint/eslint-plugin": "^5.15.0", "@typescript-eslint/parser": "^5.15.0", @@ -70,7 +70,7 @@ "npm-run-all": "^4.1.5", "parcel": "2.6.x", "prettier": "^2.7.1", - "typescript": "^4.9.5", + "typescript": "4.9.5", "yarn": "1.22.x" }, "engines": { @@ -2568,14 +2568,14 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.23", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", - "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { @@ -4408,9 +4408,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/csv2geojson": { diff --git a/package.json b/package.json index a04a6bb..014021b 100644 --- a/package.json +++ b/package.json @@ -86,9 +86,9 @@ }, "devDependencies": { "@next/bundle-analyzer": "^12.3.4", - "@types/node": "^17.0.23", + "@types/node": "17.0.45", "@types/node-fetch": "^3.0.3", - "@types/react": "^18.0.15", + "@types/react": "18.3.28", "@types/react-dom": "^18.0.6", "@typescript-eslint/eslint-plugin": "^5.15.0", "@typescript-eslint/parser": "^5.15.0", @@ -102,7 +102,7 @@ "npm-run-all": "^4.1.5", "parcel": "2.6.x", "prettier": "^2.7.1", - "typescript": "^4.9.5", + "typescript": "4.9.5", "yarn": "1.22.x" }, "resolutions": { diff --git a/pages/_document.tsx b/pages/_document.tsx index fd3642b..842b7ba 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -10,8 +10,8 @@ export default class MyDocument extends Document { - {getEnv("NODE_ENV") === "production" ? ( - + {getEnv("NODE_ENV") === "production" && getEnv("NEXT_PUBLIC_BASE_PATH", "") ? ( + ) : ( <> )} diff --git a/pages/index.tsx b/pages/index.tsx index cfad325..fa9611b 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,110 @@ 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 && } - - - -   —{" "} - {new Date(item.published).toLocaleDateString("en-US", { - year: "numeric", - month: "long", - day: "numeric", - })} - + ].sort((a, b) => b.published - a.published); -
    + return ( + <> + {allItems.length > 0 && ( +

      + {allItems.map((item: any, i) => { + const href = item.isExternal ? item.url : "/" + idToPagePath[item.id]; + const dateStr = new Date(item.published).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }); - - {item.description?.length > 200 - ? item.description?.substring(0, 197) + "..." - : item.description} - - {item.tags && !item.isExternal ? ( - <> - {/* TODO: implement tags -
      - {item.tags?.map((tag, i) => ( - - {tag} - - ))} */} - - ) : ( - <> - )} -

      - ))} -
    + return ( +
  • + + {item.image && ( + + )} +
    + + {item.title} + {item.isExternal && } + + + — {dateStr} + + {item.isExternal && ( + + Substack + + )} +
    + + {item.description?.length > 200 + ? item.description?.substring(0, 197) + "..." + : item.description} + +
    +
    +
  • + ); + })} +
+ )} + {substackEssays.length === 0 && ( +