Skip to content

Commit

Permalink
feat: github file preview
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Jul 10, 2023
1 parent 0c3a6eb commit d1e7dbb
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 15 deletions.
7 changes: 4 additions & 3 deletions src/components/layout/header/internal/HeaderContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ const ForDesktop: Component<{
const background = useMotionTemplate`radial-gradient(${radius}px circle at ${mouseX}px ${mouseY}px, var(--spotlight-color) 0%, transparent 65%)`

return (
<nav
<m.nav
layout="size"
onMouseMove={handleMouseMove}
className={clsxm(
'relative',
Expand All @@ -109,7 +110,7 @@ const ForDesktop: Component<{
'dark:from-zinc-900/70 dark:to-zinc-800/90 dark:ring-zinc-100/10',
'group [--spotlight-color:hsl(var(--a)_/_0.05)]',
'duration-200',
shouldHideNavBg && 'bg-none shadow-none ring-transparent',
shouldHideNavBg && '!bg-none !shadow-none !ring-transparent',
className,
)}
>
Expand Down Expand Up @@ -142,7 +143,7 @@ const ForDesktop: Component<{
)
})}
</div>
</nav>
</m.nav>
)
}

Expand Down
75 changes: 72 additions & 3 deletions src/components/ui/markdown/renderers/LinkRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import React, { useMemo } from 'react'
import { useQuery } from '@tanstack/react-query'
import React, { memo, useMemo } from 'react'
import dynamic from 'next/dynamic'

import { GitHubBrandIcon } from '~/components/icons/platform/GitHubBrandIcon'
import {
getTweetId,
isGistUrl,
isGithubCommitUrl,
isGithubFilePreviewUrl,
isGithubRepoUrl,
isTweetUrl,
isYoutubeUrl,
parseGithubCommitUrl,
parseGithubGistUrl,
parseGithubRepoUrl,
parseGithubTypedUrl,
} from '~/lib/link-parser'

import { HighLighter } from '../../code-highlighter'
import { LinkCard } from '../../link-card'
import { Loading } from '../../loading'
import { MLink } from './link'

const Tweet = dynamic(() => import('~/components/widgets/shared/Tweet'), {
Expand Down Expand Up @@ -79,7 +83,7 @@ export const LinkRenderer = ({ href }: { href: string }) => {
}

case isGithubCommitUrl(url): {
const { owner, repo, id } = parseGithubCommitUrl(url)
const { owner, repo, id } = parseGithubTypedUrl(url)
return (
<>
<p>
Expand All @@ -89,6 +93,18 @@ export const LinkRenderer = ({ href }: { href: string }) => {
</>
)
}
case isGithubFilePreviewUrl(url): {
const { owner, repo, afterTypeString } = parseGithubTypedUrl(url)
const splitString = afterTypeString.split('/')
const sha = splitString[0].length === 40 ? splitString[0] : undefined
const path = sha ? splitString.slice(1).join('/') : afterTypeString
return (
<>
<MLink href={href}>{href}</MLink>
<EmbedGithubFile owner={owner} repo={repo} path={path} sha={sha} />
</>
)
}
}
}
// fallback to default renderer
Expand Down Expand Up @@ -119,3 +135,56 @@ const FixedRatioContainer = ({
</div>
)
}

const EmbedGithubFile = memo(
({
owner,
path,
repo,
sha,
}: {
owner: string
repo: string
path: string
sha?: string
}) => {
const { data, isLoading, isError } = useQuery<string>({
queryKey: ['github-preview', owner, repo, path, sha],
queryFn: async () => {
return fetch(
`https://cdn.jsdelivr.net/gh/${owner}/${repo}${
sha ? `@${sha}` : ''
}/${path}`,
).then(async (res) => {
return res.text()
})
},
})

if (isLoading) {
return <Loading loadingText="Loading GitHub File Preview..." />
}

if (isError) {
return (
<pre className="flex h-[60px] flex-wrap center">
<code>Loading GitHub File Preview Failed:</code>
<br />
<code>
{owner}/{repo}/{path}
</code>
</pre>
)
}

if (!data) return null

return (
<div className="h-[50vh] overflow-auto">
<HighLighter content={data} lang="javascript" />
</div>
)
},
)

EmbedGithubFile.displayName = 'EmbedGithubFile'
2 changes: 1 addition & 1 deletion src/components/widgets/comment/CommentSkeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const CommentSkeletonSingle = () => {
}
export const CommentSkeleton: Component = () => {
return (
<div className="flex flex-col space-y-4">
<div className="flex min-h-[400px] flex-col space-y-4">
<CommentSkeletonSingle />
<CommentSkeletonSingle />
<CommentSkeletonSingle />
Expand Down
10 changes: 8 additions & 2 deletions src/components/widgets/comment/Comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { memo, useMemo } from 'react'
import type { FC } from 'react'
import type { CommentBaseProps } from './types'

import { NotSupport } from '~/components/common/NotSupport'
import { apiClient } from '~/lib/request'

import { LoadMoreIndicator } from '../shared/LoadMoreIndicator'
Expand Down Expand Up @@ -42,10 +43,15 @@ export const Comments: FC<CommentBaseProps> = ({ refId }) => {
if (isLoading) {
return <CommentSkeleton />
}
if (!data) return null
if (!data || !data.pages.length || !data.pages[0].data.length)
return (
<div className="flex min-h-[400px] center">
<NotSupport text="这里还没有评论呢" />
</div>
)
return (
<>
<ul className="list-none space-y-4">
<ul className="min-h-[400px] list-none space-y-4">
{data?.pages.map((data) =>
data.data.map((comment, index) => {
return (
Expand Down
22 changes: 18 additions & 4 deletions src/components/widgets/shared/WithArticleSelectionAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { AnimatePresence, m } from 'framer-motion'

import { useIsMobile } from '~/atoms'
import { MotionButtonBase } from '~/components/ui/button'
import { DividerVertical } from '~/components/ui/divider'
import { useIsClient } from '~/hooks/common/use-is-client'
import { stopPropagation } from '~/lib/dom'
import { useModalStack } from '~/providers/root/modal-stack-provider'

import { CommentModal } from './CommentModal'
Expand Down Expand Up @@ -71,19 +73,31 @@ export const WithArticleSelectionAction: Component<{
<m.div
className={clsx(
'absolute z-10 rounded-lg bg-gradient-to-b from-zinc-50/50 to-white/90 p-1 text-sm shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5',
'dark:from-zinc-900/50 dark:to-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20',
'backdrop-blur-sm dark:from-zinc-900/50 dark:to-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20',
)}
style={{
left: pos.x,
top: pos.y,
top: pos.y - 40,
}}
initial={{ y: 10, opacity: 0.6, scale: 0.96 }}
initial={{ y: 10, opacity: 0.8, scale: 0.1 }}
animate={{ y: 0, opacity: 1, scale: 1 }}
exit={{
y: 20,
scale: 0,
opacity: 0,
}}
>
<MotionButtonBase
data-event="selection-comment"
className="rounded-md px-2 py-1 hover:bg-slate-100/80 dark:hover:bg-zinc-900/90"
onMouseUp={stopPropagation}
onClick={() => {
navigator.clipboard.writeText(selectedText)
setShow(false)
}}
>
复制
</MotionButtonBase>
<DividerVertical className="mx-1" />
<MotionButtonBase
data-event="selection-comment"
className="rounded-md px-2 py-1 hover:bg-slate-100/80 dark:hover:bg-zinc-900/90"
Expand Down
16 changes: 14 additions & 2 deletions src/lib/link-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export const isGithubProfileUrl = (url: URL) => {
return url.hostname === 'github.com' && url.pathname.split('/').length === 2
}

export const isGithubFilePreviewUrl = (url: URL) => {
// https://github.com/Innei/sprightly/blob/14234594f44956e6f56f1f92952ce82db37ef4df/src/socket/handler.ts
const [_, owner, repo, type, ...rest] = url.pathname.split('/')
return url.hostname === 'github.com' && type === 'blob'
}

export const isTwitterProfileUrl = (url: URL) => {
return url.hostname === 'twitter.com' && url.pathname.split('/').length === 2
}
Expand Down Expand Up @@ -63,12 +69,18 @@ export const parseGithubGistUrl = (url: URL) => {
}
}

export const parseGithubCommitUrl = (url: URL) => {
const [_, owner, repo, type, id] = url.pathname.split('/')
export const parseGithubTypedUrl = (url: URL) => {
// https://github.com/Innei/sprightly/blob/14234594f44956e6f56f1f92952ce82db37ef4df/src/socket/handler.ts
// https://github.com/mx-space/core/commit/e1b4d881cf18e1cb66294d2620eada35937d9a12
const split = url.pathname.split('/')
const [_, owner, repo, type, id] = split

const afterTypeString = split.slice(4).join('/')
return {
owner,
repo,
type,
id,
afterTypeString,
}
}

1 comment on commit d1e7dbb

@vercel
Copy link

@vercel vercel bot commented on d1e7dbb Jul 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

shiro – ./

springtide.vercel.app
shiro-git-main-innei.vercel.app
shiro-innei.vercel.app
innei.in

Please sign in to comment.