From 462898f4b442a25d39c14329cb7db2f921d1a6d3 Mon Sep 17 00:00:00 2001 From: Peolite1 Date: Mon, 27 Jul 2026 20:59:29 +0100 Subject: [PATCH 1/4] feat: add opt-in contributor leaderboard --- scripts/gen-contributors.mjs | 49 +++++++++++++++++++ src/App.tsx | 9 ++++ src/data/contributors-optout.json | 4 ++ src/data/contributors.json | 9 ++++ src/pages/Contributors.tsx | 79 +++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+) create mode 100644 scripts/gen-contributors.mjs create mode 100644 src/data/contributors-optout.json create mode 100644 src/data/contributors.json create mode 100644 src/pages/Contributors.tsx diff --git a/scripts/gen-contributors.mjs b/scripts/gen-contributors.mjs new file mode 100644 index 0000000..f2abdec --- /dev/null +++ b/scripts/gen-contributors.mjs @@ -0,0 +1,49 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const OPTOUT_FILE = path.join(__dirname, '../src/data/contributors-optout.json'); +const OUTPUT_FILE = path.join(__dirname, '../src/data/contributors.json'); + +async function run() { + try { + const optOutData = await fs.readFile(OPTOUT_FILE, 'utf-8'); + const optOutList = JSON.parse(optOutData); + + console.log('Fetching contributors from GitHub API...'); + + // In a real run without token, rate limits apply, so we handle fetch gracefully + const res = await fetch('https://api.github.com/repos/wraith-protocol/www/contributors', { + headers: { + 'User-Agent': 'Node-Fetch', + ...(process.env.GITHUB_TOKEN ? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } : {}) + } + }); + + if (!res.ok) { + throw new Error(`GitHub API returned ${res.status} ${res.statusText}`); + } + + const contributors = await res.json(); + + const leaderboard = contributors + .filter(c => !optOutList.includes(c.login)) + .map(c => ({ + username: c.login, + avatar: c.avatar_url, + profile: c.html_url, + prCount: c.contributions, + // Mock wave participation as requested, or derive if data existed. + waves: ['Wave 1'] + })); + + await fs.writeFile(OUTPUT_FILE, JSON.stringify(leaderboard, null, 2), 'utf-8'); + console.log(`Successfully generated contributors.json with ${leaderboard.length} contributors.`); + } catch (error) { + console.error('Error generating contributors:', error); + process.exit(1); + } +} + +run(); diff --git a/src/App.tsx b/src/App.tsx index aab59e5..43779a7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -27,6 +27,7 @@ const Roadmap = lazy(() => import('./pages/Roadmap')); const CaseStudies = lazy(() => import('./pages/CaseStudies')); const Careers = lazy(() => import('./pages/Careers')); const NotFound = lazy(() => import('./pages/NotFound')); +const Contributors = lazy(() => import('./pages/Contributors')); function Home() { return ( @@ -87,6 +88,14 @@ export default function App() { } /> + + + + } + /> } /> diff --git a/src/data/contributors-optout.json b/src/data/contributors-optout.json new file mode 100644 index 0000000..33d816a --- /dev/null +++ b/src/data/contributors-optout.json @@ -0,0 +1,4 @@ +[ + "dependabot[bot]", + "github-actions[bot]" +] diff --git a/src/data/contributors.json b/src/data/contributors.json new file mode 100644 index 0000000..bf92b79 --- /dev/null +++ b/src/data/contributors.json @@ -0,0 +1,9 @@ +[ + { + "username": "ghost", + "avatar": "https://github.com/ghost.png", + "profile": "https://github.com/ghost", + "prCount": 10, + "waves": ["Wave 1"] + } +] diff --git a/src/pages/Contributors.tsx b/src/pages/Contributors.tsx new file mode 100644 index 0000000..b50af0e --- /dev/null +++ b/src/pages/Contributors.tsx @@ -0,0 +1,79 @@ +import React, { useEffect, useState } from 'react'; +import contributorsData from '../data/contributors.json'; + +interface Contributor { + username: string; + avatar: string; + profile: string; + prCount: number; + waves: string[]; +} + +export default function Contributors() { + const [contributors, setContributors] = useState([]); + + useEffect(() => { + // Data is static for the client and refreshed weekly by a GitHub Action/script. + // We import it directly here. + setContributors(contributorsData as Contributor[]); + }, []); + + return ( +
+
+

+ Contributor Leaderboard +

+

+ Celebrating the amazing people who build and improve Wraith Protocol. +

+
+

+ Note: Data is refreshed weekly via an automated script. + If you wish to opt-out of this leaderboard, please submit a PR to add your username to our opt-out list at src/data/contributors-optout.json. +

+
+
+ + +
+ ); +} From 945318aae7dc432f84884ff123f4b199d0495b21 Mon Sep 17 00:00:00 2001 From: Peolite001 Date: Fri, 31 Jul 2026 02:09:37 +0100 Subject: [PATCH 2/4] style: format files --- scripts/gen-contributors.mjs | 24 ++++++++++++++---------- src/data/contributors-optout.json | 5 +---- src/pages/Contributors.tsx | 23 ++++++++++++++--------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/scripts/gen-contributors.mjs b/scripts/gen-contributors.mjs index f2abdec..199f745 100644 --- a/scripts/gen-contributors.mjs +++ b/scripts/gen-contributors.mjs @@ -10,36 +10,40 @@ async function run() { try { const optOutData = await fs.readFile(OPTOUT_FILE, 'utf-8'); const optOutList = JSON.parse(optOutData); - + console.log('Fetching contributors from GitHub API...'); - + // In a real run without token, rate limits apply, so we handle fetch gracefully const res = await fetch('https://api.github.com/repos/wraith-protocol/www/contributors', { headers: { 'User-Agent': 'Node-Fetch', - ...(process.env.GITHUB_TOKEN ? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } : {}) - } + ...(process.env.GITHUB_TOKEN + ? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } + : {}), + }, }); if (!res.ok) { throw new Error(`GitHub API returned ${res.status} ${res.statusText}`); } - + const contributors = await res.json(); - + const leaderboard = contributors - .filter(c => !optOutList.includes(c.login)) - .map(c => ({ + .filter((c) => !optOutList.includes(c.login)) + .map((c) => ({ username: c.login, avatar: c.avatar_url, profile: c.html_url, prCount: c.contributions, // Mock wave participation as requested, or derive if data existed. - waves: ['Wave 1'] + waves: ['Wave 1'], })); await fs.writeFile(OUTPUT_FILE, JSON.stringify(leaderboard, null, 2), 'utf-8'); - console.log(`Successfully generated contributors.json with ${leaderboard.length} contributors.`); + console.log( + `Successfully generated contributors.json with ${leaderboard.length} contributors.`, + ); } catch (error) { console.error('Error generating contributors:', error); process.exit(1); diff --git a/src/data/contributors-optout.json b/src/data/contributors-optout.json index 33d816a..ae4062b 100644 --- a/src/data/contributors-optout.json +++ b/src/data/contributors-optout.json @@ -1,4 +1 @@ -[ - "dependabot[bot]", - "github-actions[bot]" -] +["dependabot[bot]", "github-actions[bot]"] diff --git a/src/pages/Contributors.tsx b/src/pages/Contributors.tsx index b50af0e..ab4bb83 100644 --- a/src/pages/Contributors.tsx +++ b/src/pages/Contributors.tsx @@ -29,12 +29,13 @@ export default function Contributors() {

- Note: Data is refreshed weekly via an automated script. - If you wish to opt-out of this leaderboard, please submit a PR to add your username to our opt-out list at src/data/contributors-optout.json. + Note: Data is refreshed weekly via an automated script. If you wish to + opt-out of this leaderboard, please submit a PR to add your username to our opt-out list + at src/data/contributors-optout.json.

- +
{contributors.map((contributor) => ( - {`${contributor.username}'s
@@ -54,12 +55,16 @@ export default function Contributors() { {contributor.username}

- {contributor.prCount} Contributions + {contributor.prCount}{' '} + Contributions

{contributor.waves && contributor.waves.length > 0 && (
- {contributor.waves.map(wave => ( - + {contributor.waves.map((wave) => ( + {wave} ))} From 4dc763bfd56f74f89307c178e924eb577afd4dcd Mon Sep 17 00:00:00 2001 From: Peolite001 Date: Fri, 31 Jul 2026 02:15:22 +0100 Subject: [PATCH 3/4] fix: resolve unused import typescript errors --- src/App.tsx | 8 ++++++++ src/pages/Contributors.tsx | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 4f721ef..dc01097 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -92,6 +92,14 @@ export default function App() { } /> + + + + } + /> } /> diff --git a/src/pages/Contributors.tsx b/src/pages/Contributors.tsx index ab4bb83..8c39f35 100644 --- a/src/pages/Contributors.tsx +++ b/src/pages/Contributors.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import contributorsData from '../data/contributors.json'; interface Contributor { From 09a6803d25331e4f7af7ccb614e8b2fa2c960d22 Mon Sep 17 00:00:00 2001 From: Peolite001 Date: Fri, 31 Jul 2026 02:21:56 +0100 Subject: [PATCH 4/4] fix: restore missing contributors route --- src/App.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/App.tsx b/src/App.tsx index 981c99a..5ffe66b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -112,6 +112,14 @@ export default function App() { } /> + + + + } + /> } />