diff --git a/packages/docusaurus-playground/docusaurus.config.js b/packages/docusaurus-playground/docusaurus.config.js index 1b9991d..84a4d64 100644 --- a/packages/docusaurus-playground/docusaurus.config.js +++ b/packages/docusaurus-playground/docusaurus.config.js @@ -45,6 +45,9 @@ const config = { routeBasePath: '/', path: 'docs', }, + + // Uncomment to fetch new generated data. + /* generated: { challenges: { repoArray: [ @@ -57,6 +60,7 @@ const config = { jobBoard: 'waku', }, }, + */ }), ], ], diff --git a/packages/docusaurus-playground/src/pages/index.mdx b/packages/docusaurus-playground/src/pages/index.mdx index 79c7d8b..3de57ff 100644 --- a/packages/docusaurus-playground/src/pages/index.mdx +++ b/packages/docusaurus-playground/src/pages/index.mdx @@ -22,6 +22,7 @@ import { GithubChallenges, } from '../components/mdx' import * as jobData from '/generated/jobs.json' +import * as challengesData from '/generated/challenges.json' @@ -355,5 +356,12 @@ import * as jobData from '/generated/jobs.json' Build on Waku - - + + diff --git a/packages/logos-docusaurus-preset/src/plugins/generatedData.plugin.ts b/packages/logos-docusaurus-preset/src/plugins/generatedData.plugin.ts index 6f9a959..828daed 100644 --- a/packages/logos-docusaurus-preset/src/plugins/generatedData.plugin.ts +++ b/packages/logos-docusaurus-preset/src/plugins/generatedData.plugin.ts @@ -48,6 +48,14 @@ const fetchChallenges = async ({ }: { out: string } & ChallengeConfig) => { + if (!githubAccessToken) { + console.log( + 'No github access token provided, creating empty challenges file', + ) + await fsp.writeFile(out, '{}') + return + } + const issues = await fetchGithubIssues(repoArray, githubAccessToken) await fsp.writeFile(out, JSON.stringify(issues)) diff --git a/packages/logos-docusaurus-preset/src/types/index.ts b/packages/logos-docusaurus-preset/src/types/index.ts index 21f62ed..63c2f71 100644 --- a/packages/logos-docusaurus-preset/src/types/index.ts +++ b/packages/logos-docusaurus-preset/src/types/index.ts @@ -7,3 +7,4 @@ export type SiteConfig = Partial export * from './preset' export * from './status' export * from './themes' +export * from './github' diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/GithubChallenges.scss b/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/GithubChallenges.scss index 962b98c..b932e00 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/GithubChallenges.scss +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/GithubChallenges.scss @@ -53,6 +53,7 @@ } .mdx-ghc__participant-photo { + width: 24px; border-radius: 100%; margin-left: -4px; border: 1px solid rgb(var(--lsd-border-secondary)); @@ -85,9 +86,3 @@ grid-template-columns: 82px 1fr; } } - -.mdx-ghc__achievement { - display: flex; - align-items: center; - padding-left: 4px; -} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/GithubChallenges.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/GithubChallenges.tsx index ea05d7b..d48b7be 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/GithubChallenges.tsx +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/GithubChallenges.tsx @@ -1,7 +1,8 @@ import { Typography } from '@acid-info/lsd-react' -import { SingleGithubChallenge } from './SingleGithubChallenge' import React from 'react' -import useFetchGithubIssues, { GithubIssue } from './useFetchGithubIssues' +import { dummyGithubIssue } from './githubChallengesDummyData' +import { SingleGithubChallenge } from './SingleGithubChallenge' +import { GithubIssue } from '@acid-info/logos-docusaurus-preset/src/types/github' type GithubChallengesHeaderProps = { message?: string @@ -22,50 +23,73 @@ const GithubChallengesHeader: React.FC = ({ } const hasChallenges = (githubChallenges: GithubIssue[]): boolean => { - return githubChallenges.length > 0 + return Array.isArray(githubChallenges) && !!githubChallenges.length +} + +const getIssuesFromRawData = (rawData: any): GithubIssue[] => { + let issues: GithubIssue[] = [] + + Object.keys(rawData).forEach((key) => { + if (!isNaN(Number(key))) { + // Extract the array of issues for each repository + const repoIssues = Object.values(rawData[key])[0] // each object should have only one key-value pair + if (Array.isArray(repoIssues)) { + issues = issues.concat(repoIssues) + } + } + }) + + return issues } type GithubChallengesProps = React.HTMLAttributes & { - org: string + challengesData: any useDummyData?: boolean + subheaderText?: string + filterByName?: string // A string that represents a regex pattern } export const GithubChallenges: React.FC = ({ - org, + challengesData, useDummyData, + subheaderText, + filterByName, ...props }) => { - const [data, error, loading] = useFetchGithubIssues(org, useDummyData) + let challengesArray: GithubIssue[] | undefined | null = useDummyData + ? [dummyGithubIssue] + : getIssuesFromRawData(challengesData) - if (loading) { - return - } - - if (error) { - return - } - - if (!data || !hasChallenges(data)) { + if (!challengesArray || !hasChallenges(challengesArray)) { return } + if (filterByName) { + try { + const regex = new RegExp(filterByName, 'i') // 'i' for case-insensitive + challengesArray = challengesArray?.filter((issue) => + regex.test(issue.title), + ) + } catch (error) { + console.error('Invalid regex pattern:', error) + } + } + return (
- - Lorem ipsum dolor sit amet consectetur. Enim magna urna fames mattis - tincidunt nibh mi ornare. Sed amet morbi mauris pellentesque fusce ut. - Bibendum vestibulum Lorem ipsum dolor sit amet consectetur. Enim magna - urna fames mattis tincidunt nibh mi ornare. Sed amet morbi mauris - pellentesque fusce ut. Bibendum vestibulum - - {data.map((issue) => { - return - })} + {!!subheaderText && ( + + {subheaderText} + + )} + {challengesArray.map((issue) => ( + + ))}
) } diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/SingleGithubChallenge.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/SingleGithubChallenge.tsx index 0d204b1..d0ef4bd 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/SingleGithubChallenge.tsx +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/SingleGithubChallenge.tsx @@ -3,7 +3,54 @@ import React from 'react' import { IconExternalLink } from '@logos-theme/components/Icon' import './GithubChallenges.scss' import Link from '@docusaurus/Link' -import { GithubIssue } from './useFetchGithubIssues' +import { GithubIssue } from '@acid-info/logos-docusaurus-preset/src/types/github' + +const getProjectNames = (issue: GithubIssue): string => { + const projectNames = issue.projects.map((project) => project.name) + + if (projectNames.length === 0) { + return '' + } + + return Array.from(new Set(projectNames)).join(', ') +} + +type Participant = { + name: string + avatarUrl: string +} + +const extractUniqueParticipants = (issue: GithubIssue): Participant[] => { + const participants = new Map() + + // Add the user who created the issue + participants.set(issue.user.login, { + name: issue.user.login, + avatarUrl: issue.user.avatarUrl, + }) + + // Add all assignees + issue.assignees.forEach((assignee) => { + participants.set(assignee.login, { + name: assignee.login, + avatarUrl: assignee.avatarUrl, + }) + }) + + // Skipping comment authors for now, because they don't have avatarUrl. + /* + // Add all comment authors + issue.comments.forEach((comment) => { + // Assuming comment.author is the login name of the commenter + participants.set(comment.author, { + name: comment.author, + avatarUrl: '', // Placeholder, as avatarUrl is not available directly in GithubComment type + }) + }) + */ + + return Array.from(participants.values()) +} function addSizeToAvatarUrl(avatarUrl: string, size: number = 24): string { const url = new URL(avatarUrl) @@ -25,6 +72,39 @@ type SingleGithubChallengeProps = { export const SingleGithubChallenge: React.FC = ({ issue, }) => { + const participants = extractUniqueParticipants(issue) + const projectsConst = getProjectNames(issue) + const hasProjects = projectsConst !== '' + const hasMilestone = !!issue.milestone + + const elementsToRender: React.ReactNode[] = [] + + if (hasProjects) { + elementsToRender.push( + <> + + {projectsConst.includes(', ') ? 'Projects' : 'Project'} + + + {projectsConst} + + , + ) + } + + if (hasMilestone) { + elementsToRender.push( + <> + + Milestone + + + {issue.milestone} + + , + ) + } + return (
@@ -40,7 +120,7 @@ export const SingleGithubChallenge: React.FC = ({ key={index} className="mdx-ghc__challenge-label" > - {label.name} + {label} ))}
@@ -60,50 +140,20 @@ export const SingleGithubChallenge: React.FC = ({ > {issue.comments.length} - {issue.participants.map((participant, index) => ( + {participants.map((participant, index) => ( {participant.login} ))} - {/* 1st row 3rd item */} - - Project - - - {/* 1st row 4th item */} - - {issue.project ? issue.project.name : ''} - - - {/* 2nd row 1st item */} - - Achievement - - - {/* 2nd row 2nd item */} -
- -
- - {/* 2nd row 3rd item */} - - Milestone - - - {/* 2nd row 4th item */} - - {issue.milestone || ''} - + {/* Render Projects and/or Milestone */} + {elementsToRender.map((element, index) => ( + {element} + ))} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/githubChallengesDummyData.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/githubChallengesDummyData.ts index 8e01078..d2fc30f 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/githubChallengesDummyData.ts +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/githubChallengesDummyData.ts @@ -1,4 +1,4 @@ -import { GithubIssue } from './useFetchGithubIssues' +import { GithubIssue } from '@acid-info/logos-docusaurus-preset/src/types/github' // Only for dev purposes - prevents making multiple requests to the API while developing. export const dummyGithubIssue: GithubIssue = { @@ -6,45 +6,48 @@ export const dummyGithubIssue: GithubIssue = { title: 'Bug in pagination', body: 'When navigating to the second page, the first item repeats.', url: 'https://github.com/user/repo/issues/12345', - labels: [ - { - name: 'bug', - }, - { - name: 'frontend', - }, - ], + user: { + login: 'user123', + avatarUrl: 'https://avatars.githubusercontent.com/u/8811422?v=4', + }, + labels: ['bug', 'frontend'], + commentCount: 3, comments: [ { + id: 'c1', + author: 'alice123', body: 'I have also noticed this issue. Working on a fix now.', + createdAt: '2021-01-01T12:00:00Z', }, { + id: 'c2', + author: 'bob456', body: 'Any updates on this?', + createdAt: '2021-01-02T15:30:00Z', }, { + id: 'c3', + author: 'jaquim', body: 'I like turtles.', + createdAt: '2021-01-03T09:45:00Z', }, ], - participants: [ + assignees: [ { login: 'alice123', - avatarUrl: - 'https://avatars.githubusercontent.com/u/8811422?u=b4aec0f11a78abe3b71c42e4adfba3ebd61f34aa&v=4', + avatarUrl: 'https://avatars.githubusercontent.com/u/8811422?v=4', }, { login: 'bob456', - avatarUrl: - 'https://avatars.githubusercontent.com/u/8811422?u=b4aec0f11a78abe3b71c42e4adfba3ebd61f34aa&v=4', - }, - { - login: 'jaquim', - avatarUrl: - 'https://avatars.githubusercontent.com/u/8811422?u=b4aec0f11a78abe3b71c42e4adfba3ebd61f34aa&v=4', + avatarUrl: 'https://avatars.githubusercontent.com/u/8811422?v=4', }, ], - project: { - name: 'Awesome Project', - }, - achievement: 'Solved critical frontend bug', milestone: 'v1.0.0', + created_at: '2020-12-31T11:00:00Z', + updated_at: '2021-01-04T13:00:00Z', + projects: [ + { + name: 'Awesome Project', + }, + ], } diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/useFetchGithubIssues.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/useFetchGithubIssues.ts deleted file mode 100644 index e3e5794..0000000 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/GithubChallenges/useFetchGithubIssues.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { useEffect, useState } from 'react' -import { dummyGithubIssue } from './githubChallengesDummyData' - -type GithubIssueParticipant = { - login: string - avatarUrl: string -} - -type GithubProject = { - name: string -} - -type GithubMilestone = string - -type GithubIssueComment = { - body: string -} - -type GithubIssueLabel = { - name: string -} - -export type GithubIssue = { - id: string - title: string - body: string - url: string - labels: GithubIssueLabel[] - comments: GithubIssueComment[] - participants: GithubIssueParticipant[] - project?: GithubProject - milestone?: GithubMilestone - achievement: string -} - -function useFetchGithubIssues( - org: string, - useDummyData: boolean = false, -): [GithubIssue[] | null, string | null, boolean] { - const [data, setData] = useState(null) - const [errorMessage, setErrorMessage] = useState(null) - const [loading, setLoading] = useState(true) - - useEffect(() => { - if (useDummyData) { - // Mocking an API call with setTimeout - setTimeout(() => { - try { - // Success scenario: - setData([dummyGithubIssue, dummyGithubIssue, dummyGithubIssue]) - setLoading(false) - - // Uncomment the below lines to simulate an error scenario: - // throw new Error("Failed to fetch data"); - } catch (error) { - let errorMessage = 'An error occurred' - if (error instanceof Error) { - errorMessage = error.message - } else if (typeof error === 'string') { - errorMessage = error - } - - console.error(errorMessage) - setErrorMessage(errorMessage) - setLoading(false) - } - }, 2000) // Mock delay of 2 seconds - } - }, []) - - return [data, errorMessage, loading] -} - -export default useFetchGithubIssues