From 50ddd75678ce2ec23d6b6408fac621c4f5c86ea3 Mon Sep 17 00:00:00 2001 From: Innei Date: Thu, 2 Nov 2023 22:53:10 +0800 Subject: [PATCH] feat: parse github pr link Signed-off-by: Innei --- src/components/ui/link-card/LinkCard.tsx | 53 +++++-- .../ui/markdown/renderers/LinkRenderer.tsx | 132 +++++++++++------- .../ui/markdown/renderers/paragraph.tsx | 6 +- src/lib/link-parser.ts | 26 +++- 4 files changed, 148 insertions(+), 69 deletions(-) diff --git a/src/components/ui/link-card/LinkCard.tsx b/src/components/ui/link-card/LinkCard.tsx index e3acabe099..6d167194db 100644 --- a/src/components/ui/link-card/LinkCard.tsx +++ b/src/components/ui/link-card/LinkCard.tsx @@ -18,7 +18,7 @@ import { apiClient } from '~/lib/request' import styles from './LinkCard.module.css' -export type LinkCardSource = 'gh' | 'self' | 'mx-space' | 'gh-commit' +export type LinkCardSource = 'gh' | 'self' | 'mx-space' | 'gh-commit' | 'gh-pr' export interface LinkCardProps { id: string source?: LinkCardSource @@ -174,17 +174,12 @@ const LinkCardImpl: FC = (props) => { setCardInfo({ image: data.author.avatarUrl, title: ( - - - {namespace}/{repo} - - - {data.commit.message.replace(/Signed-off-by:.+/, '')} - + + {data.commit.message.replace(/Signed-off-by:.+/, '')} ), desc: ( - + +{data.stats.additions} @@ -193,6 +188,46 @@ const LinkCardImpl: FC = (props) => { {data.sha.slice(0, 7)} + + + {namespace}/{repo} + + + ), + }) + setFullUrl(data.htmlUrl) + } + + return true + } + + case 'gh-pr': { + // ${owner}/${repo}/${pr} + const [owner, repo, pr] = id.split('/') + if (!owner || !repo || !pr) { + return false + } + fetchFnRef.current = async () => { + const data = await fetchGitHubApi( + `https://api.github.com/repos/${owner}/${repo}/pulls/${pr}`, + ) + .then((data) => camelcaseKeys(data)) + .catch(() => { + // set fallback url + // + setFullUrl(`https://github.com/${owner}/${repo}/commit/${pr}`) + }) + + setCardInfo({ + image: data.user.avatarUrl, + title: {data.title}, + desc: ( + + +{data.additions} + -{data.deletions} + + {owner}/{repo} + ), }) diff --git a/src/components/ui/markdown/renderers/LinkRenderer.tsx b/src/components/ui/markdown/renderers/LinkRenderer.tsx index a7eeaee5b6..126cf64b16 100644 --- a/src/components/ui/markdown/renderers/LinkRenderer.tsx +++ b/src/components/ui/markdown/renderers/LinkRenderer.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from 'react' import dynamic from 'next/dynamic' -import type { PropsWithChildren } from 'react' +import type { FC, PropsWithChildren, ReactNode } from 'react' import { GitHubBrandIcon } from '~/components/icons/platform/GitHubBrandIcon' import { @@ -9,12 +9,13 @@ import { isGistUrl, isGithubCommitUrl, isGithubFilePreviewUrl, - isGithubRepoUrl, + isGithubPrUrl, + isGithubUrl, isSelfArticleUrl, isTweetUrl, isYoutubeUrl, parseGithubGistUrl, - parseGithubRepoUrl, + parseGithubPrUrl, parseGithubTypedUrl, } from '~/lib/link-parser' @@ -25,7 +26,11 @@ import { MLink } from '../../link/MLink' const Tweet = dynamic(() => import('~/components/widgets/shared/Tweet'), { ssr: false, }) -export const LinkRenderer = ({ + +/** + * 单行链接的渲染 + */ +export const BlockLinkRenderer = ({ href, children, }: PropsWithChildren<{ href: string }>) => { @@ -49,18 +54,23 @@ export const LinkRenderer = ({ if (!url) { return fallbackElement } + switch (true) { + case isGithubUrl(url): { + return ( + + ) + } case isTweetUrl(url): { const id = getTweetId(url) return } - case isGithubRepoUrl(url): { - const { owner, repo } = parseGithubRepoUrl(url) - return - } - case isYoutubeUrl(url): { const id = url.searchParams.get('v')! return ( @@ -75,6 +85,62 @@ export const LinkRenderer = ({ ) } + + case isCodesandboxUrl(url): { + // https://codesandbox.io/s/framer-motion-layoutroot-prop-forked-p39g96 + // to + // https://codesandbox.io/embed/framer-motion-layoutroot-prop-forked-p39g96?fontsize=14&hidenavigation=1&theme=dark + return ( + +