-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Backers + Contributors mdx components (#339)
* feat: Backer, Contributors mdx components * fix: Handle empty backers and contributors lists in mdx components * CONTRIBUTORS_PAT + authoring doc * pmndrs/docs contrib --------- Co-authored-by: Antoine BERNIER <antoine.bernier@gmail.com>
- Loading branch information
1 parent
15e5769
commit 5e99474
Showing
11 changed files
with
207 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import cn from '@/lib/cn' | ||
import { initials } from '@/utils/text' | ||
import { Octokit } from '@octokit/core' | ||
import Image from 'next/image' | ||
import { cache, ComponentProps } from 'react' | ||
import backerBadge from './backer-badge.svg' | ||
|
||
// ██████ ██████ ███ ██ ████████ ██████ ██ ██████ ██ ██ ████████ ██████ ██████ ███████ | ||
// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | ||
// ██ ██ ██ ██ ██ ██ ██ ██████ ██ ██████ ██ ██ ██ ██ ██ ██████ ███████ | ||
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | ||
// ██████ ██████ ██ ████ ██ ██ ██ ██ ██████ ██████ ██ ██████ ██ ██ ███████ | ||
|
||
const octokit = new Octokit({ | ||
auth: process.env.CONTRIBUTORS_PAT, | ||
}) | ||
|
||
export async function Contributors({ | ||
owner, | ||
repo, | ||
limit = 50, | ||
className, | ||
...props | ||
}: { owner: string; repo: string; limit: number } & ComponentProps<'ul'>) { | ||
const contributors = ( | ||
await cachedFetchContributors(owner, repo).catch( | ||
(err) => | ||
Array.from({ length: 100 }).map(() => ({ | ||
login: 'jdoe', | ||
html_url: 'https://github.com/jdoe', | ||
})) as Awaited<ReturnType<typeof cachedFetchContributors>>, | ||
) | ||
).slice(0, limit) | ||
|
||
return ( | ||
<div> | ||
<ul className={cn('flex flex-wrap gap-1', className)} {...props}> | ||
{contributors.map(({ html_url, avatar_url, login }) => ( | ||
<li key={login}> | ||
<Avatar profileUrl={html_url} imageUrl={avatar_url} name={login} /> | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
) | ||
} | ||
|
||
async function fetchContributors(owner: string, repo: string) { | ||
const res = await octokit.request(`GET /repos/{owner}/{repo}/collaborators`, { | ||
owner, | ||
repo, | ||
headers: { | ||
'X-GitHub-Api-Version': '2022-11-28', | ||
}, | ||
}) | ||
return res.data | ||
} | ||
const cachedFetchContributors = cache(fetchContributors) | ||
|
||
// ██████ █████ ██████ ██ ██ ███████ ██████ ███████ | ||
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | ||
// ██████ ███████ ██ █████ █████ ██████ ███████ | ||
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | ||
// ██████ ██ ██ ██████ ██ ██ ███████ ██ ██ ███████ | ||
|
||
export async function Backers({ | ||
repo, | ||
limit = 50, | ||
className, | ||
...props | ||
}: ComponentProps<'ul'> & { | ||
repo: string | ||
limit: number | ||
}) { | ||
const backers = (await fetchBackers(repo)).slice(0, limit) | ||
|
||
return ( | ||
<div> | ||
<ul className={cn('flex flex-wrap gap-1', className)} {...props}> | ||
{backers.map((backer) => ( | ||
<li key={backer.name}> | ||
<Avatar profileUrl={backer.profile} imageUrl={backer.image} name={backer.name} /> | ||
</li> | ||
))} | ||
</ul> | ||
<p> | ||
<a | ||
href={`https://opencollective.com/${repo}#backers`} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
<Image src={backerBadge} alt="Backer" /> | ||
</a> | ||
</p> | ||
</div> | ||
) | ||
} | ||
|
||
async function fetchBackers(repo: string) { | ||
const res = await fetch(`https://opencollective.com/${repo}/members/users.json`) | ||
const backers: { | ||
profile: string | ||
name: string | ||
image: string | ||
totalAmountDonated: number | ||
}[] = await res.json() | ||
|
||
const backersMap = new Map(backers.map((backer) => [backer.name, backer])) | ||
backersMap.forEach((backer) => { | ||
const existingBacker = backersMap.get(backer.name) | ||
if (existingBacker && backer.totalAmountDonated >= existingBacker.totalAmountDonated) { | ||
backersMap.set(backer.name, backer) // replace with the backer with the highest donation | ||
} | ||
}) | ||
const uniqueBackers = Array.from(backersMap.values()) | ||
|
||
return uniqueBackers.sort((a, b) => b.totalAmountDonated - a.totalAmountDonated) | ||
} | ||
|
||
const cachedFetchBackers = cache(fetchBackers) | ||
|
||
// | ||
// common | ||
// | ||
|
||
function Avatar({ | ||
profileUrl, | ||
imageUrl, | ||
name, | ||
className, | ||
...props | ||
}: { profileUrl: string; imageUrl: string; name: string } & ComponentProps<'a'>) { | ||
return ( | ||
<a | ||
href={profileUrl} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
className={cn( | ||
className, | ||
'bg-surface-container-high inline-flex size-12 items-center justify-center overflow-clip rounded-full', | ||
)} | ||
{...props} | ||
> | ||
{imageUrl ? ( | ||
<Image width="48" height="48" src={imageUrl} alt={name} className="size-full" /> | ||
) : ( | ||
initials(name) | ||
)} | ||
</a> | ||
) | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './People' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters