refactor: replace static file with API to get contributors

This commit is contained in:
unix
2020-04-15 12:32:48 +08:00
parent e4667349f0
commit d695961d01
7 changed files with 83 additions and 189 deletions

View File

@@ -1,36 +1,20 @@
import React, { useMemo } from 'react'
import { Card, Link, Spacer, Avatar, Tooltip, useTheme } from 'components'
import React from 'react'
import { Card, Spacer, useTheme } from 'components'
import AttributesTitle from './attributes-title'
import VirtualAnchor from 'lib/components/anchor'
import { useConfigs } from '../../config-context'
import ContributorMetadatas from 'lib/data/contributors.json'
const GithubURL = 'https://github.com/zeit-ui/react/blob/master'
import Contributors from './contributors'
export interface AttributesProps {
edit: string
}
export interface Contributor {
name: string
avatar: string
url: string
}
export type ContributorMeta = {
[key: string]: Array<Contributor>
}
const Attributes: React.FC<React.PropsWithChildren<AttributesProps>> = React.memo(({
edit, children,
}) => {
const theme = useTheme()
const { isChinese } = useConfigs()
const link = useMemo(() => `${GithubURL}${edit || '/pages'}`, [])
const contributors = useMemo(() => {
const key = edit.replace('/pages', 'pages')
const users = (ContributorMetadatas as ContributorMeta)[key]
return users || []
}, [])
const path = edit.replace('/pages', 'pages')
return (
<>
@@ -41,21 +25,7 @@ const Attributes: React.FC<React.PropsWithChildren<AttributesProps>> = React.mem
</Card>
<Spacer y={3} />
<h4 className="contributor-title">{isChinese ? '文档贡献者' : 'Contributors'}</h4>
<div className="contributors">
{contributors.map((user, index) => (
<Tooltip text={<b>{user.name}</b>} key={`${user.url}-${index}`}>
<Link color pure target="_blank" rel="nofollow" href={user.url}>
<Avatar src={user.avatar} />
</Link>
</Tooltip>
))}
<Tooltip text={isChinese ? '在 GitHub 上编辑此页面' : 'Edit this page on GitHub'} type="dark">
<Link color pure target="_blank" rel="nofollow" href={link}>
<Avatar text="Add" />
</Link>
</Tooltip>
</div>
<Contributors path={path} />
<style global jsx>{`
.attr table {
margin-right: ${theme.layout.gap};
@@ -134,19 +104,6 @@ const Attributes: React.FC<React.PropsWithChildren<AttributesProps>> = React.mem
letter-spacing: 1.5px;
}
.contributors {
padding-left: ${theme.layout.gap};
padding-top: ${theme.layout.gap};
max-width: 100%;
height: auto;
display: flex;
flex-wrap: wrap;
}
.contributors :global(.tooltip) {
margin-right: 3px;
}
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
.attr {
overflow-x: scroll;

View File

@@ -0,0 +1,77 @@
import React, { useEffect, useMemo, useState } from 'react'
import { Avatar, Link, Tooltip, useTheme } from 'components'
import { useConfigs } from 'lib/config-context'
const GithubURL = 'https://github.com/zeit-ui/react/blob/master'
const host = 'https://contributors.zeit-ui.co/api/users'
export interface Contributor {
name: string
avatar: string
url: string
}
interface Props {
path: string
}
const getContributors = async (path: string): Promise<Array<Contributor>> => {
try {
const response = await fetch(`${host}?path=${path}`)
if (!response.ok) return []
return response.json()
} catch (e) {
return []
}
}
const Contributors: React.FC<Props> = ({ path }) => {
const theme = useTheme()
const { isChinese } = useConfigs()
const [users, setUsers] = useState<Array<Contributor>>([])
const link = useMemo(() => `${GithubURL}/${path || '/pages'}`, [])
useEffect(() => {
let unmount = false
;(async () => {
const contributors = await getContributors(path)
if (unmount) return
setUsers(contributors)
})()
return () => {
unmount = true
}
}, [])
return (
<div className="contributors">
{users.map((user, index) => (
<Tooltip text={<b>{user.name}</b>} key={`${user.url}-${index}`}>
<Link color pure target="_blank" rel="nofollow" href={user.url}>
<Avatar src={user.avatar} />
</Link>
</Tooltip>
))}
<Tooltip text={isChinese ? '在 GitHub 上编辑此页面' : 'Edit this page on GitHub'} type="dark">
<Link color pure target="_blank" rel="nofollow" href={link}>
<Avatar text="Add" />
</Link>
</Tooltip>
<style jsx>{`
.contributors {
padding-left: ${theme.layout.gap};
padding-top: ${theme.layout.gap};
max-width: 100%;
height: auto;
display: flex;
flex-wrap: wrap;
}
.contributors :global(.tooltip) {
margin-right: 3px;
}
`}</style>
</div>
)
}
export default Contributors

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,5 @@
{
"github": {
"silent": true
},
"build": {
"env": {
"GIT_ORG_READONLY": "@git-org-readonly"
}
}
}

View File

@@ -9,7 +9,7 @@
"license": "MIT",
"scripts": {
"dev": "yarn docs-collect && next dev",
"docs-build": "yarn docs-collect && yarn contributor-collect && next build",
"docs-build": "yarn docs-collect && next build",
"docs-start": "next start",
"docs-collect": "node scripts/collect-meta.js",
"contributor-collect": "node scripts/collect-contributors.js",
@@ -61,7 +61,6 @@
"@typescript-eslint/parser": "^2.24.0",
"babel-jest": "^25.3.0",
"babel-loader": "^8.0.6",
"dotenv": "^8.2.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"eslint": "^6.8.0",
@@ -69,7 +68,6 @@
"eslint-plugin-react": "^7.19.0",
"extract-mdx-metadata": "^1.0.0",
"fs-extra": "^8.1.0",
"graphql-request": "^1.8.2",
"jest": "^25.3.0",
"next": "^9.3.4",
"react": "^16.13.0",

View File

@@ -1,102 +0,0 @@
if (!process.env.GIT_ORG_READONLY) require('dotenv').config()
const fs = require('fs-extra')
const path = require('path')
const { GraphQLClient } = require('graphql-request')
const target = path.join(__dirname, '../lib/data/', 'contributors.json')
const token = process.env.GIT_ORG_READONLY
if (!token) {
console.error('> Not found "GIT_ORG_READONLY" in "process.env".\n')
console.log(' Env variables are automatically injected at production.')
console.log(' If you want to test, run [echo "GIT_ORG_READONLY=your_git_token" > .env ]\n')
process.exit(1)
}
const client = new GraphQLClient('https://api.github.com/graphql', {
headers: {
Authorization: `Bearer ${token}`,
},
})
const pagePrefix = path.join(__dirname, '../pages')
const filterContributors = data => {
if (!data || !data.repository) return []
const nodes = data.repository.object.history.nodes
let users = [], keys = {}
for (const item of nodes) {
const key = item.author.user.url
if (!keys[key]) {
keys[key] = 1
users.push({
name: item.author.name,
avatar: item.author.user.avatarUrl,
url: item.author.user.url,
})
}
}
return users
}
const getContributors = async repoFilePath => {
const query = `query($path: String!) {
repository(owner: "zeit-ui", name: "react") {
object(expression: "master") {
... on Commit {
history(first: 100, path: $path) {
nodes {
author {
name
user {
avatarUrl
url
}
}
}
}
}
}
}
}`
const data = await client.request(query, { path: repoFilePath })
return filterContributors(data)
}
const getFiles = async dirPath => {
const files = await fs.readdir(dirPath)
return files.filter(name => name.endsWith('.mdx'))
}
const getUrls = async () => {
const en = path.join(pagePrefix, 'en-us', 'components')
const zh = path.join(pagePrefix, 'zh-cn', 'components')
const enFiles = await getFiles(en)
const zhFiles = await getFiles(zh)
return enFiles
.map(name => `pages/en-us/components/${name}`)
.concat(zhFiles.map(name => `pages/zh-cn/components/${name}`))
}
;(async () => {
const urls = await getUrls()
const users = await Promise.all(urls.map(async url => {
try {
return {
name: url,
users: await getContributors(url),
}
} catch (e) {
return {}
}
}))
const contributors = users.reduce((pre, current) => {
if (!current.name) return pre
return {
...pre,
[current.name]: current.users,
}
}, {})
fs.writeJSONSync(target, contributors)
})()

View File

@@ -2923,14 +2923,6 @@ create-react-context@0.2.2:
fbjs "^0.8.0"
gud "^1.0.0"
cross-fetch@2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.2.tgz#a47ff4f7fc712daba8f6a695a11c948440d45723"
integrity sha1-pH/09/xxLauo9qaVoRyUhEDUVyM=
dependencies:
node-fetch "2.1.2"
whatwg-fetch "2.0.4"
cross-fetch@3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.4.tgz#7bef7020207e684a7638ef5f2f698e24d9eb283c"
@@ -3298,11 +3290,6 @@ dot-case@^2.1.0:
dependencies:
no-case "^2.2.0"
dotenv@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
duplexify@^3.4.2, duplexify@^3.6.0:
version "3.7.1"
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
@@ -4214,13 +4201,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
graphql-request@^1.8.2:
version "1.8.2"
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-1.8.2.tgz#398d10ae15c585676741bde3fc01d5ca948f8fbe"
integrity sha512-dDX2M+VMsxXFCmUX0Vo0TopIZIX4ggzOtiCsThgtrKR4niiaagsGTDIHj3fsOMFETpa064vzovI+4YV4QnMbcg==
dependencies:
cross-fetch "2.2.2"
growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -6092,11 +6072,6 @@ no-case@^2.2.0, no-case@^2.3.2:
dependencies:
lower-case "^1.1.1"
node-fetch@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5"
integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=
node-fetch@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
@@ -8828,11 +8803,6 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5:
dependencies:
iconv-lite "0.4.24"
whatwg-fetch@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==
whatwg-fetch@3.0.0, whatwg-fetch@>=0.10.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"