Skip to content

Commit

Permalink
feat: Backers + Contributors mdx components (#339)
Browse files Browse the repository at this point in the history
* 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
kangju2000 and abernier authored Sep 17, 2024
1 parent 15e5769 commit 5e99474
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ jobs:
-e THEME_IMPORTANT \
-e THEME_WARNING \
-e THEME_CAUTION \
-e CONTRIBUTORS_PAT \
ghcr.io/pmndrs/docs:2 npm run build
env:
BASE_PATH: ${{ steps.set-base-path.outputs.BASE_PATH }}
Expand All @@ -115,6 +116,7 @@ jobs:
THEME_IMPORTANT: '${{ inputs.theme_important }}'
THEME_WARNING: '${{ inputs.theme_warning }}'
THEME_CAUTION: '${{ inputs.theme_caution }}'
CONTRIBUTORS_PAT: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/upload-pages-artifact@v3
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ jobs:
--build-env THEME_IMPORTANT="#8957e5" \
--build-env THEME_WARNING="#d29922" \
--build-env THEME_CAUTION="#da3633" \
--build-env CONTRIBUTORS_PAT="${{ secrets.GITHUB_TOKEN }}" \
> deployment-url.txt
echo "deployment_url=$(cat deployment-url.txt)" >> $GITHUB_OUTPUT
Expand Down
29 changes: 29 additions & 0 deletions docs/getting-started/authoring.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,32 @@ or better:
<Hint>I'm a deprecated hint. Use `Gha` instead.</Hint>

</details>

### `Contributors`

```md
<Contributors owner="pmndrs" repo="docs" />
```

> [!WARNING]
> [`CONTRIBUTORS_PAT`](introduction#configuration:~:text=CONTRIBUTORS_PAT) needs to be set. Otherwise, it will display John Doe.
<details>
<summary>Result</summary>

<Contributors owner="pmndrs" repo="docs" />

</details>

### Backers

```md
<Backers repo="react-three-fiber" />
```

<details>
<summary>Result</summary>

<Backers repo="react-three-fiber" />

</details>
5 changes: 5 additions & 0 deletions docs/getting-started/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ $ npm ci
| `THEME_IMPORTANT` | "important" color | `#8957e5` |
| `THEME_WARNING` | "warning" color | `#d29922` |
| `THEME_CAUTION` | "caution" color | `#da3633` |
| `CONTRIBUTORS_PAT` | GitHub token for contributors API (see: https://docs.github.com/en/rest/collaborators/collaborators?apiVersion=2022-11-28#list-repository-collaborators) | `ghp_1234567890` |

\* Required

Expand Down Expand Up @@ -116,6 +117,7 @@ $ (
export THEME_IMPORTANT="#8957e5"
export THEME_WARNING="#d29922"
export THEME_CAUTION="#da3633"
export CONTRIBUTORS_PAT=

kill $(lsof -ti:"$_PORT")
npx serve $MDX -p $_PORT --no-port-switching --no-clipboard &
Expand Down Expand Up @@ -163,6 +165,7 @@ $ (
export THEME_IMPORTANT="#8957e5"
export THEME_WARNING="#d29922"
export THEME_CAUTION="#da3633"
export CONTRIBUTORS_PAT=

npm run build

Expand Down Expand Up @@ -211,6 +214,7 @@ $ (
export THEME_IMPORTANT="#8957e5"
export THEME_WARNING="#d29922"
export THEME_CAUTION="#da3633"
export CONTRIBUTORS_PAT=

rm -rf "$MDX/out"

Expand Down Expand Up @@ -238,6 +242,7 @@ $ (
-e THEME_IMPORTANT \
-e THEME_WARNING \
-e THEME_CAUTION \
-e CONTRIBUTORS_PAT \
pmndrs-docs npm run build

kill $(lsof -ti:"$_PORT")
Expand Down
11 changes: 1 addition & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
},
"dependencies": {
"@codesandbox/sandpack-react": "^2.19.8",
"@octokit/core": "^6.1.2",
"@radix-ui/react-collapsible": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-visually-hidden": "^1.1.0",
Expand Down
151 changes: 151 additions & 0 deletions src/components/mdx/People/People.tsx
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>
)
}
1 change: 1 addition & 0 deletions src/components/mdx/People/backer-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/components/mdx/People/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './People'
1 change: 1 addition & 0 deletions src/components/mdx/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './Hint'
export * from './Img'
export * from './Intro'
export * from './Keypoints'
export * from './People'
export * from './Summary'
export * from './Toc'

Expand Down
14 changes: 14 additions & 0 deletions src/utils/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,17 @@ export const highlight = (text: string, target: string) =>
target.length > 0
? text.replace(new RegExp(escape(target), 'gi'), (match: string) => `<mark>${match}</mark>`)
: text

export function initials(name: string) {
const parts = name.split(' ')

if (parts.length > 1) {
return parts
.slice(0, 2)
.map(([char]) => char)
.join('')
.toUpperCase()
}

return name.slice(0, 2).toUpperCase()
}

0 comments on commit 5e99474

Please sign in to comment.