mirror of
https://github.com/zhigang1992/react.git
synced 2026-03-06 17:25:12 +08:00
refactor: replace static file with API to get contributors
This commit is contained in:
@@ -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;
|
||||
|
||||
77
lib/components/attributes/contributors.tsx
Normal file
77
lib/components/attributes/contributors.tsx
Normal 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
5
now.json
5
now.json
@@ -1,10 +1,5 @@
|
||||
{
|
||||
"github": {
|
||||
"silent": true
|
||||
},
|
||||
"build": {
|
||||
"env": {
|
||||
"GIT_ORG_READONLY": "@git-org-readonly"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
})()
|
||||
30
yarn.lock
30
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user