Skip to content

Commit 2cc2210

Browse files
authored
Merge pull request #124 from ethereum/crowdin
implement CrowdinContributors component
2 parents edaf0ab + 1fe81a9 commit 2cc2210

13 files changed

+253
-236
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { Author, CrowdinContributor } from "@/lib/types"
2+
3+
import FileContributors from "@/components/FileContributors"
4+
5+
type CrowdinContributorsProps = {
6+
relativePath: string
7+
lastUpdatedDate: string
8+
contributors: CrowdinContributor[]
9+
}
10+
const CrowdinContributors = ({
11+
lastUpdatedDate,
12+
contributors,
13+
}: CrowdinContributorsProps) => {
14+
const mappedContributors: Author[] = contributors.map(
15+
({ id, username, avatarUrl }) => ({
16+
name: username,
17+
email: id.toString(),
18+
avatarUrl: avatarUrl,
19+
user: {
20+
login: username,
21+
url: `https://crowdin.com/profile/${username}`,
22+
},
23+
})
24+
)
25+
26+
return (
27+
<FileContributors
28+
error={mappedContributors.length === 0} // TODO: Confirm GH error handling
29+
loading={!mappedContributors.length}
30+
contributors={mappedContributors}
31+
lastEdit={lastUpdatedDate}
32+
/>
33+
)
34+
}
35+
36+
export default CrowdinContributors

src/components/FileContributors.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { useState } from "react"
22
import { useRouter } from "next/router"
3-
import { FaGithub } from "react-icons/fa"
43
import {
54
Avatar,
65
Flex,
76
FlexProps,
87
Heading,
9-
Icon,
108
ListItem,
119
ModalBody,
1210
ModalHeader,
@@ -16,10 +14,9 @@ import {
1614
VStack,
1715
} from "@chakra-ui/react"
1816

19-
import type { Lang } from "@/lib/types"
20-
import type { Author } from "@/lib/interfaces"
17+
import type { Author, Lang } from "@/lib/types"
2118

22-
import { Button, ButtonLink } from "@/components/Buttons"
19+
import { Button } from "@/components/Buttons"
2320
import InlineLink from "@/components/Link"
2421
import Modal from "@/components/Modal"
2522
import Text from "@/components/OldText"
@@ -72,15 +69,15 @@ const Contributor = ({ contributor }: { contributor: Author }) => {
7269
)
7370
}
7471

75-
export interface IProps extends FlexProps {
72+
export interface FileContributorsProps extends FlexProps {
7673
editPath?: string
77-
contributors: Array<Author>
78-
loading: Boolean
74+
contributors: Author[]
75+
loading: boolean
7976
error?: boolean
8077
lastEdit: string
8178
}
8279

83-
const FileContributors: React.FC<IProps> = ({
80+
const FileContributors: React.FC<FileContributorsProps> = ({
8481
contributors,
8582
loading,
8683
error,

src/components/FileContributorsCrowdin.tsx

Lines changed: 0 additions & 155 deletions
This file was deleted.

src/components/GitHubContributors.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ const GitHubContributors: React.FC<IProps> = ({
1414
relativePath,
1515
lastUpdatedDate,
1616
}) => {
17-
const { loading, authors, error } = useClientSideGitHubContributors(relativePath)
17+
const state = useClientSideGitHubContributors(relativePath)
18+
1819
return (
1920
<FileContributors
20-
error={!!error}
21-
loading={loading}
22-
contributors={authors || []}
21+
error={"error" in state}
22+
loading={state.loading as boolean}
23+
contributors={"data" in state ? state.data : []}
2324
lastEdit={lastUpdatedDate}
2425
/>
2526
)

src/hooks/useClientSideGitHubContributors.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@ import { join } from "path"
22

33
import { useEffect, useState } from "react"
44

5-
import type { FileContributorsState } from "@/lib/types"
6-
import type { Author } from "@/lib/interfaces"
5+
import type { Author, FileContributorsState } from "@/lib/types"
76

87
import { GITHUB_COMMITS_URL, OLD_CONTENT_DIR } from "@/lib/constants"
98

10-
const gitHubAuthHeaders = {
9+
export const gitHubAuthHeaders = {
1110
headers: new Headers({
1211
// About personal access tokens https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#about-personal-access-tokens
1312
Authorization: "Token " + process.env.NEXT_PUBLIC_GITHUB_TOKEN_READ_ONLY,
1413
}),
1514
}
1615

17-
const fetchGitHubContributors = async (relativePath: string) => {
16+
const fetchGitHubContributors = async (
17+
relativePath: string,
18+
): Promise<FileContributorsState> => {
1819
const url = new URL(GITHUB_COMMITS_URL)
1920
// TODO: OLD_CONTENT_DIR -> CONTENT_DIR for production
2021
const filePath = join(OLD_CONTENT_DIR, relativePath, "index.md")
@@ -43,7 +44,7 @@ const fetchGitHubContributors = async (relativePath: string) => {
4344
const authors = Array.from(authorSet).map(
4445
JSON.parse as (entry: string) => Author
4546
)
46-
return { loading: false, authors }
47+
return { loading: false, data: authors }
4748
} catch (error: unknown) {
4849
if (error instanceof Error) {
4950
console.error(filePath, error.message)
@@ -54,15 +55,15 @@ const fetchGitHubContributors = async (relativePath: string) => {
5455
/**
5556
* Client-side hook to fetch GitHub contributors for a given file
5657
* @param relativePath Relative path of the file being queried
57-
* @returns `state` comprise of { loading, authors, error } where
58-
* authors is an array of Author objects if successful
58+
* @returns `state` comprise of { loading, data, error } where
59+
* data is an array of Author objects if successful
5960
*/
6061
export const useClientSideGitHubContributors = (
6162
relativePath: string
6263
): FileContributorsState => {
6364
const [state, setState] = useState<FileContributorsState>({ loading: true })
6465
useEffect(() => {
65-
;(async () => {
66+
; (async () => {
6667
setState(await fetchGitHubContributors(relativePath))
6768
})()
6869
}, [relativePath])
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { join } from "path"
2+
3+
import { useEffect, useState } from "react"
4+
import { useRouter } from "next/router"
5+
6+
import type { Lang, LastUpdatedState } from "@/lib/types"
7+
8+
import {
9+
DEFAULT_LOCALE,
10+
GITHUB_COMMITS_URL,
11+
OLD_CONTENT_DIR,
12+
} from "@/lib/constants"
13+
14+
import { gitHubAuthHeaders } from "@/hooks/useClientSideGitHubContributors"
15+
16+
const fetchGitHubLastEdit = async (
17+
relativePath: string,
18+
locale?: Lang
19+
): Promise<LastUpdatedState> => {
20+
const url = new URL(GITHUB_COMMITS_URL)
21+
// TODO: OLD_CONTENT_DIR -> CONTENT_DIR for production
22+
const localePath =
23+
locale && locale !== DEFAULT_LOCALE
24+
? join("translations", locale, relativePath)
25+
: relativePath
26+
const filePath = join(OLD_CONTENT_DIR, localePath, "index.md")
27+
url.searchParams.set("path", filePath)
28+
29+
try {
30+
const response = await fetch(url, gitHubAuthHeaders)
31+
if (!response.ok) throw new Error(response.statusText)
32+
const lastCommit = (await response.json())[0]
33+
const lastEdit = lastCommit.commit.author.date
34+
return { loading: false, data: lastEdit }
35+
} catch (error: unknown) {
36+
if (error instanceof Error) {
37+
console.error(filePath, error.message)
38+
}
39+
return { loading: false, error }
40+
}
41+
}
42+
/**
43+
* Client-side hook to fetch date of last commit through GitHub
44+
* @param relativePath Relative path of the file being queried
45+
* @returns `state` comprise of data with loading state, where data is last edit string
46+
*/
47+
export const useClientSideGitHubLastEdit = (
48+
relativePath: string
49+
): LastUpdatedState => {
50+
const [state, setState] = useState<LastUpdatedState>({ loading: true })
51+
const { locale } = useRouter()
52+
useEffect(() => {
53+
;(async () => {
54+
setState(await fetchGitHubLastEdit(relativePath, locale as Lang))
55+
})()
56+
}, [relativePath, locale])
57+
return state
58+
}

0 commit comments

Comments
 (0)