From 775549a3f9bfe9dc8f3b2a50a3004982f06f4e8e Mon Sep 17 00:00:00 2001 From: "M. Scott Ford" Date: Wed, 15 May 2024 12:58:33 -0400 Subject: [PATCH 1/8] Adds page with link to guest pitch form --- src/components/Layout.jsx | 1 + src/pages/pitch.jsx | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/pages/pitch.jsx diff --git a/src/components/Layout.jsx b/src/components/Layout.jsx index f486df6..5e8efa4 100644 --- a/src/components/Layout.jsx +++ b/src/components/Layout.jsx @@ -15,6 +15,7 @@ const navigation = [ { name: 'Home', href: '/' }, { name: 'Community', href: '/community' }, { name: 'About', href: '/about' }, + { name: 'Pitch a Guest', href: '/pitch' }, ] function TinyWaveFormIcon({ colors = [], ...props }) { diff --git a/src/pages/pitch.jsx b/src/pages/pitch.jsx new file mode 100644 index 0000000..f82354b --- /dev/null +++ b/src/pages/pitch.jsx @@ -0,0 +1,36 @@ +import Head from 'next/head' +import { ProseContainer } from '@/components/ProseContainer' +import { getEpisodes } from '@/api/episodes' + +export default function Pitch({episodeCount}) { + return ( + <> + + Codestin Search App + + + +

Know Someone We Should Talk To?

+

+ We're always looking for new guests to have no the show. If you + know who would be a good fit for the show (even if it's you.), + then please fill out our pitch form. +

+
+ + ) +} + +export async function getStaticProps() { + const episodes = await getEpisodes() + return { + props: { + episodeCount: episodes.length + } + } +} From b82656564d4f1215a6c09a997e31fdf14c01e044 Mon Sep 17 00:00:00 2001 From: "M. Scott Ford" Date: Wed, 18 Sep 2024 13:23:55 -0400 Subject: [PATCH 2/8] Update meetup link --- src/pages/community.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/community.jsx b/src/pages/community.jsx index fd06490..5c69528 100644 --- a/src/pages/community.jsx +++ b/src/pages/community.jsx @@ -70,7 +70,7 @@ export default function Community() { katas, hash out project challenges, and more.

- + Join Now From 3c5faf939e6fba0924b7e11b37054c928acbd613 Mon Sep 17 00:00:00 2001 From: "M. Scott Ford" Date: Wed, 18 Sep 2024 13:27:30 -0400 Subject: [PATCH 3/8] Update upload and download GitHub Actions Builds were failing, because these were deprecated. See https://github.blog/changelog/2024-02-13-deprecation-notice-v1-and-v2-of-the-artifact-actions/ --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f465d9f..67f0253 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,13 +34,13 @@ jobs: - run: npm run build - name: Archive site as artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: name: out path: out - name: Archive s3_website.yml as artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4 with: name: s3_website path: s3_website.yml @@ -56,7 +56,7 @@ jobs: name: out - name: Retrieve s3_website.yml from artifacts - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4 with: name: s3_website From 7662802d833db662feaa8f9540385eed389601c1 Mon Sep 17 00:00:00 2001 From: "M. Scott Ford" Date: Wed, 25 Sep 2024 13:08:12 -0400 Subject: [PATCH 4/8] Updates upload action version --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 67f0253..06b4869 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,7 +51,7 @@ jobs: if: contains(github.ref, 'main') steps: - name: Retrieve site from artifacts - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4 with: name: out From 4833b0cbe00f823a7a665e38302758203f34e214 Mon Sep 17 00:00:00 2001 From: "M. Scott Ford" Date: Wed, 25 Sep 2024 13:12:24 -0400 Subject: [PATCH 5/8] Updates path to `s3_website.yml` --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06b4869..da5619b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: - name: Move s3_website.yml into root run: | - mv s3_website/s3_website.yml s3_website.yml + mv s3_website.yml s3_website.yml - name: S3 Publish uses: docker://justinharringa/s3_website:master From a42a10e822d1f459caf474c2ab4ac56d21b747bd Mon Sep 17 00:00:00 2001 From: "M. Scott Ford" Date: Wed, 25 Sep 2024 13:15:14 -0400 Subject: [PATCH 6/8] Removes `s3_website.yml` rename step --- .github/workflows/build.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da5619b..4cb9a32 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,10 +59,6 @@ jobs: uses: actions/download-artifact@v4 with: name: s3_website - - - name: Move s3_website.yml into root - run: | - mv s3_website.yml s3_website.yml - name: S3 Publish uses: docker://justinharringa/s3_website:master From b0baa4f472053eaa4b85cf3bc123621d7daf1b9f Mon Sep 17 00:00:00 2001 From: "M. Scott Ford" Date: Wed, 25 Sep 2024 13:19:48 -0400 Subject: [PATCH 7/8] Update artifact download paths --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4cb9a32..f8a0369 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,11 +54,17 @@ jobs: uses: actions/download-artifact@v4 with: name: out + path: out - name: Retrieve s3_website.yml from artifacts uses: actions/download-artifact@v4 with: name: s3_website + path: s3_website + + - name: Move s3_website.yml into root + run: | + mv s3_website/s3_website.yml s3_website.yml - name: S3 Publish uses: docker://justinharringa/s3_website:master From c795539f251d7fe0fd627bda3dd65d545d1706e4 Mon Sep 17 00:00:00 2001 From: "M. Scott Ford" Date: Tue, 8 Apr 2025 19:46:35 -0400 Subject: [PATCH 8/8] Adds supporters page Pulls current set of members from the Patreon API --- .env.sample | 2 + .github/workflows/build.yml | 2 + .gitignore | 1 + package-lock.json | 221 +++++++++++++++++++++++++++++- package.json | 2 + src/api/patrons.js | 82 +++++++++++ src/components/CardWithHeader.jsx | 16 +++ src/components/Layout.jsx | 1 + src/components/PatronTier.jsx | 21 +++ src/components/PatronsByTier.jsx | 7 + src/components/SignUpButton.jsx | 12 ++ src/pages/community.jsx | 32 +---- src/pages/supporters.jsx | 86 ++++++++++++ 13 files changed, 449 insertions(+), 36 deletions(-) create mode 100644 .env.sample create mode 100644 src/api/patrons.js create mode 100644 src/components/CardWithHeader.jsx create mode 100644 src/components/PatronTier.jsx create mode 100644 src/components/PatronsByTier.jsx create mode 100644 src/components/SignUpButton.jsx create mode 100644 src/pages/supporters.jsx diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..58b5fd7 --- /dev/null +++ b/.env.sample @@ -0,0 +1,2 @@ +PATREON_CREATOR_ACCESS_TOKEN="Check 1Password" +PATREON_CAMPAIGN_ID="Check 1Password" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f8a0369..d57c45a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,8 @@ env: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} S3_BUCKET: "${{ secrets.S3_BUCKET }}" CLOUDFRONT_ID: ${{ secrets.CLOUDFRONT_ID }} + PATREON_CREATOR_ACCESS_TOKEN: ${{ secrets.PATREON_CREATOR_ACCESS_TOKEN }} + PATREON_CAMPAIGN_ID: ${{ secrets.PATREON_CAMPAIGN_ID }} jobs: build: diff --git a/.gitignore b/.gitignore index 55175ef..ac12e94 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ yarn-error.log* .pnpm-debug.log* # local env files +.env .env*.local # vercel diff --git a/package-lock.json b/package-lock.json index ee73d6c..552ed9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "tailwindui-transmit", "version": "0.1.0", "dependencies": { + "@nathanhigh/patreon": "^0.5.2", + "@next/env": "^15.2.4", "@tailwindcss/typography": "^0.5.7", "autoprefixer": "^10.4.12", "clsx": "^1.2.1", @@ -216,10 +218,26 @@ "@babel/runtime": "^7.6.2" } }, + "node_modules/@nathanhigh/patreon": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nathanhigh/patreon/-/patreon-0.5.2.tgz", + "integrity": "sha512-07OtqAGSh6ZVV+I6Z5QWkbKTQ84xd5+/LRFWPonVhveecVoZaqYg9jK6TJdES5wbCoP+0J9mBPihL6PuvsHglA==", + "license": "MIT", + "dependencies": { + "form-urlencoded": "^2.0.4", + "is-plain-object": "^2.0.4", + "isomorphic-fetch": "^2.2.1", + "jsonapi-datastore": "^0.4.0-beta" + }, + "engines": { + "node": ">6.2.1" + } + }, "node_modules/@next/env": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.3.0.tgz", - "integrity": "sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ==" + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", + "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==", + "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { "version": "13.0.2", @@ -2714,6 +2732,15 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -3479,6 +3506,12 @@ "node": ">= 6" } }, + "node_modules/form-urlencoded": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/form-urlencoded/-/form-urlencoded-2.0.9.tgz", + "integrity": "sha512-fWUzNiOnYa126vFAT6TFXd1mhJrvD8IqmQ9ilZPjkLYQfaRreBr5fIUoOpPlWtqaAG64nzoE7u5zSetifab9IA==", + "license": "MIT" + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -3729,6 +3762,18 @@ "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", "dev": true }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3974,6 +4019,18 @@ "node": ">=8" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -4002,6 +4059,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -4050,6 +4116,25 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==", + "license": "MIT", + "dependencies": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, "node_modules/jiti": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", @@ -4105,6 +4190,12 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonapi-datastore": { + "version": "0.4.0-beta", + "resolved": "https://registry.npmjs.org/jsonapi-datastore/-/jsonapi-datastore-0.4.0-beta.tgz", + "integrity": "sha512-RkI3oL1Ww6HefMEVZXdBLmdVY/s4P5FlRzccyXb7QmsuyTQqYDXgI8QN6Py8QJRZF05f4b4gecdeBz/oTOgRVA==", + "license": "MIT" + }, "node_modules/jsx-ast-utils": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.1.tgz", @@ -4419,6 +4510,12 @@ } } }, + "node_modules/next/node_modules/@next/env": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.3.0.tgz", + "integrity": "sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ==", + "license": "MIT" + }, "node_modules/node-abi": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.35.0.tgz", @@ -4437,6 +4534,16 @@ "integrity": "sha512-GyHvgPvUXBvAkXa0YvYnhilSB1A+FRYMpIVggKzPZqdaZfevZOuzfWzyvgzOwRLHBeo/MMswmJFsrNF4Nw1pmA==", "dev": true }, + "node_modules/node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "license": "MIT", + "dependencies": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -5403,6 +5510,12 @@ "node": ">=10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -6107,6 +6220,12 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6340,10 +6459,21 @@ "@babel/runtime": "^7.6.2" } }, + "@nathanhigh/patreon": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nathanhigh/patreon/-/patreon-0.5.2.tgz", + "integrity": "sha512-07OtqAGSh6ZVV+I6Z5QWkbKTQ84xd5+/LRFWPonVhveecVoZaqYg9jK6TJdES5wbCoP+0J9mBPihL6PuvsHglA==", + "requires": { + "form-urlencoded": "^2.0.4", + "is-plain-object": "^2.0.4", + "isomorphic-fetch": "^2.2.1", + "jsonapi-datastore": "^0.4.0-beta" + } + }, "@next/env": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.3.0.tgz", - "integrity": "sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ==" + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", + "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==" }, "@next/eslint-plugin-next": { "version": "13.0.2", @@ -8165,6 +8295,14 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "requires": { + "iconv-lite": "^0.6.2" + } + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -8739,6 +8877,11 @@ "mime-types": "^2.1.12" } }, + "form-urlencoded": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/form-urlencoded/-/form-urlencoded-2.0.9.tgz", + "integrity": "sha512-fWUzNiOnYa126vFAT6TFXd1mhJrvD8IqmQ9ilZPjkLYQfaRreBr5fIUoOpPlWtqaAG64nzoE7u5zSetifab9IA==" + }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -8912,6 +9055,14 @@ "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", "dev": true }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -9077,6 +9228,14 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -9096,6 +9255,11 @@ "call-bind": "^1.0.2" } }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -9129,6 +9293,20 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, "jiti": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", @@ -9175,6 +9353,11 @@ "minimist": "^1.2.0" } }, + "jsonapi-datastore": { + "version": "0.4.0-beta", + "resolved": "https://registry.npmjs.org/jsonapi-datastore/-/jsonapi-datastore-0.4.0-beta.tgz", + "integrity": "sha512-RkI3oL1Ww6HefMEVZXdBLmdVY/s4P5FlRzccyXb7QmsuyTQqYDXgI8QN6Py8QJRZF05f4b4gecdeBz/oTOgRVA==" + }, "jsx-ast-utils": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.1.tgz", @@ -9406,6 +9589,13 @@ "caniuse-lite": "^1.0.30001406", "postcss": "8.4.14", "styled-jsx": "5.1.1" + }, + "dependencies": { + "@next/env": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.3.0.tgz", + "integrity": "sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ==" + } } }, "node-abi": { @@ -9423,6 +9613,15 @@ "integrity": "sha512-GyHvgPvUXBvAkXa0YvYnhilSB1A+FRYMpIVggKzPZqdaZfevZOuzfWzyvgzOwRLHBeo/MMswmJFsrNF4Nw1pmA==", "dev": true }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -10033,6 +10232,11 @@ "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", "dev": true }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -10533,6 +10737,11 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 27c13de..707e4f1 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ }, "browserslist": "defaults, not ie <= 11", "dependencies": { + "@nathanhigh/patreon": "^0.5.2", + "@next/env": "^15.2.4", "@tailwindcss/typography": "^0.5.7", "autoprefixer": "^10.4.12", "clsx": "^1.2.1", diff --git a/src/api/patrons.js b/src/api/patrons.js new file mode 100644 index 0000000..fce1691 --- /dev/null +++ b/src/api/patrons.js @@ -0,0 +1,82 @@ +import { patreon, jsonApiURL } from '@nathanhigh/patreon'; + +const campaignId = process.env.PATREON_CAMPAIGN_ID; + +const campaignUrl = jsonApiURL(`/campaigns/${campaignId}`, { + fields: { + campaign: ['created_at', 'creation_name', 'patron_count'], + member: ['full_name', 'patron_status'], + tier: ['title', 'amount_cents'] + }, + include: ['tiers'] +}); + +const membersUrl = jsonApiURL(`/campaigns/${campaignId}/members`, { + fields: { + campaign: ['created_at', 'creation_name', 'patron_count'], + member: ['full_name', 'patron_status'], + tier: ['title', 'amount_cents'] + }, + include: ['currently_entitled_tiers'] +}); + +export async function getPatronsByTier() { + const levels = await getMembershipLevels(); + const patrons = await getPatrons(); + + const patronsByTier = new Map(); + for (const [tierId, tier] of levels) { + patronsByTier.set(tierId, { + title: tier.title, + amount_cents: tier.amount_cents, + patrons: [] + }); + } + + for (const [memberId, member] of patrons) { + for (const tierId of member.tiers) { + if (patronsByTier.has(tierId)) { + patronsByTier.get(tierId).patrons.push({ + id: memberId, + full_name: member.full_name, + patron_status: member.patron_status + }); + } + } + } + + return [...patronsByTier.values()]; +} + +export async function getPatrons() { + const client = patreon(process.env.PATREON_CREATOR_ACCESS_TOKEN); + const result = await client(membersUrl); + + const collectedMembers = Object + .values(result.store.graph.member) + .filter(item => item.patron_status == 'active_patron') + .sort((left, right) => left.full_name.localeCompare(right.full_name)) + .map((item) => [ + item.id, + { + full_name: item.full_name, + patron_status: item.patron_status, + tiers: item.currently_entitled_tiers.map(tier => tier.id) + } + ]) + + return new Map(collectedMembers); +} + +export async function getMembershipLevels() { + const client = patreon(process.env.PATREON_CREATOR_ACCESS_TOKEN); + const result = await client(campaignUrl); + + const collectedLevels = Object + .values(result.store.graph.tier) + .filter(item => item.amount_cents > 0) + .sort((left, right) => left.amount_cents - right.amount_cents) + .map((item) => [item.id, { title: item.title, amount_cents: item.amount_cents }]); + + return new Map(collectedLevels) +} diff --git a/src/components/CardWithHeader.jsx b/src/components/CardWithHeader.jsx new file mode 100644 index 0000000..5ca2c68 --- /dev/null +++ b/src/components/CardWithHeader.jsx @@ -0,0 +1,16 @@ +export function CardWithHeader({title, children}) { + return ( + <> +
+
+

+ {title} +

+
+
+ {children} +
+
+ + ) +} diff --git a/src/components/Layout.jsx b/src/components/Layout.jsx index 5e8efa4..5cb4ae7 100644 --- a/src/components/Layout.jsx +++ b/src/components/Layout.jsx @@ -13,6 +13,7 @@ import sidewaysLogo from '@/images/lcr-logo-sideways.svg' const navigation = [ { name: 'Home', href: '/' }, + { name: 'Supporters', href: '/supporters' }, { name: 'Community', href: '/community' }, { name: 'About', href: '/about' }, { name: 'Pitch a Guest', href: '/pitch' }, diff --git a/src/components/PatronTier.jsx b/src/components/PatronTier.jsx new file mode 100644 index 0000000..b259c68 --- /dev/null +++ b/src/components/PatronTier.jsx @@ -0,0 +1,21 @@ +import { CardWithHeader } from '@/components/CardWithHeader'; +import { SignUpButton } from '@/components/SignUpButton'; + +export function PatronTier({ tier }) { + const { title, amount_cents, patrons } = tier; + + return (<> + +
    + {patrons.map((member) => ( +
  • + {member.full_name} +
  • + ))} +
+

+ Add your name here! +

+
+ ) +} diff --git a/src/components/PatronsByTier.jsx b/src/components/PatronsByTier.jsx new file mode 100644 index 0000000..37852f3 --- /dev/null +++ b/src/components/PatronsByTier.jsx @@ -0,0 +1,7 @@ +import { PatronTier } from '@/components/PatronTier'; + +export function PatronsByTier({ patrons }) { + return (<> + {patrons.map((tier) => ())} + ) +} diff --git a/src/components/SignUpButton.jsx b/src/components/SignUpButton.jsx new file mode 100644 index 0000000..3af7a87 --- /dev/null +++ b/src/components/SignUpButton.jsx @@ -0,0 +1,12 @@ +export function SignUpButton({href, children}) { + return ( + + ) +} diff --git a/src/pages/community.jsx b/src/pages/community.jsx index 5c69528..2187095 100644 --- a/src/pages/community.jsx +++ b/src/pages/community.jsx @@ -1,35 +1,7 @@ import Head from 'next/head' import { ProseContainer } from '@/components/ProseContainer' - -export function CardWithHeader({title, children}) { - return ( - <> -
-
-

- {title} -

-
-
- {children} -
-
- - ) -} - -export function SignUpButton({href, children}) { - return ( - - ) -} +import { CardWithHeader } from '@/components/CardWithHeader' +import { SignUpButton } from '@/components/SignUpButton' export default function Community() { return ( diff --git a/src/pages/supporters.jsx b/src/pages/supporters.jsx new file mode 100644 index 0000000..fc00736 --- /dev/null +++ b/src/pages/supporters.jsx @@ -0,0 +1,86 @@ +import Head from 'next/head' +import { ProseContainer } from '@/components/ProseContainer' +import { CardWithHeader } from '@/components/CardWithHeader' +import { SignUpButton } from '@/components/SignUpButton' + +import { getPatronsByTier } from '@/api/patrons' +import { PatronsByTier } from '@/components/PatronsByTier' + +export default function Supporters({ patrons }) { + + + return ( + <> + + Codestin Search App + + + +

Many Thanks to Our Supporters

+

+ Legacy Code Rocks receives financial support from the wonderful folks + listed below. We are incredibly grateful for their generosity. +

+ +

Supporters by Level

+ + +

Join Us

+

+ Want to see your name listed here? You can become a paying member of + the community by visiting our + Patreon page, and selecting a tier that works best for you. +

+

+ What do get for being a paid member? Beyond helping ensure that + there are enough funds to record, host, and edit podcast episodes, + while also providing general support for hosting our annual virtual + conference, MenderCon, there are + a couple of perks. At the moment, all membership levels get the same + benefits. +

+

+ All paying members are displayed on this page. Additionally, a + random paying member will be thanked at the end of each episode, + and we`'`ll be working through our back catalog to update the + past recordings to include shoutouts as well. +

+

+ Each installment of our conference, + MenderCon, will include a slide between events that lists the + names of paying members. +

+

+ More perks may be added later. We`'`re considering creating a + video version of future episodes that will only be available to + paying members. If you`'`ve got any perk ideas, please share + them in Slack. +

+

+ Everything free to access today will continue to be free. So + don`'`t worry about the audio content or access to + Slack being limited to only paying members. Doing so + wouldn`'`t be the best way to support Legacy Code Rocks mission. +

+

+ + Join Now + +

+
+ + ) +} + +export async function getStaticProps() { + const patrons = await getPatronsByTier() + + return { + props: { patrons} + } +}