diff --git a/packages/website/data/sponsors.json b/packages/website/data/sponsors.json index 5eabd5e5dce..42de64539f5 100644 --- a/packages/website/data/sponsors.json +++ b/packages/website/data/sponsors.json @@ -1,173 +1,326 @@ [ { - "description": null, - "id": "7mywxoz3-409rl6kk-v9b6venb-dj7gk85a", - "image": "https://images.opencollective.com/deal-empire/b3f7583/logo.png", - "name": "Deal Empire", - "slug": "deal-empire", - "tier": "supporter", - "website": "https://dealempire.com/" + "id": "Nx (by Nrwl)", + "image": "https://images.opencollective.com/nx/0efbe42/logo.png", + "name": "Nx (by Nrwl)", + "tier": "sponsor", + "totalDonations": 375000, + "website": "https://nx.dev" }, { - "description": "LIVE PAIN FREE", - "id": "x8k03rey-d5agmq5m-z9b6lbwo-z7j4nxv9", - "image": "https://images.opencollective.com/my-sports-injury-ltd/518c583/logo.png", - "name": "MySportsInjury", - "slug": "my-sports-injury-ltd", + "id": "ESLint", + "image": "https://images.opencollective.com/eslint/96b09dc/logo.png", + "name": "ESLint", + "tier": "sponsor", + "totalDonations": 110000, + "website": "https://eslint.org/" + }, + { + "id": "Airbnb", + "image": "https://images.opencollective.com/airbnb/d327d66/logo.png", + "name": "Airbnb", + "tier": "sponsor", + "totalDonations": 105800, + "website": "https://www.airbnb.com/" + }, + { + "id": "Coinbase", + "image": "https://images.opencollective.com/coinbase/a202856/logo.png", + "name": "Coinbase", + "tier": "sponsor", + "totalDonations": 100000, + "website": "https://blog.coinbase.com/engineering-and-security/home" + }, + { + "id": "EY Doberman", + "image": "https://images.opencollective.com/ey-doberman/b269462/logo.png", + "name": "EY Doberman", + "tier": "sponsor", + "totalDonations": 80400, + "website": "https://doberman.co" + }, + { + "id": "n8n.io - n8n GmbH", + "image": "https://images.opencollective.com/n8n/dca2f0c/logo.png", + "name": "n8n.io - n8n GmbH", + "tier": "sponsor", + "totalDonations": 80000, + "website": "https://n8n.io" + }, + { + "id": "Future Processing", + "image": "https://images.opencollective.com/future-processing/1410d26/logo.png", + "name": "Future Processing", "tier": "supporter", - "website": "https://www.mysportsinjury.co.uk" + "totalDonations": 54000, + "website": "https://www.future-processing.com/" }, { - "description": "java learning", - "id": "jrkx5lmn-v904qjw5-xbbp8bwa-7zdygoe3", - "image": "https://images.opencollective.com/java-exp/14e869f/logo.png", - "name": "java", - "slug": "java-exp", + "id": "Sentry", + "image": "https://images.opencollective.com/sentry/9620d33/logo.png", + "name": "Sentry", "tier": "supporter", - "website": "https://explainjava.com/" + "totalDonations": 50000, + "website": "https://sentry.io/welcome/" }, { - "description": null, - "id": "03k0exgz-nm8yj64l-rn5p5wao-9r7b4dlv", - "image": "https://images.opencollective.com/codecademy/d56a48d/logo.png", - "name": "Codecademy", - "slug": "codecademy", - "tier": "sponsor", - "website": "https://codecademy.com" + "id": "Whitebox", + "image": "https://images.opencollective.com/whiteboxinc/ef0d11d/logo.png", + "name": "Whitebox", + "tier": "supporter", + "totalDonations": 40000, + "website": "https://whitebox.com" }, { - "description": null, - "id": "v349mrwg-z75lpy7g-angpa08d-jeybknox", + "id": "GitBook", "image": "https://images.opencollective.com/gitbook/d35a8e7/logo.png", "name": "GitBook", - "slug": "gitbook", "tier": "sponsor", + "totalDonations": 40000, "website": "https://www.gitbook.com" }, { - "description": "Accelerate your development work on iOS, Android and beyond with the power of ARM-based virtual devices.", - "id": "gm9bnk80-437xqryv-89jpvzeo-ljdayw5r", - "image": "https://images.opencollective.com/corellium/aa8c228/logo.png", - "name": "Corellium", - "slug": "corellium", - "tier": "supporter", - "website": "https://www.corellium.com" + "id": "Codecademy", + "image": "https://images.opencollective.com/codecademy/d56a48d/logo.png", + "name": "Codecademy", + "tier": "sponsor", + "totalDonations": 30000, + "website": "https://codecademy.com" }, { - "description": null, - "id": "ov349mrw-gz75lpyx-elzqa08d-jeybknox", - "image": "https://images.opencollective.com/gaetan-bloch/c5cfcb2/avatar.png", - "name": "Gaëtan Bloch", - "slug": "gaetan-bloch", + "id": "Monito", + "image": "https://images.opencollective.com/monito/50fc878/logo.png", + "name": "Monito", "tier": "supporter", - "website": null + "totalDonations": 27500, + "website": "https://www.monito.com" }, { - "description": "The pluggable linting utility for JavaScript and JSX", - "id": "k3z8arxv-eymko60y-73wqgl5n-bj9w704d", - "image": "https://images.opencollective.com/eslint/96b09dc/logo.png", - "name": "ESLint", - "slug": "eslint", - "tier": "sponsor", - "website": "https://eslint.org/" + "id": "revo.js", + "image": "https://images.opencollective.com/revojsro/82623a7/logo.png", + "name": "revo.js", + "tier": "supporter", + "totalDonations": 23000, + "website": "https://revojs.ro" }, { - "description": "Our mission is to create an open financial system for the world. ", - "id": "7ywz9j4a-vgod8pgn-996mr35n-xklb0e7a", - "image": "https://images.opencollective.com/coinbase/a202856/logo.png", - "name": "Coinbase", - "slug": "coinbase", - "tier": "sponsor", - "website": "https://blog.coinbase.com/engineering-and-security/home" + "id": "Ian MacLeod", + "image": "https://images.opencollective.com/nevir/35c52ef/avatar.png", + "name": "Ian MacLeod", + "tier": "supporter", + "totalDonations": 22000, + "website": "https://twitter.com/nevir" }, { - "description": null, - "id": "vjrkx5lm-nv904qjv-k0bq8bwa-7zdygoe3", - "image": "https://images.opencollective.com/jeffrey-rennie/avatar.png", - "name": "Jeffrey Rennie", - "slug": "jeffrey-rennie", + "id": "STORIS", + "image": "https://images.opencollective.com/storis/dfb0e13/logo.png", + "name": "STORIS", "tier": "supporter", - "website": null + "totalDonations": 16500, + "website": "https://www.storis.com/" }, { - "description": "We're Balsa, and we're building tools for builders.", - "id": "nmlo94zn-7x08dpov-y0e6ewga-3vjbrky5", - "image": "https://images.opencollective.com/balsa/77de498/logo.png", - "name": "Balsa", - "slug": "balsa", + "id": "Michael Ranciglio", + "image": "https://images.opencollective.com/michael-ranciglio/avatar.png", + "name": "Michael Ranciglio", "tier": "supporter", - "website": "https://balsa.com" + "totalDonations": 15000 }, { - "description": "Evil Martians is a distributed product development consultancy that works with startups and established businesses, and creates open source-based products and services.", - "id": "53kzxy4v-07wlr6md-7ve6mj9n-o8agdbe5", - "image": "https://images.opencollective.com/evilmartians/707ab4d/logo.png", - "name": "Evil Martians", - "slug": "evilmartians", + "id": "Joe Alden", + "image": "https://images.opencollective.com/joealden/44a6738/avatar.png", + "name": "Joe Alden", + "tier": "contributor", + "totalDonations": 14000, + "website": "https://joealden.com" + }, + { + "id": "David Johnston", + "image": "https://images.opencollective.com/blacksheepcode/976d69a/avatar.png", + "name": "David Johnston", "tier": "supporter", - "website": "https://evilmartians.com/" + "totalDonations": 11500, + "website": "https://blacksheepcode.com" }, { - "description": null, - "id": "nmlo94zn-7x08dpo5-nyoqewga-3vjbrky5", + "id": "Gianfranco Palumbo", + "image": "https://images.opencollective.com/gianpaj/5d62d25/avatar.png", + "name": "Gianfranco Palumbo", + "tier": "contributor", + "totalDonations": 10000, + "website": "http://gian.xyz" + }, + { + "id": "The Guardian", + "image": "https://images.opencollective.com/gdndevelopers/0b72bf0/logo.png", + "name": "The Guardian", + "tier": "contributor", + "totalDonations": 10000, + "website": "https://www.theguardian.com/" + }, + { + "id": "Tripwire, Inc.", "image": "https://images.opencollective.com/tripwire/7599e30/logo.png", "name": "Tripwire, Inc.", - "slug": "tripwire", "tier": "supporter", + "totalDonations": 7000, "website": "https://tripwire.com" }, { - "description": "Nx is an extensible development toolkit that helps teams of all sizes easily create and scale their Web applications using modern tools.", - "id": "bvrgbk35-7l4x96e0-nlaqomew-a0jdyzn8", - "image": "https://images.opencollective.com/nx/0efbe42/logo.png", - "name": "Nx (by Nrwl)", - "slug": "nx", - "tier": "sponsor", - "website": "https://nx.dev" + "id": "Evil Martians", + "image": "https://images.opencollective.com/evilmartians/707ab4d/logo.png", + "name": "Evil Martians", + "tier": "supporter", + "totalDonations": 6500, + "website": "https://evilmartians.com/" }, { - "description": "Free and open Workflow Automation tool which can be self-hosted and easily extended", - "id": "7ywz9j4a-vgod8pgv-d8ypmr35-nxklb0e7", - "image": "https://images.opencollective.com/n8n/dca2f0c/logo.png", - "name": "n8n.io - n8n GmbH", - "slug": "n8n", + "id": "Balsa", + "image": "https://images.opencollective.com/balsa/77de498/logo.png", + "name": "Balsa", "tier": "supporter", - "website": "https://n8n.io" + "totalDonations": 6500, + "website": "https://balsa.com" }, { - "description": null, - "id": "ov349mrw-gz75lpyz-0l86a08d-jeybknox", - "image": "https://images.opencollective.com/florian-studio/a713d94/logo.png", - "name": "Florian Studio", - "slug": "florian-studio", + "id": "Jeffrey Rennie", + "image": "https://images.opencollective.com/jeffrey-rennie/avatar.png", + "name": "Jeffrey Rennie", "tier": "supporter", - "website": "https://www.ceodata.com" + "totalDonations": 6000 }, { - "description": "Full Stack JavaScript Developer", - "id": "03k0exgz-nm8yj64r-wjjp5wao-9r7b4dlv", - "image": "https://images.opencollective.com/blacksheepcode/976d69a/avatar.png", - "name": "David Johnston", - "slug": "blacksheepcode", - "tier": "supporter", - "website": "https://blacksheepcode.com" + "id": "Pete Gonzalez", + "image": "https://images.opencollective.com/octogonz/513f01a/avatar.png", + "name": "Pete Gonzalez", + "tier": "contributor", + "totalDonations": 5000, + "website": "https://github.com/octogonz" }, { - "description": "Book unique homes and experience a city like a local.", - "id": "88rzownx-l9e50pxw-rwqymvbd-gk7j43a9", - "image": "https://images.opencollective.com/airbnb/d327d66/logo.png", - "name": "Airbnb", - "slug": "airbnb", + "id": "Niklas Fiekas", + "image": "https://images.opencollective.com/niklas-fiekas/ffec4a8/avatar.png", + "name": "Niklas Fiekas", + "tier": "contributor", + "totalDonations": 5000, + "website": "https://backscattering.de" + }, + { + "id": "Indeed", + "image": "https://images.opencollective.com/indeed/4b8725e/logo.png", + "name": "Indeed", + "tier": "contributor", + "totalDonations": 5000, + "website": "https://Indeed.com" + }, + { + "id": "Andrey Sitnik", + "image": "https://images.opencollective.com/andrey-sitnik/11aeb28/avatar.png", + "name": "Andrey Sitnik", + "tier": "contributor", + "totalDonations": 4500, + "website": "https://sitnik.ru" + }, + { + "id": "Corellium", + "image": "https://images.opencollective.com/corellium/aa8c228/logo.png", + "name": "Corellium", "tier": "supporter", - "website": "https://www.airbnb.com/" + "totalDonations": 3000, + "website": "https://www.corellium.com" }, { - "description": "I write code sometimes and support stuff I like on here.", - "id": "53kzxy4v-07wlr6mv-wob6mj9n-o8agdbe5", - "image": "https://images.opencollective.com/michael-ranciglio/avatar.png", - "name": "Michael Ranciglio", - "slug": "michael-ranciglio", + "id": "Kamino Ryo", + "image": "https://images.opencollective.com/kaminoryo/d1d9ff5/avatar.png", + "name": "Kamino Ryo", + "tier": "contributor", + "totalDonations": 3000 + }, + { + "id": "Kevin Smith", + "image": "https://images.opencollective.com/kevin-smith1/a343791/avatar.png", + "name": "Kevin Smith", + "tier": "contributor", + "totalDonations": 2500 + }, + { + "id": "Dani Gellis", + "image": "https://images.opencollective.com/dani-gellis1/avatar.png", + "name": "Dani Gellis", + "tier": "contributor", + "totalDonations": 2500 + }, + { + "id": "38elements", + "image": "https://images.opencollective.com/38elements/5dfbefe/avatar.png", + "name": "38elements", + "tier": "contributor", + "totalDonations": 2200, + "website": "https://japanese-document.github.io/react-redux/connect.html" + }, + { + "id": "Gaëtan Bloch", + "image": "https://images.opencollective.com/gaetan-bloch/c5cfcb2/avatar.png", + "name": "Gaëtan Bloch", + "tier": "contributor", + "totalDonations": 2100 + }, + { + "id": "Softwear, BV", + "image": "https://images.opencollective.com/softwear_dev/dc2d34e/logo.png", + "name": "Softwear, BV", + "tier": "contributor", + "totalDonations": 2000, + "website": "https://www.softwearconnect.com" + }, + { + "id": "Sean Lindsay", + "image": "https://images.opencollective.com/sean-lindsay/avatar.png", + "name": "Sean Lindsay", + "tier": "contributor", + "totalDonations": 2000 + }, + { + "id": "Jane Doe", + "image": "https://images.opencollective.com/jane-doe/avatar.png", + "name": "Jane Doe", + "tier": "contributor", + "totalDonations": 2000 + }, + { + "id": "Philip Keiter", + "image": "https://images.opencollective.com/philip-keiter/1c359c0/avatar.png", + "name": "Philip Keiter", + "tier": "contributor", + "totalDonations": 1000 + }, + { + "id": "Toru Shimogaisho", + "image": "https://images.opencollective.com/gaishimo/f620536/avatar.png", + "name": "Toru Shimogaisho", + "tier": "contributor", + "totalDonations": 1000 + }, + { + "id": "Kazuhiro Kobayashi", + "image": "https://images.opencollective.com/kazuhiro-kobayashi/a1eeb73/avatar.png", + "name": "Kazuhiro Kobayashi", + "tier": "contributor", + "totalDonations": 1000 + }, + { + "id": "Masafumi Koba", + "image": "https://images.opencollective.com/ybiquitous/6a6913d/avatar.png", + "name": "Masafumi Koba", + "tier": "contributor", + "totalDonations": 1000 + }, + { + "id": "MySportsInjury", + "image": "https://images.opencollective.com/my-sports-injury-ltd/518c583/logo.png", + "name": "MySportsInjury", "tier": "supporter", - "website": null + "totalDonations": 1000, + "website": "https://www.mysportsinjury.co.uk" } ] diff --git a/packages/website/src/components/FinancialContributors.tsx b/packages/website/src/components/FinancialContributors.tsx deleted file mode 100644 index 400442239d8..00000000000 --- a/packages/website/src/components/FinancialContributors.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import Link from '@docusaurus/Link'; -import sponsors from '@site/data/sponsors.json'; -import clsx from 'clsx'; -import React from 'react'; - -import styles from './FinancialContributors.module.css'; - -function Sponsors(props: { - description: string; - showName?: boolean; - tier: string; - title: string; -}): JSX.Element { - const tierSponsors = sponsors.filter(sponsor => sponsor.tier === props.tier); - return ( -
-

{props.title}

-

{props.description}

- -
- ); -} - -export function FinancialContributors(): JSX.Element { - return ( - <> -

- The TypeScript ESLint project would not be possible without the generous - support of our financial contributors. -

-
- - -
- - Become a financial contributor - - - ); -} diff --git a/packages/website/src/components/FinancialContributors/Sponsor.tsx b/packages/website/src/components/FinancialContributors/Sponsor.tsx new file mode 100644 index 00000000000..f43b0d3d81e --- /dev/null +++ b/packages/website/src/components/FinancialContributors/Sponsor.tsx @@ -0,0 +1,42 @@ +import React from 'react'; + +import { SponsorData, SponsorIncludeOptions } from './types'; + +interface SponsorProps { + className?: string; + include?: SponsorIncludeOptions; + sponsor: SponsorData; +} + +export function Sponsor({ + className, + include = {}, + sponsor, +}: SponsorProps): JSX.Element { + let children = {`${sponsor.name}; + + if (include.name) { + children = ( + <> + {children} + {sponsor.name.split(' - ')[0]} + + ); + } + + if (include.link) { + children = ( + + {children} + + ); + } + + return children; +} diff --git a/packages/website/src/components/FinancialContributors/Sponsors/index.tsx b/packages/website/src/components/FinancialContributors/Sponsors/index.tsx new file mode 100644 index 00000000000..2e47b077d5b --- /dev/null +++ b/packages/website/src/components/FinancialContributors/Sponsors/index.tsx @@ -0,0 +1,40 @@ +import sponsors from '@site/data/sponsors.json'; +import clsx from 'clsx'; +import React from 'react'; + +import styles from './styles.module.css'; +import { Sponsor } from '../Sponsor'; +import { SponsorIncludeOptions } from '../types'; + +interface SponsorsProps { + className: string; + description: string; + include?: SponsorIncludeOptions; + expanded?: boolean; + tier?: string; + title: string; +} + +export function Sponsors({ + className, + description, + include, + tier, + title, +}: SponsorsProps): JSX.Element { + return ( +
+

{title}

+

{description}

+ +
+ ); +} diff --git a/packages/website/src/components/FinancialContributors.module.css b/packages/website/src/components/FinancialContributors/Sponsors/styles.module.css similarity index 53% rename from packages/website/src/components/FinancialContributors.module.css rename to packages/website/src/components/FinancialContributors/Sponsors/styles.module.css index 96749add5b1..0373225a2b5 100644 --- a/packages/website/src/components/FinancialContributors.module.css +++ b/packages/website/src/components/FinancialContributors/Sponsors/styles.module.css @@ -1,5 +1,6 @@ .tierArea { - margin: 32px 0; + margin: 32px auto; + width: 100%; } .sponsorsTier { @@ -15,6 +16,11 @@ .sponsorsTier li { list-style: none; margin: 5px; + max-width: 120px; +} + +.sponsorsTier img { + margin: auto; } .sponsorsTier a, @@ -28,35 +34,32 @@ } .tier-sponsor img { - max-height: 110px; - width: 110px; + display: inline-block; + max-height: 120px; + width: 120px; } .tier-supporter img { - max-height: 60px; - width: 60px; + max-height: 45px; + width: 45px; } -.sponsorLink { - display: inline-flex; - flex-direction: column; - gap: 8px; +.tier-contributor img { + max-height: 30px; + width: 30px; } -.become { - margin: 8px 0 24px; - font-size: 1rem; +@media screen and (min-width: 700px) { + .tierArea { + min-width: 500px; + width: 50vw; + } } @media screen and (min-width: 1150px) { - .sponsorsContainer { - display: flex; - gap: 32px; - margin: auto; - max-width: 75%; - } - .tierArea { - width: 50%; + margin: 16px 0; + width: auto; + padding: 0 60px; } } diff --git a/packages/website/src/components/FinancialContributors/index.tsx b/packages/website/src/components/FinancialContributors/index.tsx new file mode 100644 index 00000000000..9665632df41 --- /dev/null +++ b/packages/website/src/components/FinancialContributors/index.tsx @@ -0,0 +1,46 @@ +import Link from '@docusaurus/Link'; +import clsx from 'clsx'; +import React from 'react'; + +import styles from './styles.module.css'; +import { Sponsors } from './Sponsors'; + +export function FinancialContributors(): JSX.Element { + return ( + <> +

+ The TypeScript ESLint project would not be possible without the generous + support of our financial contributors. +

+
+ + + +
+ + Become a financial contributor + + + ); +} diff --git a/packages/website/src/components/FinancialContributors/styles.module.css b/packages/website/src/components/FinancialContributors/styles.module.css new file mode 100644 index 00000000000..b4de958b902 --- /dev/null +++ b/packages/website/src/components/FinancialContributors/styles.module.css @@ -0,0 +1,37 @@ +.sponsorLink { + display: inline-flex; + flex-direction: column; + gap: 8px; +} + +.become { + margin: 8px 0 24px; + font-size: 1rem; +} + +@media screen and (min-width: 1150px) { + .sponsorsContainer { + display: grid; + grid-template-columns: 55% 45%; + margin: auto; + max-width: 100%; + } + + .tierArea { + margin: 16px 0; + width: auto; + padding: 0 60px; + } + + .tierSponsorArea { + grid-area: 1 / 1 / 3 / 2; + } + + .tierSupporterArea { + grid-area: 1 / 2 / 2 / 3; + } + + .tierOtherArea { + grid-area: 2 / 2 / 3 / 3; + } +} diff --git a/packages/website/src/components/FinancialContributors/types.ts b/packages/website/src/components/FinancialContributors/types.ts new file mode 100644 index 00000000000..fb20cb4623a --- /dev/null +++ b/packages/website/src/components/FinancialContributors/types.ts @@ -0,0 +1,14 @@ +export interface SponsorData { + description?: string; + id: string; + image: string; + name: string; + tier?: string; + totalDonations: number; + website?: string; +} + +export interface SponsorIncludeOptions { + link?: boolean; + name?: boolean; +} diff --git a/tools/generate-sponsors.ts b/tools/generate-sponsors.ts index 79f472879a3..140445de11e 100644 --- a/tools/generate-sponsors.ts +++ b/tools/generate-sponsors.ts @@ -4,90 +4,198 @@ import * as path from 'path'; const graphqlEndpoint = 'https://api.opencollective.com/graphql/v2'; -const graphqlQuery = `{ - account(slug: "typescript-eslint") { - orders(status: ACTIVE, limit: 1000) { - totalCount - nodes { - tier { - slug +const queries = { + account: `{ + account(slug: "typescript-eslint") { + orders(status: ACTIVE, limit: 1000) { + totalCount + nodes { + tier { + slug + } + fromAccount { + id + imageUrl + name + website + } } - fromAccount { - id - name - slug - website - imageUrl - description + } + } + }`, + collective: `{ + collective(slug: "typescript-eslint") { + members(limit: 1000, role: BACKER) { + nodes { + account { + id + imageUrl + name + website + } + tier { + amount { + valueInCents + } + orders(limit: 100) { + nodes { + amount { + valueInCents + } + } + } + } + totalDonations { + valueInCents + } + updatedAt } } } - } -}`; + }`, +}; -const excludedIds = new Set([ - '53kzxy4v-07wlr6mr-o9epmj9n-o8agdbe5', // Josh Goldberg -]); - -interface SponsorsData { - data: { - account: { - orders: { - nodes: SponsorNode[]; - }; - }; +interface AccountData { + orders: { + nodes: OrderNode[]; }; } -interface SponsorNode { - fromAccount: { - description: string; - id: string; - imageUrl: string; - name: string; - slug: string; - twitterHandle: string; - website: string; +interface OrderNode { + fromAccount: MemberAccount; + tier?: Tier; +} + +interface Tier { + slug: string; +} + +interface CollectiveData { + members: { + nodes: MemberNode[]; }; - tier?: { - slug: string; +} + +interface MemberNode { + account: MemberAccount; + totalDonations: { + valueInCents: number; }; } -async function main(): Promise { +interface MemberAccount { + id: string; + imageUrl: string; + name: string; + twitterHandle: string; + website: string; +} + +interface MemberAccountAndTier extends MemberAccount { + tier?: Tier; +} + +const excludedNames = new Set([ + 'Guest', // Apparent anonymous donor equivalent without an avatar + 'Josh Goldberg', // Team member 💖 + + // These names *seem* to be spam websites, but we're not sure. + // If your name is mistakenly on this list, we're sorry; please let us know! + 'Deal Empire', + 'Florian Studio', + 'java', + 'Loyalty Leo', + 'Penalty.com', +]); + +async function requestGraphql(key: keyof typeof queries): Promise { const response = await fetch(graphqlEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query: graphqlQuery }), + body: JSON.stringify({ query: queries[key] }), }); - const data = (await response.json()) as SponsorsData; - const uniqueIds = new Set(excludedIds); - const allSponsorsConfig = data.data.account.orders.nodes - .filter(node => !!node.tier) - .map(node => ({ - description: node.fromAccount.description, - id: node.fromAccount.id, - image: node.fromAccount.imageUrl, - name: node.fromAccount.name, - slug: node.fromAccount.slug, - tier: node.tier.slug, - twitterHandle: node.fromAccount.twitterHandle, - website: node.fromAccount.website, - })) - .filter(({ id }) => { - if (uniqueIds.has(id)) { + + const { data } = (await response.json()) as { + data: Record; + }; + return data[key] as Data; +} + +async function main(): Promise { + const [account, collective] = await Promise.all([ + requestGraphql('account'), + requestGraphql('collective'), + ]); + + const accountsById = account.orders.nodes.reduce< + Record + >((accumulator, account) => { + const name = account.fromAccount.name || account.fromAccount.id; + accumulator[name] = { + ...accumulator[name], + ...account.fromAccount, + tier: account.tier, + }; + return accumulator; + }, {}); + + const totalDonationsById = collective.members.nodes.reduce< + Record + >((accumulator, member) => { + const name = member.account.name || member.account.id; + accumulator[name] ||= 0; + accumulator[name] += member.totalDonations.valueInCents; + return accumulator; + }, {}); + + const uniqueNames = new Set(excludedNames); + const allSponsorsConfig = collective.members.nodes + .map(member => { + const name = member.account.name || member.account.id; + const fromAccount: MemberAccountAndTier = { + ...member.account, + ...accountsById[name], + }; + const totalDonations = totalDonationsById[name]; + const slug = fromAccount.tier?.slug ?? 'contributor'; + + return { + id: name, + image: fromAccount.imageUrl, + name: fromAccount.name, + tier: + slug === 'sponsor' || totalDonations >= 750_00 + ? 'sponsor' + : slug === 'supporter-plus' || totalDonations >= 150_00 + ? 'supporter' + : slug, + totalDonations, + twitterHandle: fromAccount.twitterHandle, + website: fromAccount.website, + }; + }) + .filter(({ id, totalDonations }) => { + if (uniqueNames.has(id) || totalDonations < 10_00) { return false; } - uniqueIds.add(id); + uniqueNames.add(id); return true; - }); + }) + .sort((a, b) => b.totalDonations - a.totalDonations); const rcPath = path.resolve( __dirname, '../packages/website/data/sponsors.json', ); - fs.writeFileSync(rcPath, JSON.stringify(allSponsorsConfig, null, 2)); + fs.writeFileSync( + rcPath, + JSON.stringify( + allSponsorsConfig, + (_, value: unknown) => value ?? undefined, + 2, + ), + ); } main().catch(error => {